diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index c651b7cd09ba19..062dd5f21fb275 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -5380,6 +5380,19 @@ class V8_EXPORT Isolate { */ static Isolate* GetCurrent(); + /** + * Custom callback used by embedders to help V8 determine if it should abort + * when it throws and no internal handler is predicted to catch the + * exception. If --abort-on-uncaught-exception is used on the command line, + * then V8 will abort if either: + * - no custom callback is set. + * - the custom callback set returns true. + * Otherwise, the custom callback will not be called and V8 will not abort. + */ + typedef bool (*AbortOnUncaughtExceptionCallback)(Isolate*); + void SetAbortOnUncaughtExceptionCallback( + AbortOnUncaughtExceptionCallback callback); + /** * Methods below this point require holding a lock (using Locker) in * a multi-threaded environment. diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 593aea641b2bf3..a57171f8a3b1d3 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -7176,6 +7176,13 @@ void Isolate::Exit() { } +void Isolate::SetAbortOnUncaughtExceptionCallback( + AbortOnUncaughtExceptionCallback callback) { + i::Isolate* isolate = reinterpret_cast(this); + isolate->SetAbortOnUncaughtExceptionCallback(callback); +} + + Isolate::DisallowJavascriptExecutionScope::DisallowJavascriptExecutionScope( Isolate* isolate, Isolate::DisallowJavascriptExecutionScope::OnFailure on_failure) diff --git a/deps/v8/src/isolate.cc b/deps/v8/src/isolate.cc index e9d526a4538925..2c83faac4fba24 100644 --- a/deps/v8/src/isolate.cc +++ b/deps/v8/src/isolate.cc @@ -1013,13 +1013,21 @@ Object* Isolate::Throw(Object* exception, MessageLocation* location) { Handle message_obj = CreateMessage(exception_handle, location); thread_local_top()->pending_message_obj_ = *message_obj; - // If the abort-on-uncaught-exception flag is specified, abort on any - // exception not caught by JavaScript, even when an external handler is - // present. This flag is intended for use by JavaScript developers, so - // print a user-friendly stack trace (not an internal one). + // For any exception not caught by JavaScript, even when an external + // handler is present: + // If the abort-on-uncaught-exception flag is specified, and if the + // embedder didn't specify a custom uncaught exception callback, + // or if the custom callback determined that V8 should abort, then + // abort. if (FLAG_abort_on_uncaught_exception && - PredictExceptionCatcher() != CAUGHT_BY_JAVASCRIPT) { - FLAG_abort_on_uncaught_exception = false; // Prevent endless recursion. + PredictExceptionCatcher() != CAUGHT_BY_JAVASCRIPT && + (!abort_on_uncaught_exception_callback_ || + abort_on_uncaught_exception_callback_( + reinterpret_cast(this)))) { + // Prevent endless recursion. + FLAG_abort_on_uncaught_exception = false; + // This flag is intended for use by JavaScript developers, so + // print a user-friendly stack trace (not an internal one). PrintF(stderr, "%s\n\nFROM\n", MessageHandler::GetLocalizedMessage(this, message_obj).get()); PrintCurrentStackTrace(stderr); @@ -1612,6 +1620,12 @@ void Isolate::SetCaptureStackTraceForUncaughtExceptions( } +void Isolate::SetAbortOnUncaughtExceptionCallback( + v8::Isolate::AbortOnUncaughtExceptionCallback callback) { + abort_on_uncaught_exception_callback_ = callback; +} + + Handle Isolate::native_context() { return handle(context()->native_context()); } @@ -1782,7 +1796,8 @@ Isolate::Isolate(bool enable_serializer) next_unique_sfi_id_(0), #endif use_counter_callback_(NULL), - basic_block_profiler_(NULL) { + basic_block_profiler_(NULL), + abort_on_uncaught_exception_callback_(NULL) { { base::LockGuard lock_guard(thread_data_table_mutex_.Pointer()); CHECK(thread_data_table_); diff --git a/deps/v8/src/isolate.h b/deps/v8/src/isolate.h index a67f0c7fb55c72..876a72f3273c19 100644 --- a/deps/v8/src/isolate.h +++ b/deps/v8/src/isolate.h @@ -690,6 +690,9 @@ class Isolate { int frame_limit, StackTrace::StackTraceOptions options); + void SetAbortOnUncaughtExceptionCallback( + v8::Isolate::AbortOnUncaughtExceptionCallback callback); + enum PrintStackMode { kPrintStackConcise, kPrintStackVerbose }; void PrintCurrentStackTrace(FILE* out); void PrintStack(StringStream* accumulator, @@ -1363,6 +1366,9 @@ class Isolate { v8::ArrayBuffer::Allocator* array_buffer_allocator_; + v8::Isolate::AbortOnUncaughtExceptionCallback + abort_on_uncaught_exception_callback_; + friend class ExecutionAccess; friend class HandleScopeImplementer; friend class OptimizingCompileDispatcher; diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index ad3190a7112d64..88d4aef25e23b7 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -21880,3 +21880,34 @@ TEST(CompatibleReceiverCheckOnCachedICHandler) { "result;\n", 0); } + + +static int nb_uncaught_exception_callback_calls = 0; + + +bool NoAbortOnUncaughtException(v8::Isolate* isolate) { + ++nb_uncaught_exception_callback_calls; + return false; +} + + +TEST(AbortOnUncaughtExceptionNoAbort) { + v8::Isolate* isolate = CcTest::isolate(); + v8::HandleScope handle_scope(isolate); + v8::Handle global_template = + v8::ObjectTemplate::New(isolate); + LocalContext env(NULL, global_template); + + i::FLAG_abort_on_uncaught_exception = true; + isolate->SetAbortOnUncaughtExceptionCallback(NoAbortOnUncaughtException); + + CompileRun("function boom() { throw new Error(\"boom\") }"); + + v8::Local global_object = env->Global(); + v8::Local foo = + v8::Local::Cast(global_object->Get(v8_str("boom"))); + + foo->Call(global_object, 0, NULL); + + CHECK_EQ(1, nb_uncaught_exception_callback_calls); +}