Commit 47276d3d authored by kozyatinskiy's avatar kozyatinskiy Committed by Commit bot

[inspector] introduced Debugger.scheduleStepIntoAsync

This method could be called on pause and will do stepInto next scheduled callback if any will happen until next break.
First implementation support only callbacks chained by Promise.prototype.then.

BUG=chromium:432469
R=yangguo@chromium.org,dgozman@chromium.org

Review-Url: https://codereview.chromium.org/2723273002
Cr-Commit-Position: refs/heads/master@{#43616}
parent e96b5ed4
...@@ -9099,17 +9099,18 @@ void debug::PrepareStep(Isolate* v8_isolate, StepAction action) { ...@@ -9099,17 +9099,18 @@ void debug::PrepareStep(Isolate* v8_isolate, StepAction action) {
isolate->debug()->PrepareStep(static_cast<i::StepAction>(action)); isolate->debug()->PrepareStep(static_cast<i::StepAction>(action));
} }
bool debug::HasNonBlackboxedFrameOnStack(Isolate* v8_isolate) { void debug::ClearStepping(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8(isolate); ENTER_V8(isolate);
i::HandleScope scope(isolate); CHECK(isolate->debug()->CheckExecutionState());
for (i::StackTraceFrameIterator it(isolate); !it.done(); it.Advance()) { // Clear all current stepping setup.
if (!it.is_javascript()) continue; isolate->debug()->ClearStepping();
if (!isolate->debug()->IsFrameBlackboxed(it.javascript_frame())) { }
return true;
} bool debug::AllFramesOnStackAreBlackboxed(Isolate* v8_isolate) {
} i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
return false; ENTER_V8(isolate);
return isolate->debug()->AllFramesOnStackAreBlackboxed();
} }
v8::Isolate* debug::Script::GetIsolate() const { v8::Isolate* debug::Script::GetIsolate() const {
......
...@@ -102,8 +102,9 @@ enum StepAction { ...@@ -102,8 +102,9 @@ enum StepAction {
}; };
void PrepareStep(Isolate* isolate, StepAction action); void PrepareStep(Isolate* isolate, StepAction action);
void ClearStepping(Isolate* isolate);
bool HasNonBlackboxedFrameOnStack(Isolate* isolate); bool AllFramesOnStackAreBlackboxed(Isolate* isolate);
/** /**
* Out-of-memory callback function. * Out-of-memory callback function.
...@@ -164,8 +165,9 @@ MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate, ...@@ -164,8 +165,9 @@ MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate,
class DebugDelegate { class DebugDelegate {
public: public:
virtual ~DebugDelegate() {} virtual ~DebugDelegate() {}
virtual void PromiseEventOccurred(debug::PromiseDebugActionType type, int id, virtual void PromiseEventOccurred(v8::Local<v8::Context> context,
int parent_id) {} debug::PromiseDebugActionType type, int id,
int parent_id, bool created_by_user) {}
virtual void ScriptCompiled(v8::Local<Script> script, virtual void ScriptCompiled(v8::Local<Script> script,
bool has_compile_error) {} bool has_compile_error) {}
virtual void BreakProgramRequested(v8::Local<v8::Context> paused_context, virtual void BreakProgramRequested(v8::Local<v8::Context> paused_context,
......
...@@ -1729,12 +1729,7 @@ bool Debug::IsExceptionBlackboxed(bool uncaught) { ...@@ -1729,12 +1729,7 @@ bool Debug::IsExceptionBlackboxed(bool uncaught) {
// caught exception if top frame is blackboxed. // caught exception if top frame is blackboxed.
bool is_top_frame_blackboxed = IsFrameBlackboxed(it.frame()); bool is_top_frame_blackboxed = IsFrameBlackboxed(it.frame());
if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed; if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed;
it.Advance(); return AllFramesOnStackAreBlackboxed();
while (!it.done()) {
if (!IsFrameBlackboxed(it.frame())) return false;
it.Advance();
}
return true;
} }
bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) { bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) {
...@@ -1989,6 +1984,15 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) { ...@@ -1989,6 +1984,15 @@ bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) {
return shared->debug_is_blackboxed(); return shared->debug_is_blackboxed();
} }
bool Debug::AllFramesOnStackAreBlackboxed() {
HandleScope scope(isolate_);
for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) {
if (!it.is_javascript()) continue;
if (!IsFrameBlackboxed(it.javascript_frame())) return false;
}
return true;
}
void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id, void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
int parent_id) { int parent_id) {
if (in_debug_scope() || ignore_events()) return; if (in_debug_scope() || ignore_events()) return;
...@@ -1999,7 +2003,19 @@ void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id, ...@@ -1999,7 +2003,19 @@ void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
HandleScope scope(isolate_); HandleScope scope(isolate_);
PostponeInterruptsScope no_interrupts(isolate_); PostponeInterruptsScope no_interrupts(isolate_);
DisableBreak no_recursive_break(this); DisableBreak no_recursive_break(this);
debug_delegate_->PromiseEventOccurred(type, id, parent_id); bool created_by_user = false;
if (type == debug::kDebugPromiseCreated) {
JavaScriptFrameIterator it(isolate_);
// We need to skip top frame which contains instrumentation.
it.Advance();
created_by_user =
!it.done() &&
it.frame()->function()->shared()->IsSubjectToDebugging() &&
!IsFrameBlackboxed(it.frame());
}
debug_delegate_->PromiseEventOccurred(
Utils::ToLocal(debug_scope.GetContext()), type, id, parent_id,
created_by_user);
} }
void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) { void Debug::ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script) {
...@@ -2279,7 +2295,8 @@ bool Debug::PerformSideEffectCheckForCallback(Address function) { ...@@ -2279,7 +2295,8 @@ bool Debug::PerformSideEffectCheckForCallback(Address function) {
} }
void LegacyDebugDelegate::PromiseEventOccurred( void LegacyDebugDelegate::PromiseEventOccurred(
v8::debug::PromiseDebugActionType type, int id, int parent_id) { v8::Local<v8::Context> context, v8::debug::PromiseDebugActionType type,
int id, int parent_id, bool created_by_user) {
Handle<Object> event_data; Handle<Object> event_data;
if (isolate_->debug()->MakeAsyncTaskEvent(type, id).ToHandle(&event_data)) { if (isolate_->debug()->MakeAsyncTaskEvent(type, id).ToHandle(&event_data)) {
ProcessDebugEvent(v8::AsyncTaskEvent, Handle<JSObject>::cast(event_data)); ProcessDebugEvent(v8::AsyncTaskEvent, Handle<JSObject>::cast(event_data));
......
...@@ -351,7 +351,7 @@ class Debug { ...@@ -351,7 +351,7 @@ class Debug {
// Support for LiveEdit // Support for LiveEdit
void ScheduleFrameRestart(StackFrame* frame); void ScheduleFrameRestart(StackFrame* frame);
bool IsFrameBlackboxed(JavaScriptFrame* frame); bool AllFramesOnStackAreBlackboxed();
// Threading support. // Threading support.
char* ArchiveDebug(char* to); char* ArchiveDebug(char* to);
...@@ -487,6 +487,8 @@ class Debug { ...@@ -487,6 +487,8 @@ class Debug {
// Clear all one-shot instrumentations, but restore break points. // Clear all one-shot instrumentations, but restore break points.
void ClearOneShot(); void ClearOneShot();
bool IsFrameBlackboxed(JavaScriptFrame* frame);
void ActivateStepOut(StackFrame* frame); void ActivateStepOut(StackFrame* frame);
void RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info); void RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info);
MaybeHandle<FixedArray> CheckBreakPoints(Handle<DebugInfo> debug_info, MaybeHandle<FixedArray> CheckBreakPoints(Handle<DebugInfo> debug_info,
...@@ -599,8 +601,9 @@ class Debug { ...@@ -599,8 +601,9 @@ class Debug {
class LegacyDebugDelegate : public v8::debug::DebugDelegate { class LegacyDebugDelegate : public v8::debug::DebugDelegate {
public: public:
explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {} explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {}
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, void PromiseEventOccurred(v8::Local<v8::Context> context,
int parent_id) override; v8::debug::PromiseDebugActionType type, int id,
int parent_id, bool created_by_user) override;
void ScriptCompiled(v8::Local<v8::debug::Script> script, void ScriptCompiled(v8::Local<v8::debug::Script> script,
bool has_compile_error) override; bool has_compile_error) override;
void BreakProgramRequested(v8::Local<v8::Context> paused_context, void BreakProgramRequested(v8::Local<v8::Context> paused_context,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
}, },
{ {
"domain": "Debugger", "domain": "Debugger",
"async": [ "scheduleStepIntoAsync" ],
"exported": ["SearchMatch", "paused.reason"] "exported": ["SearchMatch", "paused.reason"]
}, },
{ {
......
...@@ -566,6 +566,11 @@ ...@@ -566,6 +566,11 @@
"name": "pause", "name": "pause",
"description": "Stops on the next JavaScript statement." "description": "Stops on the next JavaScript statement."
}, },
{
"name": "scheduleStepIntoAsync",
"description": "Steps into next scheduled async task if any is scheduled before next pause. Returns success when async task is actually scheduled, returns error if no task were scheduled or another scheduleStepIntoAsync was called.",
"experimental": true
},
{ {
"name": "resume", "name": "resume",
"description": "Resumes JavaScript execution." "description": "Resumes JavaScript execution."
......
...@@ -736,6 +736,28 @@ Response V8DebuggerAgentImpl::stepOut() { ...@@ -736,6 +736,28 @@ Response V8DebuggerAgentImpl::stepOut() {
return Response::OK(); return Response::OK();
} }
void V8DebuggerAgentImpl::scheduleStepIntoAsync(
std::unique_ptr<ScheduleStepIntoAsyncCallback> callback) {
if (!isPaused()) {
callback->sendFailure(Response::Error(kDebuggerNotPaused));
return;
}
if (m_stepIntoAsyncCallback) {
m_stepIntoAsyncCallback->sendFailure(Response::Error(
"Current scheduled step into async was overriden with new one."));
}
m_stepIntoAsyncCallback = std::move(callback);
}
bool V8DebuggerAgentImpl::shouldBreakInScheduledAsyncTask() {
if (!m_stepIntoAsyncCallback) return false;
m_stepIntoAsyncCallback->sendSuccess();
m_stepIntoAsyncCallback.reset();
m_scheduledDebuggerStep = NoStep;
v8::debug::ClearStepping(m_isolate);
return true;
}
Response V8DebuggerAgentImpl::setPauseOnExceptions( Response V8DebuggerAgentImpl::setPauseOnExceptions(
const String16& stringPauseState) { const String16& stringPauseState) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled); if (!enabled()) return Response::Error(kDebuggerNotEnabled);
...@@ -1226,6 +1248,12 @@ void V8DebuggerAgentImpl::didPause(int contextId, ...@@ -1226,6 +1248,12 @@ void V8DebuggerAgentImpl::didPause(int contextId,
breakAuxData->setArray("reasons", std::move(reasons)); breakAuxData->setArray("reasons", std::move(reasons));
} }
if (m_stepIntoAsyncCallback) {
m_stepIntoAsyncCallback->sendFailure(
Response::Error("No async tasks were scheduled before pause."));
m_stepIntoAsyncCallback.reset();
}
std::unique_ptr<Array<CallFrame>> protocolCallFrames; std::unique_ptr<Array<CallFrame>> protocolCallFrames;
Response response = currentCallFrames(&protocolCallFrames); Response response = currentCallFrames(&protocolCallFrames);
if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create(); if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
......
...@@ -88,6 +88,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -88,6 +88,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
Response stepOver() override; Response stepOver() override;
Response stepInto() override; Response stepInto() override;
Response stepOut() override; Response stepOut() override;
void scheduleStepIntoAsync(
std::unique_ptr<ScheduleStepIntoAsyncCallback> callback) override;
Response setPauseOnExceptions(const String16& pauseState) override; Response setPauseOnExceptions(const String16& pauseState) override;
Response evaluateOnCallFrame( Response evaluateOnCallFrame(
const String16& callFrameId, const String16& expression, const String16& callFrameId, const String16& expression,
...@@ -143,6 +145,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -143,6 +145,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
v8::Isolate* isolate() { return m_isolate; } v8::Isolate* isolate() { return m_isolate; }
bool shouldBreakInScheduledAsyncTask();
private: private:
void enableImpl(); void enableImpl();
...@@ -212,6 +216,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ...@@ -212,6 +216,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
protocol::HashMap<String16, std::vector<std::pair<int, int>>> protocol::HashMap<String16, std::vector<std::pair<int, int>>>
m_blackboxedPositions; m_blackboxedPositions;
std::unique_ptr<ScheduleStepIntoAsyncCallback> m_stepIntoAsyncCallback;
DISALLOW_COPY_AND_ASSIGN(V8DebuggerAgentImpl); DISALLOW_COPY_AND_ASSIGN(V8DebuggerAgentImpl);
}; };
......
...@@ -194,6 +194,7 @@ void V8Debugger::disable() { ...@@ -194,6 +194,7 @@ void V8Debugger::disable() {
m_debuggerScript.Reset(); m_debuggerScript.Reset();
m_debuggerContext.Reset(); m_debuggerContext.Reset();
allAsyncTasksCanceled(); allAsyncTasksCanceled();
m_taskWithScheduledBreak = nullptr;
m_wasmTranslation.Clear(); m_wasmTranslation.Clear();
v8::debug::SetDebugDelegate(m_isolate, nullptr); v8::debug::SetDebugDelegate(m_isolate, nullptr);
v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr); v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr);
...@@ -337,7 +338,7 @@ void V8Debugger::setPauseOnNextStatement(bool pause) { ...@@ -337,7 +338,7 @@ void V8Debugger::setPauseOnNextStatement(bool pause) {
bool V8Debugger::canBreakProgram() { bool V8Debugger::canBreakProgram() {
if (!m_breakpointsActivated) return false; if (!m_breakpointsActivated) return false;
return v8::debug::HasNonBlackboxedFrameOnStack(m_isolate); return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
} }
void V8Debugger::breakProgram() { void V8Debugger::breakProgram() {
...@@ -625,17 +626,21 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script, ...@@ -625,17 +626,21 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
end); end);
} }
void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context,
int id, int parentId) { v8::debug::PromiseDebugActionType type,
if (!m_maxAsyncCallStackDepth) return; int id, int parentId,
bool createdByUser) {
// Async task events from Promises are given misaligned pointers to prevent // Async task events from Promises are given misaligned pointers to prevent
// from overlapping with other Blink task identifiers. There is a single // from overlapping with other Blink task identifiers. There is a single
// namespace of such ids, managed by src/js/promise.js. // namespace of such ids, managed by src/js/promise.js.
void* ptr = reinterpret_cast<void*>(id * 2 + 1); void* ptr = reinterpret_cast<void*>(id * 2 + 1);
void* parentPtr =
parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr;
handleAsyncTaskStepping(context, type, ptr, parentPtr, createdByUser);
if (!m_maxAsyncCallStackDepth) return;
switch (type) { switch (type) {
case v8::debug::kDebugPromiseCreated: case v8::debug::kDebugPromiseCreated:
asyncTaskCreated( asyncTaskCreated(ptr, parentPtr);
ptr, parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr);
break; break;
case v8::debug::kDebugEnqueueAsyncFunction: case v8::debug::kDebugEnqueueAsyncFunction:
asyncTaskScheduled("async function", ptr, true); asyncTaskScheduled("async function", ptr, true);
...@@ -658,6 +663,45 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, ...@@ -658,6 +663,45 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
} }
} }
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;
}
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() { V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
if (!m_currentStacks.size()) return nullptr; if (!m_currentStacks.size()) return nullptr;
return m_currentStacks.back().get(); return m_currentStacks.back().get();
...@@ -802,11 +846,7 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( ...@@ -802,11 +846,7 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace( std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
v8::Local<v8::StackTrace> stackTrace) { v8::Local<v8::StackTrace> stackTrace) {
int contextGroupId = return V8StackTraceImpl::create(this, currentContextGroupId(), stackTrace,
m_isolate->InContext()
? m_inspector->contextGroupId(m_isolate->GetCurrentContext())
: 0;
return V8StackTraceImpl::create(this, contextGroupId, stackTrace,
V8StackTraceImpl::maxCallStackSizeToCapture); V8StackTraceImpl::maxCallStackSizeToCapture);
} }
...@@ -843,7 +883,7 @@ void V8Debugger::asyncTaskCreated(void* task, void* parentTask) { ...@@ -843,7 +883,7 @@ void V8Debugger::asyncTaskCreated(void* task, void* parentTask) {
if (!m_maxAsyncCallStackDepth) return; if (!m_maxAsyncCallStackDepth) return;
if (parentTask) m_parentTask[task] = parentTask; if (parentTask) m_parentTask[task] = parentTask;
v8::HandleScope scope(m_isolate); 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. // from V8 for promise events only.
// Passing one as maxStackSize forces no async chain for the new stack and // Passing one as maxStackSize forces no async chain for the new stack and
// allows us to not grow exponentially. // allows us to not grow exponentially.
...@@ -865,13 +905,9 @@ void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task, ...@@ -865,13 +905,9 @@ void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task,
bool recurring) { bool recurring) {
if (!m_maxAsyncCallStackDepth) return; if (!m_maxAsyncCallStackDepth) return;
v8::HandleScope scope(m_isolate); v8::HandleScope scope(m_isolate);
int contextGroupId =
m_isolate->InContext()
? m_inspector->contextGroupId(m_isolate->GetCurrentContext())
: 0;
std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture( std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
this, contextGroupId, V8StackTraceImpl::maxCallStackSizeToCapture, this, currentContextGroupId(),
taskName); V8StackTraceImpl::maxCallStackSizeToCapture, taskName);
if (chain) { if (chain) {
m_asyncTaskStacks[task] = std::move(chain); m_asyncTaskStacks[task] = std::move(chain);
if (recurring) m_recurringTasks.insert(task); if (recurring) m_recurringTasks.insert(task);
...@@ -954,8 +990,7 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace( ...@@ -954,8 +990,7 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
if (!m_isolate->InContext()) return nullptr; if (!m_isolate->InContext()) return nullptr;
v8::HandleScope handles(m_isolate); v8::HandleScope handles(m_isolate);
int contextGroupId = int contextGroupId = currentContextGroupId();
m_inspector->contextGroupId(m_isolate->GetCurrentContext());
if (!contextGroupId) return nullptr; if (!contextGroupId) return nullptr;
size_t stackSize = size_t stackSize =
...@@ -966,4 +1001,9 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace( ...@@ -966,4 +1001,9 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
return V8StackTraceImpl::capture(this, contextGroupId, stackSize); 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 } // namespace v8_inspector
...@@ -130,8 +130,9 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -130,8 +130,9 @@ class V8Debugger : public v8::debug::DebugDelegate {
void registerAsyncTaskIfNeeded(void* task); void registerAsyncTaskIfNeeded(void* task);
// v8::debug::DebugEventListener implementation. // v8::debug::DebugEventListener implementation.
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, void PromiseEventOccurred(v8::Local<v8::Context> context,
int parentId) override; v8::debug::PromiseDebugActionType type, int id,
int parentId, bool createdByUser) override;
void ScriptCompiled(v8::Local<v8::debug::Script> script, void ScriptCompiled(v8::Local<v8::debug::Script> script,
bool has_compile_error) override; bool has_compile_error) override;
void BreakProgramRequested(v8::Local<v8::Context> paused_context, void BreakProgramRequested(v8::Local<v8::Context> paused_context,
...@@ -145,6 +146,12 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -145,6 +146,12 @@ class V8Debugger : public v8::debug::DebugDelegate {
const v8::debug::Location& start, const v8::debug::Location& start,
const v8::debug::Location& end) override; const v8::debug::Location& end) override;
int currentContextGroupId();
void handleAsyncTaskStepping(v8::Local<v8::Context> context,
v8::debug::PromiseDebugActionType type,
void* task, void* parentTask,
bool createdByUser);
v8::Isolate* m_isolate; v8::Isolate* m_isolate;
V8InspectorImpl* m_inspector; V8InspectorImpl* m_inspector;
int m_enableCount; int m_enableCount;
...@@ -171,6 +178,8 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -171,6 +178,8 @@ class V8Debugger : public v8::debug::DebugDelegate {
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks; std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks;
protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap; protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
protocol::HashMap<void*, void*> m_parentTask; protocol::HashMap<void*, void*> m_parentTask;
protocol::HashMap<void*, void*> m_firstNextTask;
void* m_taskWithScheduledBreak = nullptr;
v8::debug::ExceptionBreakState m_pauseOnExceptionsState; v8::debug::ExceptionBreakState m_pauseOnExceptionsState;
......
Checks Debugger.scheduleStepIntoAsync.
Running test: testScheduleErrors
paused at:
function testNoScheduledTask() {
#debugger;
return 42;
{
error : {
code : -32000
message : Current scheduled step into async was overriden with new one.
}
id : <messageId>
}
{
error : {
code : -32000
message : No async tasks were scheduled before pause.
}
id : <messageId>
}
paused at:
debugger;
#return 42;
}
Running test: testSimple
paused at:
function testSimple() {
#debugger;
Promise.resolve().then(v => v * 2);
paused at:
debugger;
#Promise.resolve().then(v => v * 2);
}
{
id : <messageId>
result : {
}
}
paused at:
debugger;
Promise.resolve().then(#v => v * 2);
}
Running test: testNotResolvedPromise
paused at:
var p = new Promise(resolve => resolveCallback = resolve);
#debugger;
p.then(v => v * 2);
paused at:
debugger;
#p.then(v => v * 2);
resolveCallback();
{
id : <messageId>
result : {
}
}
paused at:
debugger;
p.then(#v => v * 2);
resolveCallback();
Running test: testTwoAsyncTasks
paused at:
function testTwoAsyncTasks() {
#debugger;
Promise.resolve().then(v => v * 2);
{
id : <messageId>
result : {
}
}
paused at:
debugger;
Promise.resolve().then(#v => v * 2);
Promise.resolve().then(v => v * 4);
Running test: testTwoTasksAndGoToSecond
paused at:
function testTwoAsyncTasks() {
#debugger;
Promise.resolve().then(v => v * 2);
paused at:
debugger;
#Promise.resolve().then(v => v * 2);
Promise.resolve().then(v => v * 4);
paused at:
Promise.resolve().then(v => v * 2);
#Promise.resolve().then(v => v * 4);
}
{
id : <messageId>
result : {
}
}
paused at:
Promise.resolve().then(v => v * 2);
Promise.resolve().then(#v => v * 4);
}
Running test: testTwoAsyncTasksWithBreak
paused at:
function testTwoAsyncTasksWithBreak() {
#debugger;
Promise.resolve().then(v => v * 2);
paused at:
debugger;
#Promise.resolve().then(v => v * 2);
debugger;
{
id : <messageId>
result : {
}
}
paused at:
Promise.resolve().then(v => v * 2);
#debugger;
Promise.resolve().then(v => v * 4);
{
id : <messageId>
result : {
}
}
paused at:
debugger;
Promise.resolve().then(#v => v * 4);
}
Running test: testPromiseAll
paused at:
function testPromiseAll() {
#debugger;
Promise.all([ Promise.resolve(), Promise.resolve() ]).then(v => v * 2);
paused at:
debugger;
#Promise.all([ Promise.resolve(), Promise.resolve() ]).then(v => v * 2);
}
{
id : <messageId>
result : {
}
}
paused at:
debugger;
Promise.all([ Promise.resolve(), Promise.resolve() ]).then(#v => v * 2);
}
Running test: testWithBlackboxedCode
paused at:
function testBlackboxedCreatePromise() {
#debugger;
createPromise().then(v => v * 2);
paused at:
debugger;
#createPromise().then(v => v * 2);
}
{
id : <messageId>
result : {
}
}
paused at:
debugger;
createPromise().then(#v => v * 2);
}
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
InspectorTest.log('Checks Debugger.scheduleStepIntoAsync.');
InspectorTest.addScript(`
function testNoScheduledTask() {
debugger;
return 42;
}
function testSimple() {
debugger;
Promise.resolve().then(v => v * 2);
}
function testNotResolvedPromise() {
var resolveCallback;
var p = new Promise(resolve => resolveCallback = resolve);
debugger;
p.then(v => v * 2);
resolveCallback();
}
function testTwoAsyncTasks() {
debugger;
Promise.resolve().then(v => v * 2);
Promise.resolve().then(v => v * 4);
}
function testTwoAsyncTasksWithBreak() {
debugger;
Promise.resolve().then(v => v * 2);
debugger;
Promise.resolve().then(v => v * 4);
}
function testPromiseAll() {
debugger;
Promise.all([ Promise.resolve(), Promise.resolve() ]).then(v => v * 2);
}
function testBlackboxedCreatePromise() {
debugger;
createPromise().then(v => v * 2);
}
//# sourceURL=test.js`);
InspectorTest.addScript(`
function createPromise() {
return Promise.resolve().then(v => v * 3).then(v => v * 4);
}
//# sourceURL=framework.js`)
InspectorTest.setupScriptMap();
Protocol.Debugger.enable();
InspectorTest.runAsyncTestSuite([
async function testScheduleErrors() {
Protocol.Runtime.evaluate({ expression: 'testNoScheduledTask()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.stepInto();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
},
async function testSimple() {
Protocol.Runtime.evaluate({ expression: 'testSimple()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.stepInto();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
},
async function testNotResolvedPromise() {
Protocol.Runtime.evaluate({ expression: 'testNotResolvedPromise()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.stepInto();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
},
async function testTwoAsyncTasks() {
Protocol.Runtime.evaluate({ expression: 'testTwoAsyncTasks()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.resume();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
},
async function testTwoTasksAndGoToSecond() {
Protocol.Runtime.evaluate({ expression: 'testTwoAsyncTasks()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.resume();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
},
async function testTwoAsyncTasksWithBreak() {
Protocol.Runtime.evaluate({ expression: 'testTwoAsyncTasksWithBreak()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.resume();
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.resume();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
},
async function testPromiseAll() {
Protocol.Runtime.evaluate({ expression: 'testPromiseAll()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
},
async function testWithBlackboxedCode() {
Protocol.Runtime.evaluate({ expression: 'testBlackboxedCreatePromise()' });
await waitPauseAndDumpLocation();
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
await Protocol.Debugger.setBlackboxPatterns({patterns: ['framework\.js'] });
Protocol.Debugger.scheduleStepIntoAsync().then(InspectorTest.logMessage);
Protocol.Debugger.stepOver();
await waitPauseAndDumpLocation();
await Protocol.Debugger.resume();
}
]);
async function waitPauseAndDumpLocation() {
var message = await Protocol.Debugger.oncePaused();
InspectorTest.log('paused at:');
InspectorTest.logSourceLocation(message.params.callFrames[0].location);
return message;
}
...@@ -248,6 +248,14 @@ InspectorTest.runTestSuite = function(testSuite) ...@@ -248,6 +248,14 @@ InspectorTest.runTestSuite = function(testSuite)
nextTest(); nextTest();
} }
InspectorTest.runAsyncTestSuite = async function(testSuite) {
for (var test of testSuite) {
InspectorTest.log("\nRunning test: " + test.name);
await test();
}
InspectorTest.completeTest();
}
InspectorTest._sendCommandPromise = function(method, params) InspectorTest._sendCommandPromise = function(method, params)
{ {
var requestId = ++InspectorTest._requestId; var requestId = ++InspectorTest._requestId;
......
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