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:
parent
07ab6791a1
commit
1b17f59b5d
57
src/debug.cc
57
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<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());
|
||||
}
|
||||
|
||||
|
||||
|
29
src/debug.h
29
src/debug.h
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user