From 1b17f59b5db7f1c3e1996cdc3cd1f73eab1159ed Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Wed, 13 Aug 2014 11:14:35 +0000 Subject: [PATCH] Move Promise tracking from debug to isolate. This prepares for tracking promise rejections when debugger is off. R=aandrey@chromium.org Review URL: https://codereview.chromium.org/462413003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23101 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/debug.cc | 57 +----------------------------------------------- src/debug.h | 29 ------------------------ src/isolate.cc | 51 +++++++++++++++++++++++++++++++++++++++++++ src/isolate.h | 33 +++++++++++++++++++++++----- src/runtime.cc | 4 ++-- src/v8threads.cc | 3 +++ 6 files changed, 85 insertions(+), 92 deletions(-) diff --git a/src/debug.cc b/src/debug.cc index 2ae8630885..d974cd7002 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -568,7 +568,6 @@ void Debug::ThreadInit() { // TODO(isolates): frames_are_dropped_? thread_local_.current_debug_scope_ = NULL; thread_local_.restarter_frame_function_pointer_ = NULL; - thread_local_.promise_on_stack_ = NULL; } @@ -855,9 +854,6 @@ void Debug::Unload() { ClearAllBreakPoints(); ClearStepping(); - // Match unmatched PopPromise calls. - while (thread_local_.promise_on_stack_) PopPromise(); - // Return debugger is not loaded. if (!is_loaded()) return; @@ -1272,57 +1268,6 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { } -PromiseOnStack::PromiseOnStack(Isolate* isolate, PromiseOnStack* prev, - Handle promise) - : isolate_(isolate), prev_(prev) { - handler_ = StackHandler::FromAddress( - Isolate::handler(isolate->thread_local_top())); - promise_ = - Handle::cast(isolate->global_handles()->Create(*promise)); -} - - -PromiseOnStack::~PromiseOnStack() { - isolate_->global_handles()->Destroy( - Handle::cast(promise_).location()); -} - - -void Debug::PushPromise(Handle promise) { - PromiseOnStack* prev = thread_local_.promise_on_stack_; - thread_local_.promise_on_stack_ = new PromiseOnStack(isolate_, prev, promise); -} - - -void Debug::PopPromise() { - if (thread_local_.promise_on_stack_ == NULL) return; - PromiseOnStack* prev = thread_local_.promise_on_stack_->prev(); - delete thread_local_.promise_on_stack_; - thread_local_.promise_on_stack_ = prev; -} - - -Handle Debug::GetPromiseOnStackOnThrow() { - Handle undefined = isolate_->factory()->undefined_value(); - if (thread_local_.promise_on_stack_ == NULL) return undefined; - StackHandler* promise_try = thread_local_.promise_on_stack_->handler(); - // Find the top-most try-catch handler. - StackHandler* handler = StackHandler::FromAddress( - Isolate::handler(isolate_->thread_local_top())); - do { - if (handler == promise_try) { - // Mark the pushed try-catch handler to prevent a later duplicate event - // triggered with the following reject. - return thread_local_.promise_on_stack_->promise(); - } - handler = handler->next(); - // Throwing inside a Promise can be intercepted by an inner try-catch, so - // we stop at the first try-catch handler. - } while (handler != NULL && !handler->is_catch()); - return undefined; -} - - bool Debug::PromiseHasRejectHandler(Handle promise) { Handle fun = Handle::cast( JSObject::GetDataProperty(isolate_->js_builtins_object(), @@ -2567,7 +2512,7 @@ MaybeHandle Debug::MakeAsyncTaskEvent(Handle task_event) { void Debug::OnThrow(Handle exception, bool uncaught) { if (in_debug_scope() || ignore_events()) return; HandleScope scope(isolate_); - OnException(exception, uncaught, GetPromiseOnStackOnThrow()); + OnException(exception, uncaught, isolate_->GetPromiseOnStackOnThrow()); } diff --git a/src/debug.h b/src/debug.h index e60e1aaab8..a5119d0773 100644 --- a/src/debug.h +++ b/src/debug.h @@ -333,22 +333,6 @@ class LockingCommandMessageQueue BASE_EMBEDDED { }; -class PromiseOnStack { - public: - PromiseOnStack(Isolate* isolate, PromiseOnStack* prev, - Handle getter); - ~PromiseOnStack(); - StackHandler* handler() { return handler_; } - Handle promise() { return promise_; } - PromiseOnStack* prev() { return prev_; } - private: - Isolate* isolate_; - StackHandler* handler_; - Handle promise_; - PromiseOnStack* prev_; -}; - - // This class contains the debugger support. The main purpose is to handle // setting break points in the code. // @@ -452,11 +436,6 @@ class Debug { // Check whether this frame is just about to return. bool IsBreakAtReturn(JavaScriptFrame* frame); - // Promise handling. - // Push and pop a promise and the current try-catch handler. - void PushPromise(Handle promise); - void PopPromise(); - // Support for LiveEdit void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id, LiveEdit::FrameDropMode mode, @@ -551,7 +530,6 @@ class Debug { void ClearMirrorCache(); // Returns a promise if the pushed try-catch handler matches the current one. - Handle GetPromiseOnStackOnThrow(); bool PromiseHasRejectHandler(Handle promise); void CallEventCallback(v8::DebugEvent event, @@ -658,13 +636,6 @@ class Debug { // of the pointer to function being restarted. Otherwise (most of the time) // stores NULL. This pointer is used with 'step in' implementation. Object** restarter_frame_function_pointer_; - - // When a promise is being resolved, we may want to trigger a debug event - // if 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 linked list. - PromiseOnStack* promise_on_stack_; }; // Storage location for registers when handling debug break calls diff --git a/src/isolate.cc b/src/isolate.cc index 215296d735..cfd5a3d8e6 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -79,6 +79,7 @@ void ThreadLocalTop::InitializeInternal() { save_context_ = NULL; catcher_ = NULL; top_lookup_result_ = NULL; + promise_on_stack_ = NULL; // These members are re-initialized later after deserialization // is complete. @@ -100,6 +101,12 @@ void ThreadLocalTop::Initialize() { } +void ThreadLocalTop::Free() { + // Match unmatched PopPromise calls. + while (promise_on_stack_) isolate_->PopPromise(); +} + + base::Thread::LocalStorageKey Isolate::isolate_key_; base::Thread::LocalStorageKey Isolate::thread_id_key_; base::Thread::LocalStorageKey Isolate::per_isolate_thread_data_key_; @@ -1289,6 +1296,48 @@ bool Isolate::OptionalRescheduleException(bool is_bottom_call) { } +void Isolate::PushPromise(Handle promise) { + ThreadLocalTop* tltop = thread_local_top(); + PromiseOnStack* prev = tltop->promise_on_stack_; + StackHandler* handler = StackHandler::FromAddress(Isolate::handler(tltop)); + Handle global_handle = + Handle::cast(global_handles()->Create(*promise)); + tltop->promise_on_stack_ = new PromiseOnStack(handler, global_handle, prev); +} + + +void Isolate::PopPromise() { + ThreadLocalTop* tltop = thread_local_top(); + if (tltop->promise_on_stack_ == NULL) return; + PromiseOnStack* prev = tltop->promise_on_stack_->prev(); + Handle global_handle = tltop->promise_on_stack_->promise(); + delete tltop->promise_on_stack_; + tltop->promise_on_stack_ = prev; + global_handles()->Destroy(global_handle.location()); +} + + +Handle Isolate::GetPromiseOnStackOnThrow() { + Handle undefined = factory()->undefined_value(); + ThreadLocalTop* tltop = thread_local_top(); + if (tltop->promise_on_stack_ == NULL) return undefined; + StackHandler* promise_try = tltop->promise_on_stack_->handler(); + // Find the top-most try-catch handler. + StackHandler* handler = StackHandler::FromAddress(Isolate::handler(tltop)); + do { + if (handler == promise_try) { + // Mark the pushed try-catch handler to prevent a later duplicate event + // triggered with the following reject. + return tltop->promise_on_stack_->promise(); + } + handler = handler->next(); + // Throwing inside a Promise can be intercepted by an inner try-catch, so + // we stop at the first try-catch handler. + } while (handler != NULL && !handler->is_catch()); + return undefined; +} + + void Isolate::SetCaptureStackTraceForUncaughtExceptions( bool capture, int frame_limit, @@ -1552,6 +1601,8 @@ void Isolate::Deinit() { debug()->Unload(); + FreeThreadResources(); + if (concurrent_recompilation_enabled()) { optimizing_compiler_thread_->Stop(); delete optimizing_compiler_thread_; diff --git a/src/isolate.h b/src/isolate.h index 9ef6fc732a..c0376aeac0 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -78,6 +78,7 @@ typedef void* ExternalReferenceRedirectorPointer(); class Debug; class Debugger; +class PromiseOnStack; #if !defined(__arm__) && V8_TARGET_ARCH_ARM || \ !defined(__aarch64__) && V8_TARGET_ARCH_ARM64 || \ @@ -240,11 +241,7 @@ class ThreadLocalTop BASE_EMBEDDED { v8::TryCatch::JSStackComparableAddress(try_catch_handler())); } - void Free() { - DCHECK(!has_pending_message_); - DCHECK(!external_caught_exception_); - DCHECK(try_catch_handler_ == NULL); - } + void Free(); Isolate* isolate_; // The context where the current execution method is created and for variable @@ -270,6 +267,11 @@ class ThreadLocalTop BASE_EMBEDDED { Address c_entry_fp_; // the frame pointer of the top c entry frame Address handler_; // try-blocks are chained through the stack + // Throwing an exception may cause a Promise rejection. For this purpose + // we keep track of a stack of nested promises and the corresponding + // try-catch handlers. + PromiseOnStack* promise_on_stack_; + #ifdef USE_SIMULATOR Simulator* simulator_; #endif @@ -676,6 +678,11 @@ class Isolate { // JavaScript code. If an exception is scheduled true is returned. bool OptionalRescheduleException(bool is_bottom_call); + // Push and pop a promise and the current try-catch handler. + void PushPromise(Handle promise); + void PopPromise(); + Handle GetPromiseOnStackOnThrow(); + class ExceptionScope { public: explicit ExceptionScope(Isolate* isolate) : @@ -1349,6 +1356,22 @@ class Isolate { #undef THREAD_LOCAL_TOP_ACCESSOR +class PromiseOnStack { + public: + PromiseOnStack(StackHandler* handler, Handle promise, + PromiseOnStack* prev) + : handler_(handler), promise_(promise), prev_(prev) {} + StackHandler* handler() { return handler_; } + Handle promise() { return promise_; } + PromiseOnStack* prev() { return prev_; } + + private: + StackHandler* handler_; + Handle promise_; + PromiseOnStack* prev_; +}; + + // If the GCC version is 4.1.x or 4.2.x an additional field is added to the // class as a work around for a bug in the generated code found with these // versions of GCC. See V8 issue 122 for details. diff --git a/src/runtime.cc b/src/runtime.cc index 1fbedc6adc..50a4cc9598 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -5495,7 +5495,7 @@ RUNTIME_FUNCTION(Runtime_DebugPushPromise) { DCHECK(args.length() == 1); HandleScope scope(isolate); CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); - isolate->debug()->PushPromise(promise); + isolate->PushPromise(promise); return isolate->heap()->undefined_value(); } @@ -5503,7 +5503,7 @@ RUNTIME_FUNCTION(Runtime_DebugPushPromise) { RUNTIME_FUNCTION(Runtime_DebugPopPromise) { DCHECK(args.length() == 0); SealHandleScope shs(isolate); - isolate->debug()->PopPromise(); + isolate->PopPromise(); return isolate->heap()->undefined_value(); } diff --git a/src/v8threads.cc b/src/v8threads.cc index 010f50b3e4..a46b289ba1 100644 --- a/src/v8threads.cc +++ b/src/v8threads.cc @@ -307,6 +307,9 @@ void ThreadManager::EagerlyArchiveThread() { void ThreadManager::FreeThreadResources() { + DCHECK(!isolate_->has_pending_exception()); + DCHECK(!isolate_->external_caught_exception()); + DCHECK(isolate_->try_catch_handler() == NULL); isolate_->handle_scope_implementer()->FreeThreadResources(); isolate_->FreeThreadResources(); isolate_->debug()->FreeThreadResources();