Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Unified Diff: src/debug/debug.cc

Issue 2682593003: [debugger] implement legacy debug event listeners via debug delegate. (Closed)
Patch Set: addressed comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/debug/debug.h ('k') | src/debug/debug-interface.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/debug/debug.cc
diff --git a/src/debug/debug.cc b/src/debug/debug.cc
index 45931b1819fbb21cd8773f429cb488d1bba40e33..84a16754e1d8c453dd5930addcefbb15a43df9c2 100644
--- a/src/debug/debug.cc
+++ b/src/debug/debug.cc
@@ -38,15 +38,12 @@ namespace internal {
Debug::Debug(Isolate* isolate)
: debug_context_(Handle<Context>()),
- event_listener_(Handle<Object>()),
- event_listener_data_(Handle<Object>()),
is_active_(false),
hook_on_function_call_(false),
is_suppressed_(false),
live_edit_enabled_(true), // TODO(yangguo): set to false by default.
break_disabled_(false),
break_points_active_(true),
- in_debug_event_listener_(false),
break_on_exception_(false),
break_on_uncaught_exception_(false),
side_effect_check_failed_(false),
@@ -473,6 +470,7 @@ bool Debug::Load() {
void Debug::Unload() {
ClearAllBreakPoints();
ClearStepping();
+ RemoveDebugDelegate();
// Return debugger is not loaded.
if (!is_loaded()) return;
@@ -497,6 +495,7 @@ void Debug::Break(JavaScriptFrame* frame) {
// Postpone interrupt during breakpoint processing.
PostponeInterruptsScope postpone(isolate_);
+ DisableBreak no_recursive_break(this);
// Return if we fail to retrieve debug info.
Handle<JSFunction> function(frame->function());
@@ -1667,11 +1666,11 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
return CallFunction("MakeCompileEvent", arraysize(argv), argv);
}
-MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<Smi> type,
- Handle<Smi> id) {
- DCHECK(id->IsNumber());
+MaybeHandle<Object> Debug::MakeAsyncTaskEvent(
+ v8::debug::PromiseDebugActionType type, int id) {
// 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);
}
@@ -1764,6 +1763,9 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
// Check whether the promise reject is considered an uncaught exception.
uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise);
}
+
+ if (!debug_delegate_) return;
+
// Bail out if exception breaks are not active
if (uncaught) {
// Uncaught exceptions are reported by either flags.
@@ -1773,7 +1775,6 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
if (!break_on_exception_) return;
}
- bool empty_js_stack = false;
{
JavaScriptFrameIterator it(isolate_);
// 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) {
IsExceptionBlackboxed(uncaught))) {
return;
}
- empty_js_stack = it.done();
+ if (it.done()) return; // Do not trigger an event with an empty stack.
}
DebugScope debug_scope(this);
if (debug_scope.failed()) return;
+ HandleScope scope(isolate_);
+ PostponeInterruptsScope postpone(isolate_);
+ DisableBreak no_recursive_break(this);
- if (debug_delegate_ && !empty_js_stack) {
- 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;
-
- 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;
+ // Create the execution state.
+ Handle<Object> exec_state;
// Bail out and don't call debugger if exception.
- if (!MakeExceptionEvent(
- exception, uncaught, promise).ToHandle(&event_data)) {
- return;
- }
+ if (!MakeExecutionState().ToHandle(&exec_state)) return;
- // Process debug event.
- ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data));
- // Return to continue execution from where the exception was thrown.
+ debug_delegate_->ExceptionThrown(
+ GetDebugEventContext(isolate_),
+ 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) {
@@ -1825,32 +1812,20 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit) {
PrintBreakLocation();
#endif // DEBUG
- if (debug_delegate_) {
- 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;
- }
-
+ if (!debug_delegate_) return;
HandleScope scope(isolate_);
- // Create the event data object.
- Handle<Object> event_data;
+ PostponeInterruptsScope no_interrupts(isolate_);
+ DisableBreak no_recursive_break(this);
+
+ // Create the execution state.
+ Handle<Object> exec_state;
// 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.
- ProcessDebugEvent(v8::Break, Handle<JSObject>::cast(event_data));
+ debug_delegate_->BreakProgramRequested(
+ 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) {
reinterpret_cast<CollectedCallbackData*>(info.GetParameter()));
if (!data->debug->is_active()) return;
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) {
@@ -1929,21 +1904,21 @@ void Debug::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
int id = GetReferenceAsyncTaskId(isolate_, promise);
switch (type) {
case PromiseHookType::kInit:
- debug_delegate_->PromiseEventOccurred(
- debug::kDebugPromiseCreated, id,
- parent->IsJSPromise() ? GetReferenceAsyncTaskId(
- isolate_, Handle<JSPromise>::cast(parent))
- : 0);
+ OnAsyncTaskEvent(debug::kDebugPromiseCreated, id,
+ parent->IsJSPromise()
+ ? GetReferenceAsyncTaskId(
+ isolate_, Handle<JSPromise>::cast(parent))
+ : 0);
return;
case PromiseHookType::kResolve:
// We can't use this hook because it's called before promise object will
// get resolved status.
return;
case PromiseHookType::kBefore:
- OnAsyncTaskEvent(debug::kDebugWillHandle, id);
+ OnAsyncTaskEvent(debug::kDebugWillHandle, id, 0);
return;
case PromiseHookType::kAfter:
- OnAsyncTaskEvent(debug::kDebugDidHandle, id);
+ OnAsyncTaskEvent(debug::kDebugDidHandle, id, 0);
return;
}
}
@@ -1988,7 +1963,10 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
if (!shared->computed_debug_is_blackboxed()) {
bool is_blackboxed = false;
if (shared->script()->IsScript()) {
+ SuppressDebug while_processing(this);
HandleScope handle_scope(isolate_);
+ PostponeInterruptsScope no_interrupts(isolate_);
+ DisableBreak no_recursive_break(this);
Handle<Script> script(Script::cast(shared->script()));
if (script->type() == i::Script::TYPE_NORMAL) {
debug::Location start =
@@ -2004,70 +1982,17 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
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 (debug_delegate_) {
- debug_delegate_->PromiseEventOccurred(type, id, 0);
- if (!non_inspector_listener_exists()) return;
- }
-
- HandleScope scope(isolate_);
- DebugScope debug_scope(this);
+ if (!debug_delegate_) return;
+ SuppressDebug while_processing(this);
+ DebugScope debug_scope(isolate_->debug());
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_);
-
- // Create the execution state.
- Handle<Object> exec_state;
- // 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;
+ PostponeInterruptsScope no_interrupts(isolate_);
+ DisableBreak no_recursive_break(this);
+ debug_delegate_->PromiseEventOccurred(type, id, parent_id);
}
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) {
return;
}
+ if (!debug_delegate_) return;
SuppressDebug while_processing(this);
DebugScope debug_scope(this);
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_);
- // Create the compile state object.
- Handle<Object> event_data;
- // Bail out and don't call debugger if exception.
- if (!MakeCompileEvent(script, event).ToHandle(&event_data)) return;
-
- // Process debug event.
- ProcessDebugEvent(event, Handle<JSObject>::cast(event_data));
+ PostponeInterruptsScope postpone(isolate_);
+ DisableBreak no_recursive_break(this);
+ debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script),
+ event != v8::AfterCompile);
}
@@ -2105,27 +2021,6 @@ Handle<Context> Debug::GetDebugContext() {
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() {
StackTraceFrameIterator it(isolate_);
if (break_frame_id() != StackFrame::NO_ID) {
@@ -2147,13 +2042,25 @@ int Debug::CurrentFrameCount() {
return counter;
}
-void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) {
+void Debug::SetDebugDelegate(debug::DebugDelegate* delegate,
+ bool pass_ownership) {
+ RemoveDebugDelegate();
debug_delegate_ = delegate;
+ owns_debug_delegate_ = pass_ownership;
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() {
- bool is_active = !event_listener_.is_null() || debug_delegate_ != nullptr;
+ bool is_active = debug_delegate_ != nullptr;
if (is_active || in_debug_scope()) {
// Note that the debug context could have already been loaded to
// bootstrap test cases.
@@ -2358,59 +2265,158 @@ bool Debug::PerformSideEffectCheckForCallback(Address function) {
return false;
}
-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));
+void LegacyDebugDelegate::PromiseEventOccurred(
+ v8::debug::PromiseDebugActionType type, int id, int parent_id) {
+ Handle<Object> event_data;
+ if (isolate_->debug()->MakeAsyncTaskEvent(type, id).ToHandle(&event_data)) {
+ ProcessDebugEvent(v8::AsyncTaskEvent, Handle<JSObject>::cast(event_data));
+ }
+}
+
+void LegacyDebugDelegate::ScriptCompiled(v8::Local<v8::debug::Script> script,
+ 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,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- Handle<Object> callback_data)
+JavaScriptDebugDelegate::JavaScriptDebugDelegate(Isolate* isolate,
+ Handle<JSFunction> listener,
+ Handle<Object> 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),
exec_state_(exec_state),
event_data_(event_data),
callback_data_(callback_data) {}
-DebugEvent EventDetailsImpl::GetEvent() const {
+DebugEvent NativeDebugDelegate::EventDetails::GetEvent() const {
return event_;
}
-
-v8::Local<v8::Object> EventDetailsImpl::GetExecutionState() const {
+v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetExecutionState()
+ const {
return v8::Utils::ToLocal(exec_state_);
}
-
-v8::Local<v8::Object> EventDetailsImpl::GetEventData() const {
+v8::Local<v8::Object> NativeDebugDelegate::EventDetails::GetEventData() const {
return v8::Utils::ToLocal(event_data_);
}
-
-v8::Local<v8::Context> EventDetailsImpl::GetEventContext() const {
+v8::Local<v8::Context> NativeDebugDelegate::EventDetails::GetEventContext()
+ const {
return GetDebugEventContext(exec_state_->GetIsolate());
}
-
-v8::Local<v8::Value> EventDetailsImpl::GetCallbackData() const {
+v8::Local<v8::Value> NativeDebugDelegate::EventDetails::GetCallbackData()
+ const {
return v8::Utils::ToLocal(callback_data_);
}
-
-v8::Isolate* EventDetailsImpl::GetIsolate() const {
+v8::Isolate* NativeDebugDelegate::EventDetails::GetIsolate() const {
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 v8
« no previous file with comments | « src/debug/debug.h ('k') | src/debug/debug-interface.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698