Skip to content

Commit edfc8cd

Browse files
Julien Gilliofrobots
Julien Gilli
authored andcommitted
deps: backport 1ee712a from V8 upstream
Backport 1ee712ab8687e5f4dec93d45da068d37d28feb8b from V8 upstream. Original commit message: Add SetAbortOnUncaughtExceptionCallback API The --abort-on-uncaught-exception command line switch makes Isolate::Throw abort if the error being thrown cannot be caught by a try/catch block. Embedders may want to use other mechanisms than try/catch blocks to handle uncaught exceptions. For instance, Node.js has "domain" objects that have error handlers that can handle uncaught exception like following: var d = domain.create(); d.on('error', function onError(err) { console.log('Handling error'); }); d.run(function() { throw new Error("boom"); }); These error handlers are called by isolates' message listeners. If --abort-on-uncaught-exception is *not* used, the isolate's message listener will be called, which will in turn call the domain's error handler. The process will output 'Handling error' and will exit successfully (not due to an uncaught exception). This is the behavior that Node.js users expect. However, if --abort-on-uncaught-exception is used and when throwing an error within a domain that has an error handler, the process will abort and the domain's error handler will not be called. This is not the behavior that Node.js users expect. Having a SetAbortOnUncaughtExceptionCallback API allows embedders to determine when it's not appropriate to abort and instead handle the exception via the isolate's message listener. In the example above, Node.js would set a custom callback with SetAbortOnUncaughtExceptionCallback that would be implemented as following (the sample code has been simplified to remove what's not relevant to this change): bool ShouldAbortOnUncaughtException(Isolate* isolate) { return !IsDomainActive(); } Now when --abort-on-uncaught-exception is used, Isolate::Throw would call that callback and determine that it should not abort if a domain with an error handler is active. Instead, the isolate's message listener would be called and the error would be handled by the domain's error handler. I believe this can also be useful for other embedders. BUG= [email protected] Review URL: https://codereview.chromium.org/1375933003 Cr-Commit-Position: refs/heads/master@{#31111} Ref: #3036 Ref: #3481 PR-URL: #4106 Reviewed-By: bnoordhuis - Ben Noordhuis <[email protected]> Reviewed-By: targos - Michaël Zasso <[email protected]> Reviewed-By: rvagg - Rod Vagg <[email protected]>
1 parent dc09bbe commit edfc8cd

File tree

5 files changed

+79
-7
lines changed

5 files changed

+79
-7
lines changed

deps/v8/include/v8.h

+13
Original file line numberDiff line numberDiff line change
@@ -5377,6 +5377,19 @@ class V8_EXPORT Isolate {
53775377
*/
53785378
static Isolate* GetCurrent();
53795379

5380+
/**
5381+
* Custom callback used by embedders to help V8 determine if it should abort
5382+
* when it throws and no internal handler is predicted to catch the
5383+
* exception. If --abort-on-uncaught-exception is used on the command line,
5384+
* then V8 will abort if either:
5385+
* - no custom callback is set.
5386+
* - the custom callback set returns true.
5387+
* Otherwise, the custom callback will not be called and V8 will not abort.
5388+
*/
5389+
typedef bool (*AbortOnUncaughtExceptionCallback)(Isolate*);
5390+
void SetAbortOnUncaughtExceptionCallback(
5391+
AbortOnUncaughtExceptionCallback callback);
5392+
53805393
/**
53815394
* Methods below this point require holding a lock (using Locker) in
53825395
* a multi-threaded environment.

deps/v8/src/api.cc

+7
Original file line numberDiff line numberDiff line change
@@ -7105,6 +7105,13 @@ void Isolate::Exit() {
71057105
}
71067106

71077107

7108+
void Isolate::SetAbortOnUncaughtExceptionCallback(
7109+
AbortOnUncaughtExceptionCallback callback) {
7110+
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
7111+
isolate->SetAbortOnUncaughtExceptionCallback(callback);
7112+
}
7113+
7114+
71087115
Isolate::DisallowJavascriptExecutionScope::DisallowJavascriptExecutionScope(
71097116
Isolate* isolate,
71107117
Isolate::DisallowJavascriptExecutionScope::OnFailure on_failure)

deps/v8/src/isolate.cc

+22-7
Original file line numberDiff line numberDiff line change
@@ -1008,13 +1008,21 @@ Object* Isolate::Throw(Object* exception, MessageLocation* location) {
10081008
Handle<Object> message_obj = CreateMessage(exception_handle, location);
10091009
thread_local_top()->pending_message_obj_ = *message_obj;
10101010

1011-
// If the abort-on-uncaught-exception flag is specified, abort on any
1012-
// exception not caught by JavaScript, even when an external handler is
1013-
// present. This flag is intended for use by JavaScript developers, so
1014-
// print a user-friendly stack trace (not an internal one).
1011+
// For any exception not caught by JavaScript, even when an external
1012+
// handler is present:
1013+
// If the abort-on-uncaught-exception flag is specified, and if the
1014+
// embedder didn't specify a custom uncaught exception callback,
1015+
// or if the custom callback determined that V8 should abort, then
1016+
// abort.
10151017
if (FLAG_abort_on_uncaught_exception &&
1016-
PredictExceptionCatcher() != CAUGHT_BY_JAVASCRIPT) {
1017-
FLAG_abort_on_uncaught_exception = false; // Prevent endless recursion.
1018+
PredictExceptionCatcher() != CAUGHT_BY_JAVASCRIPT &&
1019+
(!abort_on_uncaught_exception_callback_ ||
1020+
abort_on_uncaught_exception_callback_(
1021+
reinterpret_cast<v8::Isolate*>(this)))) {
1022+
// Prevent endless recursion.
1023+
FLAG_abort_on_uncaught_exception = false;
1024+
// This flag is intended for use by JavaScript developers, so
1025+
// print a user-friendly stack trace (not an internal one).
10181026
PrintF(stderr, "%s\n\nFROM\n",
10191027
MessageHandler::GetLocalizedMessage(this, message_obj).get());
10201028
PrintCurrentStackTrace(stderr);
@@ -1602,6 +1610,12 @@ void Isolate::SetCaptureStackTraceForUncaughtExceptions(
16021610
}
16031611

16041612

1613+
void Isolate::SetAbortOnUncaughtExceptionCallback(
1614+
v8::Isolate::AbortOnUncaughtExceptionCallback callback) {
1615+
abort_on_uncaught_exception_callback_ = callback;
1616+
}
1617+
1618+
16051619
Handle<Context> Isolate::native_context() {
16061620
return handle(context()->native_context());
16071621
}
@@ -1770,7 +1784,8 @@ Isolate::Isolate(bool enable_serializer)
17701784
next_unique_sfi_id_(0),
17711785
#endif
17721786
use_counter_callback_(NULL),
1773-
basic_block_profiler_(NULL) {
1787+
basic_block_profiler_(NULL),
1788+
abort_on_uncaught_exception_callback_(NULL) {
17741789
{
17751790
base::LockGuard<base::Mutex> lock_guard(thread_data_table_mutex_.Pointer());
17761791
CHECK(thread_data_table_);

deps/v8/src/isolate.h

+6
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,9 @@ class Isolate {
655655
int frame_limit,
656656
StackTrace::StackTraceOptions options);
657657

658+
void SetAbortOnUncaughtExceptionCallback(
659+
v8::Isolate::AbortOnUncaughtExceptionCallback callback);
660+
658661
enum PrintStackMode { kPrintStackConcise, kPrintStackVerbose };
659662
void PrintCurrentStackTrace(FILE* out);
660663
void PrintStack(StringStream* accumulator,
@@ -1325,6 +1328,9 @@ class Isolate {
13251328

13261329
std::set<Cancelable*> cancelable_tasks_;
13271330

1331+
v8::Isolate::AbortOnUncaughtExceptionCallback
1332+
abort_on_uncaught_exception_callback_;
1333+
13281334
friend class ExecutionAccess;
13291335
friend class HandleScopeImplementer;
13301336
friend class OptimizingCompileDispatcher;

deps/v8/test/cctest/test-api.cc

+31
Original file line numberDiff line numberDiff line change
@@ -21863,3 +21863,34 @@ TEST(EstimatedContextSize) {
2186321863
LocalContext env;
2186421864
CHECK(50000 < env->EstimatedSize());
2186521865
}
21866+
21867+
21868+
static int nb_uncaught_exception_callback_calls = 0;
21869+
21870+
21871+
bool NoAbortOnUncaughtException(v8::Isolate* isolate) {
21872+
++nb_uncaught_exception_callback_calls;
21873+
return false;
21874+
}
21875+
21876+
21877+
TEST(AbortOnUncaughtExceptionNoAbort) {
21878+
v8::Isolate* isolate = CcTest::isolate();
21879+
v8::HandleScope handle_scope(isolate);
21880+
v8::Handle<v8::ObjectTemplate> global_template =
21881+
v8::ObjectTemplate::New(isolate);
21882+
LocalContext env(NULL, global_template);
21883+
21884+
i::FLAG_abort_on_uncaught_exception = true;
21885+
isolate->SetAbortOnUncaughtExceptionCallback(NoAbortOnUncaughtException);
21886+
21887+
CompileRun("function boom() { throw new Error(\"boom\") }");
21888+
21889+
v8::Local<v8::Object> global_object = env->Global();
21890+
v8::Local<v8::Function> foo =
21891+
v8::Local<v8::Function>::Cast(global_object->Get(v8_str("boom")));
21892+
21893+
foo->Call(global_object, 0, NULL);
21894+
21895+
CHECK_EQ(1, nb_uncaught_exception_callback_calls);
21896+
}

0 commit comments

Comments
 (0)