| Index: src/inspector/v8-debugger.cc
|
| diff --git a/src/inspector/v8-debugger.cc b/src/inspector/v8-debugger.cc
|
| index 3a2fc89f0039869b49a3c5cef4f4b92eab0017a6..ea0a66ea7d7a31ae3ca8e0b044c69da14bae666e 100644
|
| --- a/src/inspector/v8-debugger.cc
|
| +++ b/src/inspector/v8-debugger.cc
|
| @@ -194,6 +194,7 @@ void V8Debugger::disable() {
|
| m_debuggerScript.Reset();
|
| m_debuggerContext.Reset();
|
| allAsyncTasksCanceled();
|
| + m_tasksWithScheduledBreak.clear();
|
| m_wasmTranslation.Clear();
|
| v8::debug::SetDebugDelegate(m_isolate, nullptr);
|
| v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr);
|
| @@ -356,31 +357,55 @@ void V8Debugger::breakProgram() {
|
| v8::debug::Call(debuggerContext(), breakFunction).ToLocalChecked();
|
| }
|
|
|
| -void V8Debugger::continueProgram() {
|
| - if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
|
| +void V8Debugger::continueProgramImpl() {
|
| + if (!isPaused()) return;
|
| + m_inspector->client()->quitMessageLoopOnPause();
|
| m_pausedContext.Clear();
|
| m_executionState.Clear();
|
| }
|
|
|
| +void V8Debugger::continueProgram() {
|
| + if (!isPaused()) return;
|
| + v8::debug::ClearStepping(m_isolate);
|
| + continueProgramImpl();
|
| +}
|
| +
|
| void V8Debugger::stepIntoStatement() {
|
| DCHECK(isPaused());
|
| DCHECK(!m_executionState.IsEmpty());
|
| v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
|
| - continueProgram();
|
| + continueProgramImpl();
|
| }
|
|
|
| void V8Debugger::stepOverStatement() {
|
| DCHECK(isPaused());
|
| DCHECK(!m_executionState.IsEmpty());
|
| v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
|
| - continueProgram();
|
| + continueProgramImpl();
|
| }
|
|
|
| void V8Debugger::stepOutOfFunction() {
|
| DCHECK(isPaused());
|
| DCHECK(!m_executionState.IsEmpty());
|
| v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
|
| - continueProgram();
|
| + continueProgramImpl();
|
| +}
|
| +
|
| +Response V8Debugger::stepIntoScheduledCallback() {
|
| + DCHECK(isPaused());
|
| + DCHECK(!m_executionState.IsEmpty());
|
| +
|
| + if (!stepIntoScheduledCallbackAvailable()) {
|
| + return Response::Error("No scheduled callback for stepInto available.");
|
| + }
|
| + m_tasksWithScheduledBreak.insert(m_createdPromiseTask);
|
| + v8::debug::ClearStepping(m_isolate);
|
| + continueProgramImpl();
|
| + return Response::OK();
|
| +}
|
| +
|
| +bool V8Debugger::stepIntoScheduledCallbackAvailable() {
|
| + return m_createdPromiseTask;
|
| }
|
|
|
| Response V8Debugger::setScriptSource(
|
| @@ -625,17 +650,25 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
|
| end);
|
| }
|
|
|
| -void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
| - int id, int parentId) {
|
| - if (!m_maxAsyncCallStackDepth) return;
|
| +void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context,
|
| + v8::debug::PromiseDebugActionType type,
|
| + int id, int parentId, bool breakable) {
|
| // Async task events from Promises are given misaligned pointers to prevent
|
| // from overlapping with other Blink task identifiers. There is a single
|
| // namespace of such ids, managed by src/js/promise.js.
|
| void* ptr = reinterpret_cast<void*>(id * 2 + 1);
|
| + void* parentPtr =
|
| + parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr;
|
| + if ((breakable && type == v8::debug::kDebugPromiseCreated) ||
|
| + type == v8::debug::kDebugWillHandle ||
|
| + type == v8::debug::kDebugDidHandle ||
|
| + type == v8::debug::kDebugPromiseCollected) {
|
| + steppingOnPromiseEvent(context, type, ptr, parentPtr);
|
| + }
|
| + if (!m_maxAsyncCallStackDepth) return;
|
| switch (type) {
|
| case v8::debug::kDebugPromiseCreated:
|
| - asyncTaskCreated(
|
| - ptr, parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr);
|
| + asyncTaskCreated(ptr, parentPtr);
|
| break;
|
| case v8::debug::kDebugEnqueueAsyncFunction:
|
| asyncTaskScheduled("async function", ptr, true);
|
| @@ -658,6 +691,37 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
| }
|
| }
|
|
|
| +void V8Debugger::steppingOnPromiseEvent(v8::Local<v8::Context> context,
|
| + v8::debug::PromiseDebugActionType type,
|
| + void* task, void* parentPtr) {
|
| + if (type == v8::debug::kDebugPromiseCollected) {
|
| + m_tasksWithScheduledBreak.erase(task);
|
| + return;
|
| + }
|
| + DCHECK(!context.IsEmpty());
|
| + v8::Context::Scope contextScope(context);
|
| + V8DebuggerAgentImpl* agent =
|
| + m_inspector->enabledDebuggerAgentForGroup(currentContextGroupId());
|
| + if (!agent) return;
|
| + if (type == v8::debug::kDebugPromiseCreated) {
|
| + if (!parentPtr) return;
|
| + m_createdPromiseTask = task;
|
| + agent->breakProgramIfSteppingInto(
|
| + protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
|
| + m_createdPromiseTask = nullptr;
|
| + return;
|
| + }
|
| + if (m_tasksWithScheduledBreak.find(task) == m_tasksWithScheduledBreak.end()) {
|
| + return;
|
| + }
|
| + if (type == v8::debug::kDebugWillHandle) {
|
| + agent->schedulePauseOnNextStatement(
|
| + protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
|
| + } else {
|
| + agent->cancelPauseOnNextStatement();
|
| + }
|
| +}
|
| +
|
| V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
|
| if (!m_currentStacks.size()) return nullptr;
|
| return m_currentStacks.back().get();
|
| @@ -802,11 +866,7 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
|
|
|
| std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
|
| v8::Local<v8::StackTrace> stackTrace) {
|
| - int contextGroupId =
|
| - m_isolate->InContext()
|
| - ? m_inspector->contextGroupId(m_isolate->GetCurrentContext())
|
| - : 0;
|
| - return V8StackTraceImpl::create(this, contextGroupId, stackTrace,
|
| + return V8StackTraceImpl::create(this, currentContextGroupId(), stackTrace,
|
| V8StackTraceImpl::maxCallStackSizeToCapture);
|
| }
|
|
|
| @@ -843,7 +903,7 @@ void V8Debugger::asyncTaskCreated(void* task, void* parentTask) {
|
| if (!m_maxAsyncCallStackDepth) return;
|
| if (parentTask) m_parentTask[task] = parentTask;
|
| v8::HandleScope scope(m_isolate);
|
| - // We don't need to pass context group id here because we gets this callback
|
| + // We don't need to pass context group id here because we get this callback
|
| // from V8 for promise events only.
|
| // Passing one as maxStackSize forces no async chain for the new stack and
|
| // allows us to not grow exponentially.
|
| @@ -865,13 +925,9 @@ void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task,
|
| bool recurring) {
|
| if (!m_maxAsyncCallStackDepth) return;
|
| v8::HandleScope scope(m_isolate);
|
| - int contextGroupId =
|
| - m_isolate->InContext()
|
| - ? m_inspector->contextGroupId(m_isolate->GetCurrentContext())
|
| - : 0;
|
| std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
|
| - this, contextGroupId, V8StackTraceImpl::maxCallStackSizeToCapture,
|
| - taskName);
|
| + this, currentContextGroupId(),
|
| + V8StackTraceImpl::maxCallStackSizeToCapture, taskName);
|
| if (chain) {
|
| m_asyncTaskStacks[task] = std::move(chain);
|
| if (recurring) m_recurringTasks.insert(task);
|
| @@ -954,8 +1010,7 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
|
| if (!m_isolate->InContext()) return nullptr;
|
|
|
| v8::HandleScope handles(m_isolate);
|
| - int contextGroupId =
|
| - m_inspector->contextGroupId(m_isolate->GetCurrentContext());
|
| + int contextGroupId = currentContextGroupId();
|
| if (!contextGroupId) return nullptr;
|
|
|
| size_t stackSize =
|
| @@ -966,4 +1021,9 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
|
| return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
|
| }
|
|
|
| +int V8Debugger::currentContextGroupId() {
|
| + if (!m_isolate->InContext()) return 0;
|
| + return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
|
| +}
|
| +
|
| } // namespace v8_inspector
|
|
|