Commit d0d4189d authored by yangguo's avatar yangguo Committed by Commit bot

[debugger] implement legacy debug event listeners via debug delegate.

R=jgruber@chromium.org
BUG=v8:5530

Review-Url: https://codereview.chromium.org/2682593003
Cr-Commit-Position: refs/heads/master@{#43059}
parent 1d3317ff
...@@ -8912,11 +8912,15 @@ bool Debug::SetDebugEventListener(Isolate* isolate, EventCallback that, ...@@ -8912,11 +8912,15 @@ bool Debug::SetDebugEventListener(Isolate* isolate, EventCallback that,
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
ENTER_V8(i_isolate); ENTER_V8(i_isolate);
i::HandleScope scope(i_isolate); i::HandleScope scope(i_isolate);
i::Handle<i::Object> foreign = i_isolate->factory()->undefined_value(); if (that == nullptr) {
if (that != NULL) { i_isolate->debug()->SetDebugDelegate(nullptr, false);
foreign = i_isolate->factory()->NewForeign(FUNCTION_ADDR(that)); } else {
i::Handle<i::Object> i_data = i_isolate->factory()->undefined_value();
if (!data.IsEmpty()) i_data = Utils::OpenHandle(*data);
i::NativeDebugDelegate* delegate =
new i::NativeDebugDelegate(i_isolate, that, i_data);
i_isolate->debug()->SetDebugDelegate(delegate, true);
} }
i_isolate->debug()->SetEventListener(foreign, Utils::OpenHandle(*data, true));
return true; return true;
} }
...@@ -9369,7 +9373,7 @@ void debug::SetDebugDelegate(Isolate* v8_isolate, ...@@ -9369,7 +9373,7 @@ void debug::SetDebugDelegate(Isolate* v8_isolate,
debug::DebugDelegate* delegate) { debug::DebugDelegate* delegate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8(isolate); ENTER_V8(isolate);
isolate->debug()->SetDebugDelegate(delegate); isolate->debug()->SetDebugDelegate(delegate, false);
} }
void debug::ResetBlackboxedStateCache(Isolate* v8_isolate, void debug::ResetBlackboxedStateCache(Isolate* v8_isolate,
......
...@@ -163,7 +163,8 @@ class DebugDelegate { ...@@ -163,7 +163,8 @@ class DebugDelegate {
virtual void ExceptionThrown(v8::Local<v8::Context> paused_context, virtual void ExceptionThrown(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state, v8::Local<v8::Object> exec_state,
v8::Local<v8::Value> exception, v8::Local<v8::Value> exception,
bool is_promise_rejection, bool is_uncaught) {} v8::Local<v8::Value> promise, bool is_uncaught) {
}
virtual bool IsFunctionBlackboxed(v8::Local<debug::Script> script, virtual bool IsFunctionBlackboxed(v8::Local<debug::Script> script,
const debug::Location& start, const debug::Location& start,
const debug::Location& end) { const debug::Location& end) {
......
...@@ -38,15 +38,12 @@ namespace internal { ...@@ -38,15 +38,12 @@ namespace internal {
Debug::Debug(Isolate* isolate) Debug::Debug(Isolate* isolate)
: debug_context_(Handle<Context>()), : debug_context_(Handle<Context>()),
event_listener_(Handle<Object>()),
event_listener_data_(Handle<Object>()),
is_active_(false), is_active_(false),
hook_on_function_call_(false), hook_on_function_call_(false),
is_suppressed_(false), is_suppressed_(false),
live_edit_enabled_(true), // TODO(yangguo): set to false by default. live_edit_enabled_(true), // TODO(yangguo): set to false by default.
break_disabled_(false), break_disabled_(false),
break_points_active_(true), break_points_active_(true),
in_debug_event_listener_(false),
break_on_exception_(false), break_on_exception_(false),
break_on_uncaught_exception_(false), break_on_uncaught_exception_(false),
side_effect_check_failed_(false), side_effect_check_failed_(false),
...@@ -473,6 +470,7 @@ bool Debug::Load() { ...@@ -473,6 +470,7 @@ bool Debug::Load() {
void Debug::Unload() { void Debug::Unload() {
ClearAllBreakPoints(); ClearAllBreakPoints();
ClearStepping(); ClearStepping();
RemoveDebugDelegate();
// Return debugger is not loaded. // Return debugger is not loaded.
if (!is_loaded()) return; if (!is_loaded()) return;
...@@ -497,6 +495,7 @@ void Debug::Break(JavaScriptFrame* frame) { ...@@ -497,6 +495,7 @@ void Debug::Break(JavaScriptFrame* frame) {
// Postpone interrupt during breakpoint processing. // Postpone interrupt during breakpoint processing.
PostponeInterruptsScope postpone(isolate_); PostponeInterruptsScope postpone(isolate_);
DisableBreak no_recursive_break(this);
// Return if we fail to retrieve debug info. // Return if we fail to retrieve debug info.
Handle<JSFunction> function(frame->function()); Handle<JSFunction> function(frame->function());
...@@ -1667,11 +1666,11 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script, ...@@ -1667,11 +1666,11 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
return CallFunction("MakeCompileEvent", arraysize(argv), argv); return CallFunction("MakeCompileEvent", arraysize(argv), argv);
} }
MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<Smi> type, MaybeHandle<Object> Debug::MakeAsyncTaskEvent(
Handle<Smi> id) { v8::debug::PromiseDebugActionType type, int id) {
DCHECK(id->IsNumber());
// Create the async task event object. // Create the async task event object.
Handle<Object> argv[] = {type, id}; Handle<Object> argv[] = {Handle<Smi>(Smi::FromInt(type), isolate_),
Handle<Smi>(Smi::FromInt(id), isolate_)};
return CallFunction("MakeAsyncTaskEvent", arraysize(argv), argv); return CallFunction("MakeAsyncTaskEvent", arraysize(argv), argv);
} }
...@@ -1764,6 +1763,9 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) { ...@@ -1764,6 +1763,9 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
// Check whether the promise reject is considered an uncaught exception. // Check whether the promise reject is considered an uncaught exception.
uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise); uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
} }
if (!debug_delegate_) return;
// Bail out if exception breaks are not active // Bail out if exception breaks are not active
if (uncaught) { if (uncaught) {
// Uncaught exceptions are reported by either flags. // Uncaught exceptions are reported by either flags.
...@@ -1773,7 +1775,6 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) { ...@@ -1773,7 +1775,6 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
if (!break_on_exception_) return; if (!break_on_exception_) return;
} }
bool empty_js_stack = false;
{ {
JavaScriptFrameIterator it(isolate_); JavaScriptFrameIterator it(isolate_);
// Check whether the top frame is blackboxed or the break location is muted. // Check whether the top frame is blackboxed or the break location is muted.
...@@ -1781,38 +1782,24 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) { ...@@ -1781,38 +1782,24 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
IsExceptionBlackboxed(uncaught))) { IsExceptionBlackboxed(uncaught))) {
return; return;
} }
empty_js_stack = it.done(); if (it.done()) return; // Do not trigger an event with an empty stack.
} }
DebugScope debug_scope(this); DebugScope debug_scope(this);
if (debug_scope.failed()) return; if (debug_scope.failed()) return;
HandleScope scope(isolate_);
PostponeInterruptsScope postpone(isolate_);
DisableBreak no_recursive_break(this);
if (debug_delegate_ && !empty_js_stack) { // Create the execution state.
HandleScope scope(isolate_); Handle<Object> exec_state;
// Create the execution state.
Handle<Object> exec_state;
// Bail out and don't call debugger if exception.
if (!MakeExecutionState().ToHandle(&exec_state)) return;
debug_delegate_->ExceptionThrown(
GetDebugEventContext(isolate_),
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
v8::Utils::ToLocal(exception), promise->IsJSObject(), uncaught);
}
if (debug_delegate_ && !non_inspector_listener_exists()) return;
// Create the event data object.
Handle<Object> event_data;
// Bail out and don't call debugger if exception. // Bail out and don't call debugger if exception.
if (!MakeExceptionEvent( if (!MakeExecutionState().ToHandle(&exec_state)) return;
exception, uncaught, promise).ToHandle(&event_data)) {
return;
}
// Process debug event. debug_delegate_->ExceptionThrown(
ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data)); GetDebugEventContext(isolate_),
// Return to continue execution from where the exception was thrown. v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
v8::Utils::ToLocal(exception), v8::Utils::ToLocal(promise), uncaught);
} }
void Debug::OnDebugBreak(Handle<Object> break_points_hit) { void Debug::OnDebugBreak(Handle<Object> break_points_hit) {
...@@ -1825,32 +1812,20 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit) { ...@@ -1825,32 +1812,20 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit) {
PrintBreakLocation(); PrintBreakLocation();
#endif // DEBUG #endif // DEBUG
if (debug_delegate_) { if (!debug_delegate_) return;
HandleScope scope(isolate_);
// Create the execution state.
Handle<Object> exec_state;
// Bail out and don't call debugger if exception.
if (!MakeExecutionState().ToHandle(&exec_state)) return;
bool previous = in_debug_event_listener_;
in_debug_event_listener_ = true;
debug_delegate_->BreakProgramRequested(
GetDebugEventContext(isolate_),
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
v8::Utils::ToLocal(break_points_hit));
in_debug_event_listener_ = previous;
if (!non_inspector_listener_exists()) return;
}
HandleScope scope(isolate_); HandleScope scope(isolate_);
// Create the event data object. PostponeInterruptsScope no_interrupts(isolate_);
Handle<Object> event_data; DisableBreak no_recursive_break(this);
// Create the execution state.
Handle<Object> exec_state;
// Bail out and don't call debugger if exception. // Bail out and don't call debugger if exception.
if (!MakeBreakEvent(break_points_hit).ToHandle(&event_data)) return; if (!MakeExecutionState().ToHandle(&exec_state)) return;
// Process debug event. debug_delegate_->BreakProgramRequested(
ProcessDebugEvent(v8::Break, Handle<JSObject>::cast(event_data)); GetDebugEventContext(isolate_),
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
v8::Utils::ToLocal(break_points_hit));
} }
...@@ -1881,7 +1856,7 @@ void SendAsyncTaskEventCancel(const v8::WeakCallbackInfo<void>& info) { ...@@ -1881,7 +1856,7 @@ void SendAsyncTaskEventCancel(const v8::WeakCallbackInfo<void>& info) {
reinterpret_cast<CollectedCallbackData*>(info.GetParameter())); reinterpret_cast<CollectedCallbackData*>(info.GetParameter()));
if (!data->debug->is_active()) return; if (!data->debug->is_active()) return;
HandleScope scope(data->isolate); HandleScope scope(data->isolate);
data->debug->OnAsyncTaskEvent(debug::kDebugPromiseCollected, data->id); data->debug->OnAsyncTaskEvent(debug::kDebugPromiseCollected, data->id, 0);
} }
void ResetPromiseHandle(const v8::WeakCallbackInfo<void>& info) { void ResetPromiseHandle(const v8::WeakCallbackInfo<void>& info) {
...@@ -1929,21 +1904,21 @@ void Debug::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise, ...@@ -1929,21 +1904,21 @@ void Debug::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
int id = GetReferenceAsyncTaskId(isolate_, promise); int id = GetReferenceAsyncTaskId(isolate_, promise);
switch (type) { switch (type) {
case PromiseHookType::kInit: case PromiseHookType::kInit:
debug_delegate_->PromiseEventOccurred( OnAsyncTaskEvent(debug::kDebugPromiseCreated, id,
debug::kDebugPromiseCreated, id, parent->IsJSPromise()
parent->IsJSPromise() ? GetReferenceAsyncTaskId( ? GetReferenceAsyncTaskId(
isolate_, Handle<JSPromise>::cast(parent)) isolate_, Handle<JSPromise>::cast(parent))
: 0); : 0);
return; return;
case PromiseHookType::kResolve: case PromiseHookType::kResolve:
// We can't use this hook because it's called before promise object will // We can't use this hook because it's called before promise object will
// get resolved status. // get resolved status.
return; return;
case PromiseHookType::kBefore: case PromiseHookType::kBefore:
OnAsyncTaskEvent(debug::kDebugWillHandle, id); OnAsyncTaskEvent(debug::kDebugWillHandle, id, 0);
return; return;
case PromiseHookType::kAfter: case PromiseHookType::kAfter:
OnAsyncTaskEvent(debug::kDebugDidHandle, id); OnAsyncTaskEvent(debug::kDebugDidHandle, id, 0);
return; return;
} }
} }
...@@ -1988,7 +1963,10 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) { ...@@ -1988,7 +1963,10 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
if (!shared->computed_debug_is_blackboxed()) { if (!shared->computed_debug_is_blackboxed()) {
bool is_blackboxed = false; bool is_blackboxed = false;
if (shared->script()->IsScript()) { if (shared->script()->IsScript()) {
SuppressDebug while_processing(this);
HandleScope handle_scope(isolate_); HandleScope handle_scope(isolate_);
PostponeInterruptsScope no_interrupts(isolate_);
DisableBreak no_recursive_break(this);
Handle<Script> script(Script::cast(shared->script())); Handle<Script> script(Script::cast(shared->script()));
if (script->type() == i::Script::TYPE_NORMAL) { if (script->type() == i::Script::TYPE_NORMAL) {
debug::Location start = debug::Location start =
...@@ -2004,70 +1982,17 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) { ...@@ -2004,70 +1982,17 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
return shared->debug_is_blackboxed(); return shared->debug_is_blackboxed();
} }
void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id) { void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
int parent_id) {
if (in_debug_scope() || ignore_events()) return; if (in_debug_scope() || ignore_events()) return;
if (!debug_delegate_) return;
if (debug_delegate_) { SuppressDebug while_processing(this);
debug_delegate_->PromiseEventOccurred(type, id, 0); DebugScope debug_scope(isolate_->debug());
if (!non_inspector_listener_exists()) return;
}
HandleScope scope(isolate_);
DebugScope debug_scope(this);
if (debug_scope.failed()) return; if (debug_scope.failed()) return;
// Create the script collected state object.
Handle<Object> event_data;
// Bail out and don't call debugger if exception.
if (!MakeAsyncTaskEvent(handle(Smi::FromInt(type), isolate_),
handle(Smi::FromInt(id), isolate_))
.ToHandle(&event_data))
return;
// Process debug event.
ProcessDebugEvent(v8::AsyncTaskEvent, Handle<JSObject>::cast(event_data));
}
void Debug::ProcessDebugEvent(v8::DebugEvent event,
Handle<JSObject> event_data) {
// Notify registered debug event listener. This can be either a C or
// a JavaScript function.
if (event_listener_.is_null()) return;
HandleScope scope(isolate_); HandleScope scope(isolate_);
PostponeInterruptsScope no_interrupts(isolate_);
// Create the execution state. DisableBreak no_recursive_break(this);
Handle<Object> exec_state; debug_delegate_->PromiseEventOccurred(type, id, parent_id);
// Bail out and don't call debugger if exception.
if (!MakeExecutionState().ToHandle(&exec_state)) return;
// Prevent other interrupts from triggering, for example API callbacks,
// while dispatching event listners.
PostponeInterruptsScope postpone(isolate_);
bool previous = in_debug_event_listener_;
in_debug_event_listener_ = true;
if (event_listener_->IsForeign()) {
// Invoke the C debug event listener.
v8::Debug::EventCallback callback = FUNCTION_CAST<v8::Debug::EventCallback>(
Handle<Foreign>::cast(event_listener_)->foreign_address());
EventDetailsImpl event_details(event, Handle<JSObject>::cast(exec_state),
Handle<JSObject>::cast(event_data),
event_listener_data_);
callback(event_details);
CHECK(!isolate_->has_scheduled_exception());
} else {
// Invoke the JavaScript debug event listener.
DCHECK(event_listener_->IsJSFunction());
Handle<Object> argv[] = { Handle<Object>(Smi::FromInt(event), isolate_),
exec_state,
event_data,
event_listener_data_ };
Handle<JSReceiver> global = isolate_->global_proxy();
MaybeHandle<Object> result =
Execution::Call(isolate_, Handle<JSFunction>::cast(event_listener_),
global, arraysize(argv), argv);
CHECK(!result.is_null()); // Listeners must not throw.
}
in_debug_event_listener_ = previous;
} }
void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) { void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
...@@ -2076,24 +2001,15 @@ void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) { ...@@ -2076,24 +2001,15 @@ void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
script->type() != i::Script::TYPE_WASM) { script->type() != i::Script::TYPE_WASM) {
return; return;
} }
if (!debug_delegate_) return;
SuppressDebug while_processing(this); SuppressDebug while_processing(this);
DebugScope debug_scope(this); DebugScope debug_scope(this);
if (debug_scope.failed()) return; if (debug_scope.failed()) return;
if (debug_delegate_) {
debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
event != v8::AfterCompile);
if (!non_inspector_listener_exists()) return;
}
HandleScope scope(isolate_); HandleScope scope(isolate_);
// Create the compile state object. PostponeInterruptsScope postpone(isolate_);
Handle<Object> event_data; DisableBreak no_recursive_break(this);
// Bail out and don't call debugger if exception. debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
if (!MakeCompileEvent(script, event).ToHandle(&event_data)) return; event != v8::AfterCompile);
// Process debug event.
ProcessDebugEvent(event, Handle<JSObject>::cast(event_data));
} }
...@@ -2105,27 +2021,6 @@ Handle<Context> Debug::GetDebugContext() { ...@@ -2105,27 +2021,6 @@ Handle<Context> Debug::GetDebugContext() {
return handle(*debug_context(), isolate_); return handle(*debug_context(), isolate_);
} }
void Debug::SetEventListener(Handle<Object> callback,
Handle<Object> data) {
GlobalHandles* global_handles = isolate_->global_handles();
// Remove existing entry.
GlobalHandles::Destroy(event_listener_.location());
event_listener_ = Handle<Object>();
GlobalHandles::Destroy(event_listener_data_.location());
event_listener_data_ = Handle<Object>();
// Set new entry.
if (!callback->IsNullOrUndefined(isolate_)) {
event_listener_ = global_handles->Create(*callback);
if (data.is_null()) data = isolate_->factory()->undefined_value();
event_listener_data_ = global_handles->Create(*data);
}
UpdateState();
}
int Debug::CurrentFrameCount() { int Debug::CurrentFrameCount() {
StackTraceFrameIterator it(isolate_); StackTraceFrameIterator it(isolate_);
if (break_frame_id() != StackFrame::NO_ID) { if (break_frame_id() != StackFrame::NO_ID) {
...@@ -2147,13 +2042,25 @@ int Debug::CurrentFrameCount() { ...@@ -2147,13 +2042,25 @@ int Debug::CurrentFrameCount() {
return counter; return counter;
} }
void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) { void Debug::SetDebugDelegate(debug::DebugDelegate* delegate,
bool pass_ownership) {
RemoveDebugDelegate();
debug_delegate_ = delegate; debug_delegate_ = delegate;
owns_debug_delegate_ = pass_ownership;
UpdateState(); UpdateState();
} }
void Debug::RemoveDebugDelegate() {
if (debug_delegate_ == nullptr) return;
if (owns_debug_delegate_) {
owns_debug_delegate_ = false;
delete debug_delegate_;
}
debug_delegate_ = nullptr;
}
void Debug::UpdateState() { void Debug::UpdateState() {
bool is_active = !event_listener_.is_null() || debug_delegate_ != nullptr; bool is_active = debug_delegate_ != nullptr;
if (is_active || in_debug_scope()) { if (is_active || in_debug_scope()) {
// Note that the debug context could have already been loaded to // Note that the debug context could have already been loaded to
// bootstrap test cases. // bootstrap test cases.
...@@ -2358,59 +2265,158 @@ bool Debug::PerformSideEffectCheckForCallback(Address function) { ...@@ -2358,59 +2265,158 @@ bool Debug::PerformSideEffectCheckForCallback(Address function) {
return false; return false;
} }
NoSideEffectScope::~NoSideEffectScope() { void LegacyDebugDelegate::PromiseEventOccurred(
if (isolate_->needs_side_effect_check() && v8::debug::PromiseDebugActionType type, int id, int parent_id) {
isolate_->debug()->side_effect_check_failed_) { Handle<Object> event_data;
DCHECK(isolate_->has_pending_exception()); if (isolate_->debug()->MakeAsyncTaskEvent(type, id).ToHandle(&event_data)) {
DCHECK_EQ(isolate_->heap()->termination_exception(), ProcessDebugEvent(v8::AsyncTaskEvent, Handle<JSObject>::cast(event_data));
isolate_->pending_exception()); }
// Convert the termination exception into a regular exception. }
isolate_->CancelTerminateExecution();
isolate_->Throw(*isolate_->factory()->NewEvalError( void LegacyDebugDelegate::ScriptCompiled(v8::Local<v8::debug::Script> script,
MessageTemplate::kNoSideEffectDebugEvaluate)); bool is_compile_error) {
Handle<Object> event_data;
v8::DebugEvent event = is_compile_error ? v8::CompileError : v8::AfterCompile;
if (isolate_->debug()
->MakeCompileEvent(v8::Utils::OpenHandle(*script), event)
.ToHandle(&event_data)) {
ProcessDebugEvent(event, Handle<JSObject>::cast(event_data));
}
}
void LegacyDebugDelegate::BreakProgramRequested(
v8::Local<v8::Context> paused_context, v8::Local<v8::Object> exec_state,
v8::Local<v8::Value> break_points_hit) {
Handle<Object> event_data;
if (isolate_->debug()
->MakeBreakEvent(v8::Utils::OpenHandle(*break_points_hit))
.ToHandle(&event_data)) {
ProcessDebugEvent(
v8::Break, Handle<JSObject>::cast(event_data),
Handle<JSObject>::cast(v8::Utils::OpenHandle(*exec_state)));
}
}
void LegacyDebugDelegate::ExceptionThrown(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state,
v8::Local<v8::Value> exception,
v8::Local<v8::Value> promise,
bool is_uncaught) {
Handle<Object> event_data;
if (isolate_->debug()
->MakeExceptionEvent(v8::Utils::OpenHandle(*exception), is_uncaught,
v8::Utils::OpenHandle(*promise))
.ToHandle(&event_data)) {
ProcessDebugEvent(
v8::Exception, Handle<JSObject>::cast(event_data),
Handle<JSObject>::cast(v8::Utils::OpenHandle(*exec_state)));
}
}
void LegacyDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
Handle<JSObject> event_data) {
Handle<Object> exec_state;
if (isolate_->debug()->MakeExecutionState().ToHandle(&exec_state)) {
ProcessDebugEvent(event, event_data, Handle<JSObject>::cast(exec_state));
} }
isolate_->set_needs_side_effect_check(old_needs_side_effect_check_);
isolate_->debug()->UpdateHookOnFunctionCall();
isolate_->debug()->side_effect_check_failed_ = false;
} }
EventDetailsImpl::EventDetailsImpl(DebugEvent event, JavaScriptDebugDelegate::JavaScriptDebugDelegate(Isolate* isolate,
Handle<JSObject> exec_state, Handle<JSFunction> listener,
Handle<JSObject> event_data, Handle<Object> data)
Handle<Object> callback_data) : LegacyDebugDelegate(isolate) {
GlobalHandles* global_handles = isolate->global_handles();
listener_ = Handle<JSFunction>::cast(global_handles->Create(*listener));
data_ = global_handles->Create(*data);
}
JavaScriptDebugDelegate::~JavaScriptDebugDelegate() {
GlobalHandles::Destroy(Handle<Object>::cast(listener_).location());
GlobalHandles::Destroy(data_.location());
}
void JavaScriptDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
Handle<JSObject> event_data,
Handle<JSObject> exec_state) {
Handle<Object> argv[] = {Handle<Object>(Smi::FromInt(event), isolate_),
exec_state, event_data, data_};
Handle<JSReceiver> global = isolate_->global_proxy();
// Listener must not throw.
Execution::Call(isolate_, listener_, global, arraysize(argv), argv)
.ToHandleChecked();
}
NativeDebugDelegate::NativeDebugDelegate(Isolate* isolate,
v8::Debug::EventCallback callback,
Handle<Object> data)
: LegacyDebugDelegate(isolate), callback_(callback) {
data_ = isolate->global_handles()->Create(*data);
}
NativeDebugDelegate::~NativeDebugDelegate() {
GlobalHandles::Destroy(data_.location());
}
NativeDebugDelegate::EventDetails::EventDetails(DebugEvent event,
Handle<JSObject> exec_state,
Handle<JSObject> event_data,
Handle<Object> callback_data)
: event_(event), : event_(event),
exec_state_(exec_state), exec_state_(exec_state),
event_data_(event_data), event_data_(event_data),
callback_data_(callback_data) {} callback_data_(callback_data) {}
DebugEvent EventDetailsImpl::GetEvent() const { DebugEvent NativeDebugDelegate::EventDetails::GetEvent() const {
return event_; return event_;
} }
v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetExecutionState()
v8::Local<v8::Object> EventDetailsImpl::GetExecutionState() const { const {
return v8::Utils::ToLocal(exec_state_); return v8::Utils::ToLocal(exec_state_);
} }
v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetEventData() const {
v8::Local<v8::Object> EventDetailsImpl::GetEventData() const {
return v8::Utils::ToLocal(event_data_); return v8::Utils::ToLocal(event_data_);
} }
v8::Local<v8::Context> NativeDebugDelegate::EventDetails::GetEventContext()
v8::Local<v8::Context> EventDetailsImpl::GetEventContext() const { const {
return GetDebugEventContext(exec_state_->GetIsolate()); return GetDebugEventContext(exec_state_->GetIsolate());
} }
v8::Local<v8::Value> NativeDebugDelegate::EventDetails::GetCallbackData()
v8::Local<v8::Value> EventDetailsImpl::GetCallbackData() const { const {
return v8::Utils::ToLocal(callback_data_); return v8::Utils::ToLocal(callback_data_);
} }
v8::Isolate* NativeDebugDelegate::EventDetails::GetIsolate() const {
v8::Isolate* EventDetailsImpl::GetIsolate() const {
return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate()); return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate());
} }
void NativeDebugDelegate::ProcessDebugEvent(v8::DebugEvent event,
Handle<JSObject> event_data,
Handle<JSObject> exec_state) {
EventDetails event_details(event, exec_state, event_data, data_);
Isolate* isolate = isolate_;
callback_(event_details);
CHECK(!isolate->has_scheduled_exception());
}
NoSideEffectScope::~NoSideEffectScope() {
if (isolate_->needs_side_effect_check() &&
isolate_->debug()->side_effect_check_failed_) {
DCHECK(isolate_->has_pending_exception());
DCHECK_EQ(isolate_->heap()->termination_exception(),
isolate_->pending_exception());
// Convert the termination exception into a regular exception.
isolate_->CancelTerminateExecution();
isolate_->Throw(*isolate_->factory()->NewEvalError(
MessageTemplate::kNoSideEffectDebugEvaluate));
}
isolate_->set_needs_side_effect_check(old_needs_side_effect_check_);
isolate_->debug()->UpdateHookOnFunctionCall();
isolate_->debug()->side_effect_check_failed_ = false;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -230,28 +230,6 @@ class DebugInfoListNode { ...@@ -230,28 +230,6 @@ class DebugInfoListNode {
DebugInfoListNode* next_; DebugInfoListNode* next_;
}; };
// Details of the debug event delivered to the debug event listener.
class EventDetailsImpl : public v8::Debug::EventDetails {
public:
EventDetailsImpl(DebugEvent event, Handle<JSObject> exec_state,
Handle<JSObject> event_data, Handle<Object> callback_data);
virtual DebugEvent GetEvent() const;
virtual v8::Local<v8::Object> GetExecutionState() const;
virtual v8::Local<v8::Object> GetEventData() const;
virtual v8::Local<v8::Context> GetEventContext() const;
virtual v8::Local<v8::Value> GetCallbackData() const;
virtual v8::Debug::ClientData* GetClientData() const { return nullptr; }
virtual v8::Isolate* GetIsolate() const;
private:
DebugEvent event_; // Debug event causing the break.
Handle<JSObject> exec_state_; // Current execution state.
Handle<JSObject> event_data_; // Data associated with the event.
Handle<Object> callback_data_; // User data passed with the callback
// when it was registered.
};
class DebugFeatureTracker { class DebugFeatureTracker {
public: public:
enum Feature { enum Feature {
...@@ -290,10 +268,9 @@ class Debug { ...@@ -290,10 +268,9 @@ class Debug {
void OnPromiseReject(Handle<Object> promise, Handle<Object> value); void OnPromiseReject(Handle<Object> promise, Handle<Object> value);
void OnCompileError(Handle<Script> script); void OnCompileError(Handle<Script> script);
void OnAfterCompile(Handle<Script> script); void OnAfterCompile(Handle<Script> script);
void OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id); void OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
int parent_id);
// API facing.
void SetEventListener(Handle<Object> callback, Handle<Object> data);
MUST_USE_RESULT MaybeHandle<Object> Call(Handle<Object> fun, MUST_USE_RESULT MaybeHandle<Object> Call(Handle<Object> fun,
Handle<Object> data); Handle<Object> data);
Handle<Context> GetDebugContext(); Handle<Context> GetDebugContext();
...@@ -346,7 +323,7 @@ class Debug { ...@@ -346,7 +323,7 @@ class Debug {
bool IsBlackboxed(Handle<SharedFunctionInfo> shared); bool IsBlackboxed(Handle<SharedFunctionInfo> shared);
void SetDebugDelegate(debug::DebugDelegate* delegate); void SetDebugDelegate(debug::DebugDelegate* delegate, bool pass_ownership);
// Returns whether the operation succeeded. // Returns whether the operation succeeded.
bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared); bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
...@@ -446,9 +423,11 @@ class Debug { ...@@ -446,9 +423,11 @@ class Debug {
private: private:
explicit Debug(Isolate* isolate); explicit Debug(Isolate* isolate);
~Debug() { DCHECK_NULL(debug_delegate_); }
void UpdateState(); void UpdateState();
void UpdateHookOnFunctionCall(); void UpdateHookOnFunctionCall();
void RemoveDebugDelegate();
void Unload(); void Unload();
void SetNextBreakId() { void SetNextBreakId() {
thread_local_.break_id_ = ++thread_local_.break_count_; thread_local_.break_id_ = ++thread_local_.break_count_;
...@@ -460,9 +439,7 @@ class Debug { ...@@ -460,9 +439,7 @@ class Debug {
inline bool ignore_events() const { inline bool ignore_events() const {
return is_suppressed_ || !is_active_ || isolate_->needs_side_effect_check(); return is_suppressed_ || !is_active_ || isolate_->needs_side_effect_check();
} }
inline bool break_disabled() const { inline bool break_disabled() const { return break_disabled_; }
return break_disabled_ || in_debug_event_listener_;
}
void clear_suspended_generator() { void clear_suspended_generator() {
thread_local_.suspended_generator_ = Smi::kZero; thread_local_.suspended_generator_ = Smi::kZero;
...@@ -472,17 +449,6 @@ class Debug { ...@@ -472,17 +449,6 @@ class Debug {
return thread_local_.suspended_generator_ != Smi::kZero; return thread_local_.suspended_generator_ != Smi::kZero;
} }
// There are three types of event listeners: C++ message_handler,
// JavaScript event listener and C++ event listener.
// Currently inspector still uses C++ event listener and installs
// more specific event listeners for part of events. Calling of
// C++ event listener is redundant when more specific event listener
// is presented. Other clients can install JavaScript event listener
// (e.g. some of NodeJS module).
bool non_inspector_listener_exists() const {
return !event_listener_.is_null() && !event_listener_->IsForeign();
}
bool IsExceptionBlackboxed(bool uncaught); bool IsExceptionBlackboxed(bool uncaught);
void OnException(Handle<Object> exception, Handle<Object> promise); void OnException(Handle<Object> exception, Handle<Object> promise);
...@@ -497,8 +463,8 @@ class Debug { ...@@ -497,8 +463,8 @@ class Debug {
Handle<Object> promise); Handle<Object> promise);
MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent( MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent(
Handle<Script> script, v8::DebugEvent type); Handle<Script> script, v8::DebugEvent type);
MUST_USE_RESULT MaybeHandle<Object> MakeAsyncTaskEvent(Handle<Smi> type, MUST_USE_RESULT MaybeHandle<Object> MakeAsyncTaskEvent(
Handle<Smi> id); v8::debug::PromiseDebugActionType type, int id);
void ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script); void ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script);
void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data); void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data);
...@@ -538,10 +504,9 @@ class Debug { ...@@ -538,10 +504,9 @@ class Debug {
// Global handles. // Global handles.
Handle<Context> debug_context_; Handle<Context> debug_context_;
Handle<Object> event_listener_;
Handle<Object> event_listener_data_;
debug::DebugDelegate* debug_delegate_ = nullptr; debug::DebugDelegate* debug_delegate_ = nullptr;
bool owns_debug_delegate_ = false;
// Debugger is active, i.e. there is a debug event listener attached. // Debugger is active, i.e. there is a debug event listener attached.
bool is_active_; bool is_active_;
...@@ -556,8 +521,6 @@ class Debug { ...@@ -556,8 +521,6 @@ class Debug {
bool break_disabled_; bool break_disabled_;
// Do not break on break points. // Do not break on break points.
bool break_points_active_; bool break_points_active_;
// Nested inside a debug event listener.
bool in_debug_event_listener_;
// Trigger debug break events for all exceptions. // Trigger debug break events for all exceptions.
bool break_on_exception_; bool break_on_exception_;
// Trigger debug break events for uncaught exceptions. // Trigger debug break events for uncaught exceptions.
...@@ -621,6 +584,7 @@ class Debug { ...@@ -621,6 +584,7 @@ class Debug {
friend class LiveEdit; friend class LiveEdit;
friend class SuppressDebug; friend class SuppressDebug;
friend class NoSideEffectScope; friend class NoSideEffectScope;
friend class LegacyDebugDelegate;
friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc
friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc
...@@ -628,6 +592,84 @@ class Debug { ...@@ -628,6 +592,84 @@ class Debug {
DISALLOW_COPY_AND_ASSIGN(Debug); DISALLOW_COPY_AND_ASSIGN(Debug);
}; };
class LegacyDebugDelegate : public v8::debug::DebugDelegate {
public:
explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {}
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
int parent_id) override;
void ScriptCompiled(v8::Local<v8::debug::Script> script,
bool has_compile_error) override;
void BreakProgramRequested(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state,
v8::Local<v8::Value> break_points_hit) override;
void ExceptionThrown(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state,
v8::Local<v8::Value> exception,
v8::Local<v8::Value> promise, bool is_uncaught) override;
bool IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
const v8::debug::Location& start,
const v8::debug::Location& end) override {
return false;
}
protected:
Isolate* isolate_;
private:
void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data);
virtual void ProcessDebugEvent(v8::DebugEvent event,
Handle<JSObject> event_data,
Handle<JSObject> exec_state) = 0;
};
class JavaScriptDebugDelegate : public LegacyDebugDelegate {
public:
JavaScriptDebugDelegate(Isolate* isolate, Handle<JSFunction> listener,
Handle<Object> data);
virtual ~JavaScriptDebugDelegate();
private:
void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data,
Handle<JSObject> exec_state) override;
Handle<JSFunction> listener_;
Handle<Object> data_;
};
class NativeDebugDelegate : public LegacyDebugDelegate {
public:
NativeDebugDelegate(Isolate* isolate, v8::Debug::EventCallback callback,
Handle<Object> data);
virtual ~NativeDebugDelegate();
private:
// Details of the debug event delivered to the debug event listener.
class EventDetails : public v8::Debug::EventDetails {
public:
EventDetails(DebugEvent event, Handle<JSObject> exec_state,
Handle<JSObject> event_data, Handle<Object> callback_data);
virtual DebugEvent GetEvent() const;
virtual v8::Local<v8::Object> GetExecutionState() const;
virtual v8::Local<v8::Object> GetEventData() const;
virtual v8::Local<v8::Context> GetEventContext() const;
virtual v8::Local<v8::Value> GetCallbackData() const;
virtual v8::Debug::ClientData* GetClientData() const { return nullptr; }
virtual v8::Isolate* GetIsolate() const;
private:
DebugEvent event_; // Debug event causing the break.
Handle<JSObject> exec_state_; // Current execution state.
Handle<JSObject> event_data_; // Data associated with the event.
Handle<Object> callback_data_; // User data passed with the callback
// when it was registered.
};
void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data,
Handle<JSObject> exec_state) override;
v8::Debug::EventCallback callback_;
Handle<Object> data_;
};
// This scope is used to load and enter the debug context and create a new // This scope is used to load and enter the debug context and create a new
// break state. Leaving the scope will restore the previous state. // break state. Leaving the scope will restore the previous state.
...@@ -661,21 +703,16 @@ class DebugScope BASE_EMBEDDED { ...@@ -661,21 +703,16 @@ class DebugScope BASE_EMBEDDED {
class DisableBreak BASE_EMBEDDED { class DisableBreak BASE_EMBEDDED {
public: public:
explicit DisableBreak(Debug* debug) explicit DisableBreak(Debug* debug)
: debug_(debug), : debug_(debug), previous_break_disabled_(debug->break_disabled_) {
previous_break_disabled_(debug->break_disabled_),
previous_in_debug_event_listener_(debug->in_debug_event_listener_) {
debug_->break_disabled_ = true; debug_->break_disabled_ = true;
debug_->in_debug_event_listener_ = true;
} }
~DisableBreak() { ~DisableBreak() {
debug_->break_disabled_ = previous_break_disabled_; debug_->break_disabled_ = previous_break_disabled_;
debug_->in_debug_event_listener_ = previous_in_debug_event_listener_;
} }
private: private:
Debug* debug_; Debug* debug_;
bool previous_break_disabled_; bool previous_break_disabled_;
bool previous_in_debug_event_listener_;
DISALLOW_COPY_AND_ASSIGN(DisableBreak); DISALLOW_COPY_AND_ASSIGN(DisableBreak);
}; };
......
...@@ -628,7 +628,9 @@ void V8Debugger::BreakProgramRequested(v8::Local<v8::Context> pausedContext, ...@@ -628,7 +628,9 @@ void V8Debugger::BreakProgramRequested(v8::Local<v8::Context> pausedContext,
void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext, void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
v8::Local<v8::Object> execState, v8::Local<v8::Object> execState,
v8::Local<v8::Value> exception, v8::Local<v8::Value> exception,
bool isPromiseRejection, bool isUncaught) { v8::Local<v8::Value> promise,
bool isUncaught) {
bool isPromiseRejection = promise->IsPromise();
handleProgramBreak(pausedContext, execState, exception, handleProgramBreak(pausedContext, execState, exception,
v8::Local<v8::Array>(), isPromiseRejection, isUncaught); v8::Local<v8::Array>(), isPromiseRejection, isUncaught);
} }
......
...@@ -140,7 +140,7 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -140,7 +140,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
void ExceptionThrown(v8::Local<v8::Context> paused_context, void ExceptionThrown(v8::Local<v8::Context> paused_context,
v8::Local<v8::Object> exec_state, v8::Local<v8::Object> exec_state,
v8::Local<v8::Value> exception, v8::Local<v8::Value> exception,
bool is_promise_rejection, bool is_uncaught) override; v8::Local<v8::Value> promise, bool is_uncaught) override;
bool IsFunctionBlackboxed(v8::Local<v8::debug::Script> script, bool IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
const v8::debug::Location& start, const v8::debug::Location& start,
const v8::debug::Location& end) override; const v8::debug::Location& end) override;
......
...@@ -79,8 +79,13 @@ RUNTIME_FUNCTION(Runtime_SetDebugEventListener) { ...@@ -79,8 +79,13 @@ RUNTIME_FUNCTION(Runtime_SetDebugEventListener) {
CHECK(args[0]->IsJSFunction() || args[0]->IsNullOrUndefined(isolate)); CHECK(args[0]->IsJSFunction() || args[0]->IsNullOrUndefined(isolate));
CONVERT_ARG_HANDLE_CHECKED(Object, callback, 0); CONVERT_ARG_HANDLE_CHECKED(Object, callback, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, data, 1); CONVERT_ARG_HANDLE_CHECKED(Object, data, 1);
isolate->debug()->SetEventListener(callback, data); if (callback->IsJSFunction()) {
JavaScriptDebugDelegate* delegate = new JavaScriptDebugDelegate(
isolate, Handle<JSFunction>::cast(callback), data);
isolate->debug()->SetDebugDelegate(delegate, true);
} else {
isolate->debug()->SetDebugDelegate(nullptr, false);
}
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
...@@ -1853,7 +1858,7 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionPromiseCreated) { ...@@ -1853,7 +1858,7 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionPromiseCreated) {
JSObject::SetProperty(promise, async_stack_id_symbol, JSObject::SetProperty(promise, async_stack_id_symbol,
handle(Smi::FromInt(id), isolate), STRICT) handle(Smi::FromInt(id), isolate), STRICT)
.Assert(); .Assert();
isolate->debug()->OnAsyncTaskEvent(debug::kDebugEnqueueAsyncFunction, id); isolate->debug()->OnAsyncTaskEvent(debug::kDebugEnqueueAsyncFunction, id, 0);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
...@@ -1876,7 +1881,7 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncEventEnqueueRecurring) { ...@@ -1876,7 +1881,7 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncEventEnqueueRecurring) {
isolate->debug()->OnAsyncTaskEvent( isolate->debug()->OnAsyncTaskEvent(
status == v8::Promise::kFulfilled ? debug::kDebugEnqueuePromiseResolve status == v8::Promise::kFulfilled ? debug::kDebugEnqueuePromiseResolve
: debug::kDebugEnqueuePromiseReject, : debug::kDebugEnqueuePromiseReject,
isolate->debug()->NextAsyncTaskId(promise)); isolate->debug()->NextAsyncTaskId(promise), 0);
} }
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
......
...@@ -45,7 +45,7 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) { ...@@ -45,7 +45,7 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
rejected_promise = isolate->GetPromiseOnStackOnThrow(); rejected_promise = isolate->GetPromiseOnStackOnThrow();
isolate->debug()->OnAsyncTaskEvent( isolate->debug()->OnAsyncTaskEvent(
debug::kDebugEnqueuePromiseReject, debug::kDebugEnqueuePromiseReject,
isolate->debug()->NextAsyncTaskId(promise)); isolate->debug()->NextAsyncTaskId(promise), 0);
} }
PromiseRejectEvent(isolate, promise, rejected_promise, value, true); PromiseRejectEvent(isolate, promise, rejected_promise, value, true);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
......
...@@ -3756,43 +3756,6 @@ TEST(TryFinallyOriginalMessage) { ...@@ -3756,43 +3756,6 @@ TEST(TryFinallyOriginalMessage) {
} }
TEST(EvalJSInDebugEventListenerOnNativeReThrownException) {
DebugLocalContext env;
v8::HandleScope scope(env->GetIsolate());
env.ExposeDebug();
// Create functions for testing break on exception.
v8::Local<v8::Function> noThrowJS = CompileFunction(
&env, "function noThrowJS(){var a=[1]; a.push(2); return a.length;}",
"noThrowJS");
debug_event_listener_callback = noThrowJS;
debug_event_listener_callback_result = 2;
env->GetIsolate()->AddMessageListener(MessageCallbackCount);
v8::Debug::SetDebugEventListener(env->GetIsolate(), DebugEventCounter);
// Break on uncaught exception
ChangeBreakOnException(false, true);
DebugEventCounterClear();
MessageCallbackCountClear();
// ReThrow native error
{
v8::TryCatch tryCatch(env->GetIsolate());
env->GetIsolate()->ThrowException(
v8::Exception::TypeError(v8_str(env->GetIsolate(), "Type error")));
CHECK(tryCatch.HasCaught());
tryCatch.ReThrow();
}
CHECK_EQ(1, exception_hit_count);
CHECK_EQ(1, uncaught_exception_hit_count);
CHECK_EQ(0, message_callback_count); // FIXME: Should it be 1 ?
CHECK(!debug_event_listener_callback.IsEmpty());
debug_event_listener_callback.Clear();
}
// Test break on exception from compiler errors. When compiling using // Test break on exception from compiler errors. When compiling using
// v8::Script::Compile there is no JavaScript stack whereas when compiling using // v8::Script::Compile there is no JavaScript stack whereas when compiling using
// eval there are JavaScript frames. // eval there are JavaScript frames.
...@@ -3822,16 +3785,18 @@ TEST(BreakOnCompileException) { ...@@ -3822,16 +3785,18 @@ TEST(BreakOnCompileException) {
// Throws SyntaxError: Unexpected end of input // Throws SyntaxError: Unexpected end of input
CHECK( CHECK(
v8::Script::Compile(context, v8_str(env->GetIsolate(), "+++")).IsEmpty()); v8::Script::Compile(context, v8_str(env->GetIsolate(), "+++")).IsEmpty());
CHECK_EQ(1, exception_hit_count); // Exceptions with no stack are skipped.
CHECK_EQ(1, uncaught_exception_hit_count); CHECK_EQ(0, exception_hit_count);
CHECK_EQ(0, uncaught_exception_hit_count);
CHECK_EQ(1, message_callback_count); CHECK_EQ(1, message_callback_count);
CHECK_EQ(0, last_js_stack_height); // No JavaScript stack. CHECK_EQ(0, last_js_stack_height); // No JavaScript stack.
// Throws SyntaxError: Unexpected identifier // Throws SyntaxError: Unexpected identifier
CHECK( CHECK(
v8::Script::Compile(context, v8_str(env->GetIsolate(), "x x")).IsEmpty()); v8::Script::Compile(context, v8_str(env->GetIsolate(), "x x")).IsEmpty());
CHECK_EQ(2, exception_hit_count); // Exceptions with no stack are skipped.
CHECK_EQ(2, uncaught_exception_hit_count); CHECK_EQ(0, exception_hit_count);
CHECK_EQ(0, uncaught_exception_hit_count);
CHECK_EQ(2, message_callback_count); CHECK_EQ(2, message_callback_count);
CHECK_EQ(0, last_js_stack_height); // No JavaScript stack. CHECK_EQ(0, last_js_stack_height); // No JavaScript stack.
...@@ -3840,8 +3805,8 @@ TEST(BreakOnCompileException) { ...@@ -3840,8 +3805,8 @@ TEST(BreakOnCompileException) {
.ToLocalChecked() .ToLocalChecked()
->Run(context) ->Run(context)
.IsEmpty()); .IsEmpty());
CHECK_EQ(3, exception_hit_count); CHECK_EQ(1, exception_hit_count);
CHECK_EQ(3, uncaught_exception_hit_count); CHECK_EQ(1, uncaught_exception_hit_count);
CHECK_EQ(3, message_callback_count); CHECK_EQ(3, message_callback_count);
CHECK_EQ(1, last_js_stack_height); CHECK_EQ(1, last_js_stack_height);
...@@ -3850,8 +3815,8 @@ TEST(BreakOnCompileException) { ...@@ -3850,8 +3815,8 @@ TEST(BreakOnCompileException) {
.ToLocalChecked() .ToLocalChecked()
->Run(context) ->Run(context)
.IsEmpty()); .IsEmpty());
CHECK_EQ(4, exception_hit_count); CHECK_EQ(2, exception_hit_count);
CHECK_EQ(4, uncaught_exception_hit_count); CHECK_EQ(2, uncaught_exception_hit_count);
CHECK_EQ(4, message_callback_count); CHECK_EQ(4, message_callback_count);
CHECK_EQ(1, last_js_stack_height); CHECK_EQ(1, last_js_stack_height);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment