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
This commit is contained in:
yangguo@chromium.org 2014-08-13 11:14:35 +00:00
parent 07ab6791a1
commit 1b17f59b5d
6 changed files with 85 additions and 92 deletions

View File

@ -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<JSObject> promise)
: isolate_(isolate), prev_(prev) {
handler_ = StackHandler::FromAddress(
Isolate::handler(isolate->thread_local_top()));
promise_ =
Handle<JSObject>::cast(isolate->global_handles()->Create(*promise));
}
PromiseOnStack::~PromiseOnStack() {
isolate_->global_handles()->Destroy(
Handle<Object>::cast(promise_).location());
}
void Debug::PushPromise(Handle<JSObject> 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<Object> Debug::GetPromiseOnStackOnThrow() {
Handle<Object> 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<JSObject> promise) {
Handle<JSFunction> fun = Handle<JSFunction>::cast(
JSObject::GetDataProperty(isolate_->js_builtins_object(),
@ -2567,7 +2512,7 @@ MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) {
void Debug::OnThrow(Handle<Object> exception, bool uncaught) {
if (in_debug_scope() || ignore_events()) return;
HandleScope scope(isolate_);
OnException(exception, uncaught, GetPromiseOnStackOnThrow());
OnException(exception, uncaught, isolate_->GetPromiseOnStackOnThrow());
}

View File

@ -333,22 +333,6 @@ class LockingCommandMessageQueue BASE_EMBEDDED {
};
class PromiseOnStack {
public:
PromiseOnStack(Isolate* isolate, PromiseOnStack* prev,
Handle<JSObject> getter);
~PromiseOnStack();
StackHandler* handler() { return handler_; }
Handle<JSObject> promise() { return promise_; }
PromiseOnStack* prev() { return prev_; }
private:
Isolate* isolate_;
StackHandler* handler_;
Handle<JSObject> 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<JSObject> 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<Object> GetPromiseOnStackOnThrow();
bool PromiseHasRejectHandler(Handle<JSObject> 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

View File

@ -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<JSObject> promise) {
ThreadLocalTop* tltop = thread_local_top();
PromiseOnStack* prev = tltop->promise_on_stack_;
StackHandler* handler = StackHandler::FromAddress(Isolate::handler(tltop));
Handle<JSObject> global_handle =
Handle<JSObject>::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<Object> global_handle = tltop->promise_on_stack_->promise();
delete tltop->promise_on_stack_;
tltop->promise_on_stack_ = prev;
global_handles()->Destroy(global_handle.location());
}
Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
Handle<Object> 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_;

View File

@ -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<JSObject> promise);
void PopPromise();
Handle<Object> 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<JSObject> promise,
PromiseOnStack* prev)
: handler_(handler), promise_(promise), prev_(prev) {}
StackHandler* handler() { return handler_; }
Handle<JSObject> promise() { return promise_; }
PromiseOnStack* prev() { return prev_; }
private:
StackHandler* handler_;
Handle<JSObject> 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.

View File

@ -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();
}

View File

@ -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();