Index: src/inspector/v8-debugger.cc |
diff --git a/src/inspector/v8-debugger.cc b/src/inspector/v8-debugger.cc |
index f4c3ca5813a05249ba56d1278fde1a9561ddee80..3f0fb900825838c2342631d7bd5ce7d1b34d2ea3 100644 |
--- a/src/inspector/v8-debugger.cc |
+++ b/src/inspector/v8-debugger.cc |
@@ -336,6 +336,7 @@ void V8Debugger::setPauseOnNextStatement(bool pause, int targetContextGroupId) { |
return; |
} |
m_targetContextGroupId = targetContextGroupId; |
+ m_breakRequested = pause; |
if (pause) |
v8::debug::DebugBreak(m_isolate); |
else |
@@ -387,6 +388,20 @@ void V8Debugger::stepOutOfFunction(int targetContextGroupId) { |
continueProgram(); |
} |
+void V8Debugger::scheduleStepIntoAsync( |
+ std::unique_ptr<ScheduleStepIntoAsyncCallback> callback, |
+ int targetContextGroupId) { |
+ DCHECK(isPaused()); |
+ DCHECK(!m_executionState.IsEmpty()); |
+ DCHECK(targetContextGroupId); |
+ if (m_stepIntoAsyncCallback) { |
+ m_stepIntoAsyncCallback->sendFailure(Response::Error( |
+ "Current scheduled step into async was overriden with new one.")); |
+ } |
+ m_targetContextGroupId = targetContextGroupId; |
+ m_stepIntoAsyncCallback = std::move(callback); |
+} |
+ |
Response V8Debugger::setScriptSource( |
const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, |
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails, |
@@ -522,6 +537,12 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, |
return; |
} |
m_targetContextGroupId = 0; |
+ if (m_stepIntoAsyncCallback) { |
+ m_stepIntoAsyncCallback->sendFailure( |
+ Response::Error("No async tasks were scheduled before pause.")); |
+ m_stepIntoAsyncCallback.reset(); |
+ } |
+ m_breakRequested = false; |
V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup( |
m_inspector->contextGroupId(pausedContext)); |
if (!agent || (agent->skipAllPauses() && !m_scheduledOOMBreak)) return; |
@@ -624,78 +645,42 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, |
int id, int parentId, |
bool createdByUser) { |
// 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 = |
+ // from overlapping with other Blink task identifiers. |
+ void* task = reinterpret_cast<void*>(id * 2 + 1); |
+ void* parentTask = |
parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr; |
- handleAsyncTaskStepping(context, type, ptr, parentPtr, createdByUser); |
- if (!m_maxAsyncCallStackDepth) return; |
switch (type) { |
case v8::debug::kDebugPromiseCreated: |
- asyncTaskCreated(ptr, parentPtr); |
+ asyncTaskCreatedForStack(task, parentTask); |
+ if (createdByUser && parentTask) { |
+ v8::Context::Scope contextScope(context); |
+ asyncTaskCandidateForStepping(task); |
+ } |
break; |
case v8::debug::kDebugEnqueueAsyncFunction: |
- asyncTaskScheduled("async function", ptr, true); |
+ asyncTaskScheduledForStack("async function", task, true); |
break; |
case v8::debug::kDebugEnqueuePromiseResolve: |
- asyncTaskScheduled("Promise.resolve", ptr, true); |
+ asyncTaskScheduledForStack("Promise.resolve", task, true); |
break; |
case v8::debug::kDebugEnqueuePromiseReject: |
- asyncTaskScheduled("Promise.reject", ptr, true); |
+ asyncTaskScheduledForStack("Promise.reject", task, true); |
break; |
case v8::debug::kDebugPromiseCollected: |
- asyncTaskCanceled(ptr); |
+ asyncTaskCanceledForStack(task); |
+ asyncTaskCanceledForStepping(task); |
break; |
case v8::debug::kDebugWillHandle: |
- asyncTaskStarted(ptr); |
+ asyncTaskStartedForStack(task); |
+ asyncTaskStartedForStepping(task); |
break; |
case v8::debug::kDebugDidHandle: |
- asyncTaskFinished(ptr); |
+ asyncTaskFinishedForStack(task); |
+ asyncTaskFinishedForStepping(task); |
break; |
} |
} |
-void V8Debugger::handleAsyncTaskStepping(v8::Local<v8::Context> context, |
- v8::debug::PromiseDebugActionType type, |
- void* task, void* parentTask, |
- bool createdByUser) { |
- if (type == v8::debug::kDebugEnqueueAsyncFunction || |
- type == v8::debug::kDebugEnqueuePromiseResolve || |
- type == v8::debug::kDebugEnqueuePromiseReject) { |
- return; |
- } |
- |
- bool isScheduledTask = task == m_taskWithScheduledBreak; |
- if (type == v8::debug::kDebugPromiseCollected) { |
- if (isScheduledTask) m_taskWithScheduledBreak = nullptr; |
- return; |
- } |
- if (type == v8::debug::kDebugPromiseCreated && !parentTask) return; |
- |
- DCHECK(!context.IsEmpty()); |
- int contextGroupId = m_inspector->contextGroupId(context); |
- V8DebuggerAgentImpl* agent = |
- m_inspector->enabledDebuggerAgentForGroup(contextGroupId); |
- if (!agent) return; |
- if (createdByUser && type == v8::debug::kDebugPromiseCreated) { |
- if (agent->shouldBreakInScheduledAsyncTask()) { |
- m_taskWithScheduledBreak = task; |
- v8::debug::ClearStepping(m_isolate); |
- } |
- return; |
- } |
- if (!isScheduledTask) return; |
- if (type == v8::debug::kDebugWillHandle) { |
- agent->schedulePauseOnNextStatement( |
- protocol::Debugger::Paused::ReasonEnum::Other, nullptr); |
- return; |
- } |
- DCHECK(type == v8::debug::kDebugDidHandle); |
- agent->cancelPauseOnNextStatement(); |
- m_taskWithScheduledBreak = nullptr; |
-} |
- |
V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { |
if (!m_currentStacks.size()) return nullptr; |
return m_currentStacks.back().get(); |
@@ -869,11 +854,11 @@ void V8Debugger::registerAsyncTaskIfNeeded(void* task) { |
m_idToTask[id] = task; |
if (static_cast<int>(m_idToTask.size()) > m_maxAsyncCallStacks) { |
void* taskToRemove = m_idToTask.begin()->second; |
- asyncTaskCanceled(taskToRemove); |
+ asyncTaskCanceledForStack(taskToRemove); |
} |
} |
-void V8Debugger::asyncTaskCreated(void* task, void* parentTask) { |
+void V8Debugger::asyncTaskCreatedForStack(void* task, void* parentTask) { |
if (!m_maxAsyncCallStackDepth) return; |
if (parentTask) m_parentTask[task] = parentTask; |
v8::HandleScope scope(m_isolate); |
@@ -891,12 +876,27 @@ void V8Debugger::asyncTaskCreated(void* task, void* parentTask) { |
void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task, |
bool recurring) { |
- if (!m_maxAsyncCallStackDepth) return; |
- asyncTaskScheduled(toString16(taskName), task, recurring); |
+ asyncTaskScheduledForStack(toString16(taskName), task, recurring); |
+ asyncTaskCandidateForStepping(task); |
} |
-void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task, |
- bool recurring) { |
+void V8Debugger::asyncTaskCanceled(void* task) { |
+ asyncTaskCanceledForStack(task); |
+ asyncTaskCanceledForStepping(task); |
+} |
+ |
+void V8Debugger::asyncTaskStarted(void* task) { |
+ asyncTaskStartedForStack(task); |
+ asyncTaskStartedForStepping(task); |
+} |
+ |
+void V8Debugger::asyncTaskFinished(void* task) { |
+ asyncTaskFinishedForStack(task); |
+ asyncTaskFinishedForStepping(task); |
+} |
+ |
+void V8Debugger::asyncTaskScheduledForStack(const String16& taskName, |
+ void* task, bool recurring) { |
if (!m_maxAsyncCallStackDepth) return; |
v8::HandleScope scope(m_isolate); |
std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture( |
@@ -909,7 +909,7 @@ void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task, |
} |
} |
-void V8Debugger::asyncTaskCanceled(void* task) { |
+void V8Debugger::asyncTaskCanceledForStack(void* task) { |
if (!m_maxAsyncCallStackDepth) return; |
m_asyncTaskStacks.erase(task); |
m_recurringTasks.erase(task); |
@@ -921,7 +921,7 @@ void V8Debugger::asyncTaskCanceled(void* task) { |
m_taskToId.erase(it); |
} |
-void V8Debugger::asyncTaskStarted(void* task) { |
+void V8Debugger::asyncTaskStartedForStack(void* task) { |
if (!m_maxAsyncCallStackDepth) return; |
m_currentTasks.push_back(task); |
auto parentIt = m_parentTask.find(task); |
@@ -944,7 +944,7 @@ void V8Debugger::asyncTaskStarted(void* task) { |
m_currentStacks.push_back(std::move(stack)); |
} |
-void V8Debugger::asyncTaskFinished(void* task) { |
+void V8Debugger::asyncTaskFinishedForStack(void* task) { |
if (!m_maxAsyncCallStackDepth) return; |
// We could start instrumenting half way and the stack is empty. |
if (!m_currentStacks.size()) return; |
@@ -954,10 +954,38 @@ void V8Debugger::asyncTaskFinished(void* task) { |
m_currentStacks.pop_back(); |
if (m_recurringTasks.find(task) == m_recurringTasks.end()) { |
- asyncTaskCanceled(task); |
+ asyncTaskCanceledForStack(task); |
} |
} |
+void V8Debugger::asyncTaskCandidateForStepping(void* task) { |
+ if (!m_stepIntoAsyncCallback) return; |
+ DCHECK(m_targetContextGroupId); |
+ if (currentContextGroupId() != m_targetContextGroupId) return; |
+ m_taskWithScheduledBreak = task; |
+ v8::debug::ClearStepping(m_isolate); |
+ m_stepIntoAsyncCallback->sendSuccess(); |
+ m_stepIntoAsyncCallback.reset(); |
+} |
+ |
+void V8Debugger::asyncTaskStartedForStepping(void* task) { |
+ if (m_breakRequested) return; |
+ if (task != m_taskWithScheduledBreak) return; |
+ v8::debug::DebugBreak(m_isolate); |
+} |
+ |
+void V8Debugger::asyncTaskFinishedForStepping(void* task) { |
+ if (task != m_taskWithScheduledBreak) return; |
+ m_taskWithScheduledBreak = nullptr; |
+ if (m_breakRequested) return; |
+ v8::debug::CancelDebugBreak(m_isolate); |
+} |
+ |
+void V8Debugger::asyncTaskCanceledForStepping(void* task) { |
+ if (task != m_taskWithScheduledBreak) return; |
+ m_taskWithScheduledBreak = nullptr; |
+} |
+ |
void V8Debugger::allAsyncTasksCanceled() { |
m_asyncTaskStacks.clear(); |
m_recurringTasks.clear(); |