From eed0e7e7a3775f835c5b63b253573b9c5ec3ae36 Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Wed, 30 Apr 2014 14:17:40 +0000 Subject: [PATCH] Trigger exception debug event for promises at the throw site. R=rossberg@chromium.org Review URL: https://codereview.chromium.org/260723002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@21092 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8-debug.h | 3 +- src/debug-debugger.js | 3 +- src/debug.cc | 64 +++++++++++++++++++--- src/debug.h | 17 +++++- src/isolate.cc | 3 +- src/mirror-debugger.js | 4 +- src/promise.js | 34 +++++------- src/runtime.cc | 21 +++++-- src/runtime.h | 3 +- test/mjsunit/es6/debug-promises-caught-all.js | 5 +- test/mjsunit/es6/debug-promises-caught-late.js | 38 +++++++++++++ test/mjsunit/es6/debug-promises-caught-uncaught.js | 4 +- .../es6/debug-promises-throw-in-constructor.js | 46 ++++++++++++++++ test/mjsunit/es6/debug-promises-throw-in-reject.js | 12 ++-- test/mjsunit/es6/debug-promises-uncaught-all.js | 23 +++----- .../es6/debug-promises-uncaught-uncaught.js | 10 ++-- .../mjsunit/es6/debug-promises-undefined-reject.js | 12 ++-- 17 files changed, 216 insertions(+), 86 deletions(-) create mode 100644 test/mjsunit/es6/debug-promises-caught-late.js create mode 100644 test/mjsunit/es6/debug-promises-throw-in-constructor.js diff --git a/include/v8-debug.h b/include/v8-debug.h index 8523387..bd3eb77 100644 --- a/include/v8-debug.h +++ b/include/v8-debug.h @@ -20,8 +20,7 @@ enum DebugEvent { BeforeCompile = 4, AfterCompile = 5, ScriptCollected = 6, - PendingExceptionInPromise = 7, - BreakForCommand = 8 + BreakForCommand = 7 }; diff --git a/src/debug-debugger.js b/src/debug-debugger.js index dca7391..0ce8832 100644 --- a/src/debug-debugger.js +++ b/src/debug-debugger.js @@ -19,8 +19,7 @@ Debug.DebugEvent = { Break: 1, NewFunction: 3, BeforeCompile: 4, AfterCompile: 5, - ScriptCollected: 6, - PendingExceptionInPromise: 7 }; + ScriptCollected: 6 }; // Types of exceptions that can be broken upon. Debug.ExceptionBreak = { Caught : 0, diff --git a/src/debug.cc b/src/debug.cc index dadaa91..641d7e5 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -37,6 +37,7 @@ Debug::Debug(Isolate* isolate) disable_break_(false), break_on_exception_(false), break_on_uncaught_exception_(false), + current_promise_catch_handler_(NULL), debug_break_return_(NULL), debug_break_slot_(NULL), isolate_(isolate) { @@ -1317,6 +1318,53 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { } +void Debug::PromiseHandlePrologue(Handle promise_getter) { + ASSERT(current_promise_getter_.is_null()); + current_promise_getter_ = Handle::cast( + isolate_->global_handles()->Create(*promise_getter)); + current_promise_catch_handler_ = + StackHandler::FromAddress(Isolate::handler(isolate_->thread_local_top())); +} + + +void Debug::PromiseHandleEpilogue() { + current_promise_catch_handler_ = NULL; + Handle promise_getter; + if (!current_promise_getter_.ToHandle(&promise_getter)) return; + isolate_->global_handles()->Destroy(promise_getter.location()); + current_promise_getter_ = MaybeHandle(); +} + + +Handle Debug::GetPromiseForUncaughtException() { + Handle promise_getter; + Handle undefined = isolate_->factory()->undefined_value(); + if (current_promise_getter_.ToHandle(&promise_getter)) { + // Find the top-most try-catch handler. + StackHandler* handler = StackHandler::FromAddress( + Isolate::handler(isolate_->thread_local_top())); + while (handler != NULL && !handler->is_catch()) { + handler = handler->next(); + } +#ifdef DEBUG + // Make sure that our promise catch handler is in the list of handlers, + // even if it's not the top-most try-catch handler. + StackHandler* temp = handler; + while (temp != current_promise_catch_handler_ && !temp->is_catch()) { + temp = temp->next(); + CHECK(temp != NULL); + } +#endif // DEBUG + + if (handler == current_promise_catch_handler_) { + return Execution::Call( + isolate_, promise_getter, undefined, 0, NULL).ToHandleChecked(); + } + } + return undefined; +} + + void Debug::PrepareStep(StepAction step_action, int step_count, StackFrame::Id frame_id) { @@ -2640,9 +2688,7 @@ MaybeHandle Debugger::MakeScriptCollectedEvent(int id) { } -void Debugger::OnException(Handle exception, - bool uncaught, - Handle promise) { +void Debugger::OnException(Handle exception, bool uncaught) { HandleScope scope(isolate_); Debug* debug = isolate_->debug(); @@ -2650,6 +2696,9 @@ void Debugger::OnException(Handle exception, if (debug->InDebugger()) return; if (!Debugger::EventActive(v8::Exception)) return; + Handle promise = debug->GetPromiseForUncaughtException(); + uncaught |= !promise->IsUndefined(); + // Bail out if exception breaks are not active if (uncaught) { // Uncaught exceptions are reported by either flags. @@ -2667,10 +2716,6 @@ void Debugger::OnException(Handle exception, // Clear all current stepping setup. debug->ClearStepping(); - // Determine event; - DebugEvent event = promise->IsUndefined() - ? v8::Exception : v8::PendingExceptionInPromise; - // Create the event data object. Handle event_data; // Bail out and don't call debugger if exception. @@ -2680,7 +2725,7 @@ void Debugger::OnException(Handle exception, } // Process debug event. - ProcessDebugEvent(event, Handle::cast(event_data), false); + ProcessDebugEvent(v8::Exception, Handle::cast(event_data), false); // Return to continue execution from where the exception was thrown. } @@ -3162,7 +3207,8 @@ void Debugger::SetMessageHandler(v8::Debug::MessageHandler2 handler) { void Debugger::ListenersChanged() { - if (IsDebuggerActive()) { + bool active = IsDebuggerActive(); + if (active) { // Disable the compilation cache when the debugger is active. isolate_->compilation_cache()->Disable(); debugger_unload_pending_ = false; diff --git a/src/debug.h b/src/debug.h index da59526..b76c512 100644 --- a/src/debug.h +++ b/src/debug.h @@ -235,6 +235,12 @@ class Debug { void FloodHandlerWithOneShot(); void ChangeBreakOnException(ExceptionBreakType type, bool enable); bool IsBreakOnException(ExceptionBreakType type); + + void PromiseHandlePrologue(Handle promise_getter); + void PromiseHandleEpilogue(); + // Returns a promise if it does not have a reject handler. + Handle GetPromiseForUncaughtException(); + void PrepareStep(StepAction step_action, int step_count, StackFrame::Id frame_id); @@ -541,6 +547,13 @@ class Debug { bool break_on_exception_; bool break_on_uncaught_exception_; + // When a promise is being resolved, we may want to trigger a debug event for + // the case we catch a throw. For this purpose we remember the try-catch + // handler address that would catch the exception. We also hold onto a + // closure that returns a promise if the exception is considered uncaught. + StackHandler* current_promise_catch_handler_; + MaybeHandle current_promise_getter_; + // Per-thread data. class ThreadLocal { public: @@ -777,9 +790,7 @@ class Debugger { MUST_USE_RESULT MaybeHandle MakeScriptCollectedEvent(int id); void OnDebugBreak(Handle break_points_hit, bool auto_continue); - void OnException(Handle exception, - bool uncaught, - Handle promise = Handle::null()); + void OnException(Handle exception, bool uncaught); void OnBeforeCompile(Handle