From 7e367ae0ed6c9cdfe97b4f23c2df867e6e25dbb4 Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Wed, 30 Apr 2014 15:17:51 +0000 Subject: [PATCH] Reland "Trigger exception debug event for promises at the throw site." R=rossberg@chromium.org Review URL: https://codereview.chromium.org/266533003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@21097 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8-debug.h | 3 +- src/debug-debugger.js | 3 +- src/debug.cc | 65 +++++++++++++++++++--- src/debug.h | 18 +++++- src/isolate.cc | 3 +- src/mirror-debugger.js | 4 +- src/promise.js | 33 +++++------ 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 +- test/mjsunit/es6/debug-promises-reentry.js | 17 ++++++ .../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 ++-- 18 files changed, 235 insertions(+), 85 deletions(-) create mode 100644 test/mjsunit/es6/debug-promises-caught-late.js create mode 100644 test/mjsunit/es6/debug-promises-reentry.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 0e4e2ad..0762de9 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -37,6 +37,8 @@ Debug::Debug(Isolate* isolate) disable_break_(false), break_on_exception_(false), break_on_uncaught_exception_(false), + promise_catch_handlers_(0), + promise_getters_(0), debug_break_return_(NULL), debug_break_slot_(NULL), isolate_(isolate) { @@ -1318,6 +1320,53 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { } +void Debug::PromiseHandlePrologue(Handle promise_getter) { + Handle promise_getter_global = Handle::cast( + isolate_->global_handles()->Create(*promise_getter)); + StackHandler* handler = + StackHandler::FromAddress(Isolate::handler(isolate_->thread_local_top())); + promise_getters_.Add(promise_getter_global); + promise_catch_handlers_.Add(handler); +} + + +void Debug::PromiseHandleEpilogue() { + if (promise_catch_handlers_.length() == 0) return; + promise_catch_handlers_.RemoveLast(); + Handle promise_getter = promise_getters_.RemoveLast(); + isolate_->global_handles()->Destroy(promise_getter.location()); +} + + +Handle Debug::GetPromiseForUncaughtException() { + Handle undefined = isolate_->factory()->undefined_value(); + if (promise_getters_.length() == 0) return undefined; + Handle promise_getter = promise_getters_.last(); + StackHandler* promise_catch = promise_catch_handlers_.last(); + // 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 != promise_catch && !temp->is_catch()) { + temp = temp->next(); + CHECK(temp != NULL); + } +#endif // DEBUG + + if (handler == promise_catch) { + 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) { @@ -2647,9 +2696,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(); @@ -2657,6 +2704,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. @@ -2674,10 +2724,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. @@ -2687,7 +2733,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. } @@ -3169,7 +3215,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 7781d4a..292aff0 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); @@ -538,6 +544,14 @@ 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. + // Due to the possibility of reentry we use a list to form a stack. + List promise_catch_handlers_; + List > promise_getters_; + // Per-thread data. class ThreadLocal { public: @@ -774,9 +788,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