diff options
Diffstat (limited to 'src/inspector/v8-debugger.cc')
-rw-r--r-- | src/inspector/v8-debugger.cc | 760 |
1 files changed, 371 insertions, 389 deletions
diff --git a/src/inspector/v8-debugger.cc b/src/inspector/v8-debugger.cc index b3657e57..3a2fc89f 100644 --- a/src/inspector/v8-debugger.cc +++ b/src/inspector/v8-debugger.cc @@ -5,6 +5,7 @@ #include "src/inspector/v8-debugger.h" #include "src/inspector/debugger-script.h" +#include "src/inspector/inspected-context.h" #include "src/inspector/protocol/Protocol.h" #include "src/inspector/script-breakpoint.h" #include "src/inspector/string-util.h" @@ -19,22 +20,129 @@ namespace v8_inspector { namespace { -static const char v8AsyncTaskEventEnqueue[] = "enqueue"; -static const char v8AsyncTaskEventEnqueueRecurring[] = "enqueueRecurring"; -static const char v8AsyncTaskEventWillHandle[] = "willHandle"; -static const char v8AsyncTaskEventDidHandle[] = "didHandle"; -static const char v8AsyncTaskEventCancel[] = "cancel"; + +// Based on DevTools frontend measurement, with asyncCallStackDepth = 4, +// average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb +// for async stacks. +static const int kMaxAsyncTaskStacks = 128 * 1024; inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) { return value ? v8::True(isolate) : v8::False(isolate); } +V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector, + v8::Local<v8::debug::Script> script) { + v8::Local<v8::Value> contextData; + if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) { + return nullptr; + } + int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); + int contextGroupId = inspector->contextGroupId(contextId); + if (!contextGroupId) return nullptr; + return inspector->enabledDebuggerAgentForGroup(contextGroupId); +} + +v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context, + v8::Local<v8::Value> value) { + v8::Isolate* isolate = context->GetIsolate(); + v8::Local<v8::Array> entries; + bool isKeyValue = false; + if (!v8::debug::EntriesPreview(isolate, value, &isKeyValue).ToLocal(&entries)) + return v8::MaybeLocal<v8::Array>(); + + v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate); + CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0); + if (!wrappedEntries->SetPrototype(context, v8::Null(isolate)) + .FromMaybe(false)) + return v8::MaybeLocal<v8::Array>(); + for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) { + v8::Local<v8::Value> item; + if (!entries->Get(context, i).ToLocal(&item)) continue; + v8::Local<v8::Value> value; + if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue; + v8::Local<v8::Object> wrapper = v8::Object::New(isolate); + if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false)) + continue; + createDataProperty( + context, wrapper, + toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item); + if (isKeyValue) { + createDataProperty(context, wrapper, + toV8StringInternalized(isolate, "value"), value); + } + createDataProperty(context, wrappedEntries, wrappedEntries->Length(), + wrapper); + } + if (!markArrayEntriesAsInternal(context, wrappedEntries, + V8InternalValueType::kEntry)) { + return v8::MaybeLocal<v8::Array>(); + } + return wrappedEntries; +} + +v8::MaybeLocal<v8::Object> buildLocation(v8::Local<v8::Context> context, + int scriptId, int lineNumber, + int columnNumber) { + if (scriptId == v8::UnboundScript::kNoScriptId) + return v8::MaybeLocal<v8::Object>(); + if (lineNumber == v8::Function::kLineOffsetNotFound || + columnNumber == v8::Function::kLineOffsetNotFound) { + return v8::MaybeLocal<v8::Object>(); + } + v8::Isolate* isolate = context->GetIsolate(); + v8::Local<v8::Object> location = v8::Object::New(isolate); + if (!location->SetPrototype(context, v8::Null(isolate)).FromMaybe(false)) { + return v8::MaybeLocal<v8::Object>(); + } + if (!createDataProperty(context, location, + toV8StringInternalized(isolate, "scriptId"), + toV8String(isolate, String16::fromInteger(scriptId))) + .FromMaybe(false)) { + return v8::MaybeLocal<v8::Object>(); + } + if (!createDataProperty(context, location, + toV8StringInternalized(isolate, "lineNumber"), + v8::Integer::New(isolate, lineNumber)) + .FromMaybe(false)) { + return v8::MaybeLocal<v8::Object>(); + } + if (!createDataProperty(context, location, + toV8StringInternalized(isolate, "columnNumber"), + v8::Integer::New(isolate, columnNumber)) + .FromMaybe(false)) { + return v8::MaybeLocal<v8::Object>(); + } + if (!markAsInternal(context, location, V8InternalValueType::kLocation)) { + return v8::MaybeLocal<v8::Object>(); + } + return location; +} + +v8::MaybeLocal<v8::Object> generatorObjectLocation( + v8::Local<v8::Context> context, v8::Local<v8::Value> value) { + if (!value->IsGeneratorObject()) return v8::MaybeLocal<v8::Object>(); + v8::Local<v8::debug::GeneratorObject> generatorObject = + v8::debug::GeneratorObject::Cast(value); + if (!generatorObject->IsSuspended()) { + v8::Local<v8::Function> func = generatorObject->Function(); + return buildLocation(context, func->ScriptId(), func->GetScriptLineNumber(), + func->GetScriptColumnNumber()); + } + v8::Local<v8::debug::Script> script; + if (!generatorObject->Script().ToLocal(&script)) + return v8::MaybeLocal<v8::Object>(); + v8::debug::Location suspendedLocation = generatorObject->SuspendedLocation(); + return buildLocation(context, script->Id(), suspendedLocation.GetLineNumber(), + suspendedLocation.GetColumnNumber()); +} + } // namespace static bool inLiveEditScope = false; v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod( - const char* functionName, int argc, v8::Local<v8::Value> argv[]) { + const char* functionName, int argc, v8::Local<v8::Value> argv[], + bool catchExceptions) { v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); DCHECK(m_isolate->InContext()); @@ -44,19 +152,25 @@ v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod( debuggerScript ->Get(context, toV8StringInternalized(m_isolate, functionName)) .ToLocalChecked()); + if (catchExceptions) { + v8::TryCatch try_catch(m_isolate); + return function->Call(context, debuggerScript, argc, argv); + } return function->Call(context, debuggerScript, argc, argv); } V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector) : m_isolate(isolate), m_inspector(inspector), - m_lastContextId(0), m_enableCount(0), m_breakpointsActivated(true), m_runningNestedMessageLoop(false), m_ignoreScriptParsedEventsCounter(0), + m_maxAsyncCallStacks(kMaxAsyncTaskStacks), + m_lastTaskId(0), m_maxAsyncCallStackDepth(0), - m_pauseOnExceptionsState(v8::DebugInterface::NoBreakOnException) {} + m_pauseOnExceptionsState(v8::debug::NoBreakOnException), + m_wasmTranslation(isolate) {} V8Debugger::~V8Debugger() {} @@ -64,14 +178,12 @@ void V8Debugger::enable() { if (m_enableCount++) return; DCHECK(!enabled()); v8::HandleScope scope(m_isolate); - v8::DebugInterface::SetDebugEventListener(m_isolate, - &V8Debugger::v8DebugEventCallback, - v8::External::New(m_isolate, this)); - m_debuggerContext.Reset(m_isolate, - v8::DebugInterface::GetDebugContext(m_isolate)); - v8::DebugInterface::ChangeBreakOnException( - m_isolate, v8::DebugInterface::NoBreakOnException); - m_pauseOnExceptionsState = v8::DebugInterface::NoBreakOnException; + v8::debug::SetDebugDelegate(m_isolate, this); + v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback, + this); + m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate)); + v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException); + m_pauseOnExceptionsState = v8::debug::NoBreakOnException; compileDebuggerScript(); } @@ -82,61 +194,33 @@ void V8Debugger::disable() { m_debuggerScript.Reset(); m_debuggerContext.Reset(); allAsyncTasksCanceled(); - v8::DebugInterface::SetDebugEventListener(m_isolate, nullptr); + m_wasmTranslation.Clear(); + v8::debug::SetDebugDelegate(m_isolate, nullptr); + v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr); + m_isolate->RestoreOriginalHeapLimit(); } bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); } -// static -int V8Debugger::contextId(v8::Local<v8::Context> context) { - v8::Local<v8::Value> data = - context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex)); - if (data.IsEmpty() || !data->IsString()) return 0; - String16 dataString = toProtocolString(data.As<v8::String>()); - if (dataString.isEmpty()) return 0; - size_t commaPos = dataString.find(","); - if (commaPos == String16::kNotFound) return 0; - size_t commaPos2 = dataString.find(",", commaPos + 1); - if (commaPos2 == String16::kNotFound) return 0; - return dataString.substring(commaPos + 1, commaPos2 - commaPos - 1) - .toInteger(); -} - -// static -int V8Debugger::getGroupId(v8::Local<v8::Context> context) { - v8::Local<v8::Value> data = - context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex)); - if (data.IsEmpty() || !data->IsString()) return 0; - String16 dataString = toProtocolString(data.As<v8::String>()); - if (dataString.isEmpty()) return 0; - size_t commaPos = dataString.find(","); - if (commaPos == String16::kNotFound) return 0; - return dataString.substring(0, commaPos).toInteger(); -} - void V8Debugger::getCompiledScripts( int contextGroupId, std::vector<std::unique_ptr<V8DebuggerScript>>& result) { v8::HandleScope scope(m_isolate); - v8::PersistentValueVector<v8::DebugInterface::Script> scripts(m_isolate); - v8::DebugInterface::GetLoadedScripts(m_isolate, scripts); - String16 contextPrefix = String16::fromInteger(contextGroupId) + ","; + v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate); + v8::debug::GetLoadedScripts(m_isolate, scripts); for (size_t i = 0; i < scripts.Size(); ++i) { - v8::Local<v8::DebugInterface::Script> script = scripts.Get(i); + v8::Local<v8::debug::Script> script = scripts.Get(i); if (!script->WasCompiled()) continue; - v8::ScriptOriginOptions origin = script->OriginOptions(); - if (origin.IsEmbedderDebugScript()) continue; - v8::Local<v8::String> v8ContextData; - if (!script->ContextData().ToLocal(&v8ContextData)) continue; - String16 contextData = toProtocolString(v8ContextData); - if (contextData.find(contextPrefix) != 0) continue; - result.push_back( - wrapUnique(new V8DebuggerScript(m_isolate, script, false))); + v8::Local<v8::Value> contextData; + if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) + continue; + int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); + if (m_inspector->contextGroupId(contextId) != contextGroupId) continue; + result.push_back(V8DebuggerScript::Create(m_isolate, script, false)); } } -String16 V8Debugger::setBreakpoint(const String16& sourceID, - const ScriptBreakpoint& scriptBreakpoint, +String16 V8Debugger::setBreakpoint(const ScriptBreakpoint& breakpoint, int* actualLineNumber, int* actualColumnNumber) { v8::HandleScope scope(m_isolate); @@ -146,29 +230,30 @@ String16 V8Debugger::setBreakpoint(const String16& sourceID, v8::Local<v8::Object> info = v8::Object::New(m_isolate); bool success = false; success = info->Set(context, toV8StringInternalized(m_isolate, "sourceID"), - toV8String(m_isolate, sourceID)) + toV8String(m_isolate, breakpoint.script_id)) .FromMaybe(false); DCHECK(success); success = info->Set(context, toV8StringInternalized(m_isolate, "lineNumber"), - v8::Integer::New(m_isolate, scriptBreakpoint.lineNumber)) + v8::Integer::New(m_isolate, breakpoint.line_number)) .FromMaybe(false); DCHECK(success); success = info->Set(context, toV8StringInternalized(m_isolate, "columnNumber"), - v8::Integer::New(m_isolate, scriptBreakpoint.columnNumber)) + v8::Integer::New(m_isolate, breakpoint.column_number)) .FromMaybe(false); DCHECK(success); success = info->Set(context, toV8StringInternalized(m_isolate, "condition"), - toV8String(m_isolate, scriptBreakpoint.condition)) + toV8String(m_isolate, breakpoint.condition)) .FromMaybe(false); DCHECK(success); + USE(success); v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast( m_debuggerScript.Get(m_isolate) ->Get(context, toV8StringInternalized(m_isolate, "setBreakpoint")) .ToLocalChecked()); v8::Local<v8::Value> breakpointId = - v8::DebugInterface::Call(debuggerContext(), setBreakpointFunction, info) + v8::debug::Call(debuggerContext(), setBreakpointFunction, info) .ToLocalChecked(); if (!breakpointId->IsString()) return ""; *actualLineNumber = @@ -196,6 +281,7 @@ void V8Debugger::removeBreakpoint(const String16& breakpointId) { toV8String(m_isolate, breakpointId)) .FromMaybe(false); DCHECK(success); + USE(success); v8::Local<v8::Function> removeBreakpointFunction = v8::Local<v8::Function>::Cast( @@ -203,7 +289,7 @@ void V8Debugger::removeBreakpoint(const String16& breakpointId) { ->Get(context, toV8StringInternalized(m_isolate, "removeBreakpoint")) .ToLocalChecked()); - v8::DebugInterface::Call(debuggerContext(), removeBreakpointFunction, info) + v8::debug::Call(debuggerContext(), removeBreakpointFunction, info) .ToLocalChecked(); } @@ -216,8 +302,7 @@ void V8Debugger::clearBreakpoints() { m_debuggerScript.Get(m_isolate) ->Get(context, toV8StringInternalized(m_isolate, "clearBreakpoints")) .ToLocalChecked()); - v8::DebugInterface::Call(debuggerContext(), clearBreakpoints) - .ToLocalChecked(); + v8::debug::Call(debuggerContext(), clearBreakpoints).ToLocalChecked(); } void V8Debugger::setBreakpointsActivated(bool activated) { @@ -225,65 +310,39 @@ void V8Debugger::setBreakpointsActivated(bool activated) { UNREACHABLE(); return; } - v8::HandleScope scope(m_isolate); - v8::Local<v8::Context> context = debuggerContext(); - v8::Context::Scope contextScope(context); - - v8::Local<v8::Object> info = v8::Object::New(m_isolate); - bool success = false; - success = info->Set(context, toV8StringInternalized(m_isolate, "enabled"), - v8::Boolean::New(m_isolate, activated)) - .FromMaybe(false); - DCHECK(success); - v8::Local<v8::Function> setBreakpointsActivated = - v8::Local<v8::Function>::Cast( - m_debuggerScript.Get(m_isolate) - ->Get(context, toV8StringInternalized(m_isolate, - "setBreakpointsActivated")) - .ToLocalChecked()); - v8::DebugInterface::Call(debuggerContext(), setBreakpointsActivated, info) - .ToLocalChecked(); - + v8::debug::SetBreakPointsActive(m_isolate, activated); m_breakpointsActivated = activated; } -v8::DebugInterface::ExceptionBreakState -V8Debugger::getPauseOnExceptionsState() { +v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() { DCHECK(enabled()); return m_pauseOnExceptionsState; } void V8Debugger::setPauseOnExceptionsState( - v8::DebugInterface::ExceptionBreakState pauseOnExceptionsState) { + v8::debug::ExceptionBreakState pauseOnExceptionsState) { DCHECK(enabled()); if (m_pauseOnExceptionsState == pauseOnExceptionsState) return; - v8::DebugInterface::ChangeBreakOnException(m_isolate, pauseOnExceptionsState); + v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState); m_pauseOnExceptionsState = pauseOnExceptionsState; } void V8Debugger::setPauseOnNextStatement(bool pause) { - if (m_runningNestedMessageLoop) return; + if (isPaused()) return; if (pause) - v8::DebugInterface::DebugBreak(m_isolate); + v8::debug::DebugBreak(m_isolate); else - v8::DebugInterface::CancelDebugBreak(m_isolate); + v8::debug::CancelDebugBreak(m_isolate); } bool V8Debugger::canBreakProgram() { if (!m_breakpointsActivated) return false; - return m_isolate->InContext(); + return v8::debug::HasNonBlackboxedFrameOnStack(m_isolate); } void V8Debugger::breakProgram() { - if (isPaused()) { - DCHECK(!m_runningNestedMessageLoop); - v8::Local<v8::Value> exception; - v8::Local<v8::Array> hitBreakpoints; - handleProgramBreak(m_pausedContext, m_executionState, exception, - hitBreakpoints); - return; - } - + // Don't allow nested breaks. + if (isPaused()) return; if (!canBreakProgram()) return; v8::HandleScope scope(m_isolate); @@ -294,7 +353,7 @@ void V8Debugger::breakProgram() { v8::ConstructorBehavior::kThrow) .ToLocal(&breakFunction)) return; - v8::DebugInterface::Call(debuggerContext(), breakFunction).ToLocalChecked(); + v8::debug::Call(debuggerContext(), breakFunction).ToLocalChecked(); } void V8Debugger::continueProgram() { @@ -306,29 +365,24 @@ void V8Debugger::continueProgram() { void V8Debugger::stepIntoStatement() { DCHECK(isPaused()); DCHECK(!m_executionState.IsEmpty()); - v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepIn); + v8::debug::PrepareStep(m_isolate, v8::debug::StepIn); continueProgram(); } void V8Debugger::stepOverStatement() { DCHECK(isPaused()); DCHECK(!m_executionState.IsEmpty()); - v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepNext); + v8::debug::PrepareStep(m_isolate, v8::debug::StepNext); continueProgram(); } void V8Debugger::stepOutOfFunction() { DCHECK(isPaused()); DCHECK(!m_executionState.IsEmpty()); - v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepOut); + v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); continueProgram(); } -void V8Debugger::clearStepping() { - DCHECK(enabled()); - v8::DebugInterface::ClearStepping(m_isolate); -} - Response V8Debugger::setScriptSource( const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails, @@ -337,11 +391,11 @@ Response V8Debugger::setScriptSource( class EnableLiveEditScope { public: explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) { - v8::DebugInterface::SetLiveEditEnabled(m_isolate, true); + v8::debug::SetLiveEditEnabled(m_isolate, true); inLiveEditScope = true; } ~EnableLiveEditScope() { - v8::DebugInterface::SetLiveEditEnabled(m_isolate, false); + v8::debug::SetLiveEditEnabled(m_isolate, false); inLiveEditScope = false; } @@ -355,7 +409,7 @@ Response V8Debugger::setScriptSource( std::unique_ptr<v8::Context::Scope> contextScope; if (!isPaused()) - contextScope = wrapUnique(new v8::Context::Scope(debuggerContext())); + contextScope.reset(new v8::Context::Scope(debuggerContext())); v8::Local<v8::Value> argv[] = {toV8String(m_isolate, sourceID), newSource, v8Boolean(dryRun, m_isolate)}; @@ -366,7 +420,7 @@ Response V8Debugger::setScriptSource( v8::TryCatch tryCatch(m_isolate); tryCatch.SetVerbose(false); v8::MaybeLocal<v8::Value> maybeResult = - callDebuggerMethod("liveEditScriptSource", 3, argv); + callDebuggerMethod("liveEditScriptSource", 3, argv, false); if (tryCatch.HasCaught()) { v8::Local<v8::Message> message = tryCatch.Message(); if (!message.IsEmpty()) @@ -427,27 +481,14 @@ Response V8Debugger::setScriptSource( } JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) { - if (!m_isolate->InContext()) return JavaScriptCallFrames(); + if (!isPaused()) return JavaScriptCallFrames(); v8::Local<v8::Value> currentCallFramesV8; - if (m_executionState.IsEmpty()) { - v8::Local<v8::Function> currentCallFramesFunction = - v8::Local<v8::Function>::Cast( - m_debuggerScript.Get(m_isolate) - ->Get(debuggerContext(), - toV8StringInternalized(m_isolate, "currentCallFrames")) - .ToLocalChecked()); - currentCallFramesV8 = - v8::DebugInterface::Call(debuggerContext(), currentCallFramesFunction, - v8::Integer::New(m_isolate, limit)) - .ToLocalChecked(); - } else { - v8::Local<v8::Value> argv[] = {m_executionState, - v8::Integer::New(m_isolate, limit)}; - currentCallFramesV8 = - callDebuggerMethod("currentCallFrames", arraysize(argv), argv) - .ToLocalChecked(); - } - DCHECK(!currentCallFramesV8.IsEmpty()); + v8::Local<v8::Value> argv[] = {m_executionState, + v8::Integer::New(m_isolate, limit)}; + if (!callDebuggerMethod("currentCallFrames", arraysize(argv), argv, true) + .ToLocal(¤tCallFramesV8)) { + return JavaScriptCallFrames(); + } if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames(); v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>(); JavaScriptCallFrames callFrames; @@ -488,11 +529,11 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, v8::Local<v8::Array> hitBreakpointNumbers, bool isPromiseRejection, bool isUncaught) { // Don't allow nested breaks. - if (m_runningNestedMessageLoop) return; + if (isPaused()) return; - V8DebuggerAgentImpl* agent = - m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext)); - if (!agent) return; + V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup( + m_inspector->contextGroupId(pausedContext)); + if (!agent || (agent->skipAllPauses() && !m_scheduledOOMBreak)) return; std::vector<String16> breakpointIds; if (!hitBreakpointNumbers.IsEmpty()) { @@ -508,147 +549,113 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, m_pausedContext = pausedContext; m_executionState = executionState; - V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause( - pausedContext, exception, breakpointIds, isPromiseRejection, isUncaught); - if (result == V8DebuggerAgentImpl::RequestNoSkip) { - m_runningNestedMessageLoop = true; - int groupId = getGroupId(pausedContext); - DCHECK(groupId); + m_runningNestedMessageLoop = true; + agent->didPause(InspectedContext::contextId(pausedContext), exception, + breakpointIds, isPromiseRejection, isUncaught, + m_scheduledOOMBreak); + int groupId = m_inspector->contextGroupId(pausedContext); + DCHECK(groupId); + { + v8::Context::Scope scope(pausedContext); + v8::Local<v8::Context> context = m_isolate->GetCurrentContext(); + CHECK(!context.IsEmpty() && + context != v8::debug::GetDebugContext(m_isolate)); m_inspector->client()->runMessageLoopOnPause(groupId); - // The agent may have been removed in the nested loop. - agent = - m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext)); - if (agent) agent->didContinue(); m_runningNestedMessageLoop = false; } + // The agent may have been removed in the nested loop. + agent = m_inspector->enabledDebuggerAgentForGroup(groupId); + if (agent) agent->didContinue(); + if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit(); + m_scheduledOOMBreak = false; m_pausedContext.Clear(); m_executionState.Clear(); - - if (result == V8DebuggerAgentImpl::RequestStepFrame) { - v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepFrame); - } else if (result == V8DebuggerAgentImpl::RequestStepInto) { - v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepIn); - } else if (result == V8DebuggerAgentImpl::RequestStepOut) { - v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepOut); - } } -void V8Debugger::v8DebugEventCallback( - const v8::DebugInterface::EventDetails& eventDetails) { - V8Debugger* thisPtr = toV8Debugger(eventDetails.GetCallbackData()); - thisPtr->handleV8DebugEvent(eventDetails); +void V8Debugger::v8OOMCallback(void* data) { + V8Debugger* thisPtr = static_cast<V8Debugger*>(data); + thisPtr->m_isolate->IncreaseHeapLimitForDebugging(); + thisPtr->m_scheduledOOMBreak = true; + thisPtr->setPauseOnNextStatement(true); } -v8::Local<v8::Value> V8Debugger::callInternalGetterFunction( - v8::Local<v8::Object> object, const char* functionName) { - v8::MicrotasksScope microtasks(m_isolate, - v8::MicrotasksScope::kDoNotRunMicrotasks); - v8::Local<v8::Value> getterValue = - object - ->Get(m_isolate->GetCurrentContext(), - toV8StringInternalized(m_isolate, functionName)) - .ToLocalChecked(); - DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction()); - return v8::Local<v8::Function>::Cast(getterValue) - ->Call(m_isolate->GetCurrentContext(), object, 0, nullptr) - .ToLocalChecked(); +void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script, + bool has_compile_error) { + V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script); + if (!agent) return; + if (script->IsWasm()) { + m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent); + } else if (m_ignoreScriptParsedEventsCounter == 0) { + agent->didParseSource( + V8DebuggerScript::Create(m_isolate, script, inLiveEditScope), + !has_compile_error); + } } -void V8Debugger::handleV8DebugEvent( - const v8::DebugInterface::EventDetails& eventDetails) { - if (!enabled()) return; - v8::DebugEvent event = eventDetails.GetEvent(); - if (event != v8::AsyncTaskEvent && event != v8::Break && - event != v8::Exception && event != v8::AfterCompile && - event != v8::BeforeCompile && event != v8::CompileError) - return; - - v8::Local<v8::Context> eventContext = eventDetails.GetEventContext(); - DCHECK(!eventContext.IsEmpty()); - - if (event == v8::AsyncTaskEvent) { - v8::HandleScope scope(m_isolate); - handleV8AsyncTaskEvent(eventContext, eventDetails.GetExecutionState(), - eventDetails.GetEventData()); +void V8Debugger::BreakProgramRequested(v8::Local<v8::Context> pausedContext, + v8::Local<v8::Object> execState, + v8::Local<v8::Value> breakPointsHit) { + v8::Local<v8::Value> argv[] = {breakPointsHit}; + v8::Local<v8::Value> hitBreakpoints; + if (!callDebuggerMethod("getBreakpointNumbers", 1, argv, true) + .ToLocal(&hitBreakpoints)) { return; } + DCHECK(hitBreakpoints->IsArray()); + handleProgramBreak(pausedContext, execState, v8::Local<v8::Value>(), + hitBreakpoints.As<v8::Array>()); +} - V8DebuggerAgentImpl* agent = - m_inspector->enabledDebuggerAgentForGroup(getGroupId(eventContext)); - if (agent) { - v8::HandleScope scope(m_isolate); - if (m_ignoreScriptParsedEventsCounter == 0 && - (event == v8::AfterCompile || event == v8::CompileError)) { - v8::Local<v8::Context> context = debuggerContext(); - v8::Context::Scope contextScope(context); - v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()}; - v8::Local<v8::Value> value = - callDebuggerMethod("getAfterCompileScript", 1, argv).ToLocalChecked(); - if (value->IsNull()) return; - DCHECK(value->IsObject()); - v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(value); - v8::Local<v8::DebugInterface::Script> script; - if (!v8::DebugInterface::Script::Wrap(m_isolate, scriptObject) - .ToLocal(&script)) - return; - agent->didParseSource( - wrapUnique(new V8DebuggerScript(m_isolate, script, inLiveEditScope)), - event == v8::AfterCompile); - } else if (event == v8::Exception) { - v8::Local<v8::Context> context = debuggerContext(); - v8::Local<v8::Object> eventData = eventDetails.GetEventData(); - v8::Local<v8::Value> exception = - callInternalGetterFunction(eventData, "exception"); - v8::Local<v8::Value> promise = - callInternalGetterFunction(eventData, "promise"); - bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject(); - v8::Local<v8::Value> uncaught = - callInternalGetterFunction(eventData, "uncaught"); - bool isUncaught = uncaught->BooleanValue(context).FromJust(); - handleProgramBreak(eventContext, eventDetails.GetExecutionState(), - exception, v8::Local<v8::Array>(), isPromiseRejection, - isUncaught); - } else if (event == v8::Break) { - v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()}; - v8::Local<v8::Value> hitBreakpoints = - callDebuggerMethod("getBreakpointNumbers", 1, argv).ToLocalChecked(); - DCHECK(hitBreakpoints->IsArray()); - handleProgramBreak(eventContext, eventDetails.GetExecutionState(), - v8::Local<v8::Value>(), - hitBreakpoints.As<v8::Array>()); - } - } +void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext, + v8::Local<v8::Object> execState, + v8::Local<v8::Value> exception, + v8::Local<v8::Value> promise, + bool isUncaught) { + bool isPromiseRejection = promise->IsPromise(); + handleProgramBreak(pausedContext, execState, exception, + v8::Local<v8::Array>(), isPromiseRejection, isUncaught); } -void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Context> context, - v8::Local<v8::Object> executionState, - v8::Local<v8::Object> eventData) { - if (!m_maxAsyncCallStackDepth) return; +bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script, + const v8::debug::Location& start, + const v8::debug::Location& end) { + V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script); + if (!agent) return false; + return agent->isFunctionBlackboxed(String16::fromInteger(script->Id()), start, + end); +} - String16 type = toProtocolStringWithTypeCheck( - callInternalGetterFunction(eventData, "type")); - String16 name = toProtocolStringWithTypeCheck( - callInternalGetterFunction(eventData, "name")); - int id = static_cast<int>(callInternalGetterFunction(eventData, "id") - ->ToInteger(context) - .ToLocalChecked() - ->Value()); +void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, + int id, int parentId) { + if (!m_maxAsyncCallStackDepth) return; // 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); - if (type == v8AsyncTaskEventEnqueue) - asyncTaskScheduled(name, ptr, false); - else if (type == v8AsyncTaskEventEnqueueRecurring) - asyncTaskScheduled(name, ptr, true); - else if (type == v8AsyncTaskEventWillHandle) - asyncTaskStarted(ptr); - else if (type == v8AsyncTaskEventDidHandle) - asyncTaskFinished(ptr); - else if (type == v8AsyncTaskEventCancel) - asyncTaskCanceled(ptr); - else - UNREACHABLE(); + switch (type) { + case v8::debug::kDebugPromiseCreated: + asyncTaskCreated( + ptr, parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr); + break; + case v8::debug::kDebugEnqueueAsyncFunction: + asyncTaskScheduled("async function", ptr, true); + break; + case v8::debug::kDebugEnqueuePromiseResolve: + asyncTaskScheduled("Promise.resolve", ptr, true); + break; + case v8::debug::kDebugEnqueuePromiseReject: + asyncTaskScheduled("Promise.reject", ptr, true); + break; + case v8::debug::kDebugPromiseCollected: + asyncTaskCanceled(ptr); + break; + case v8::debug::kDebugWillHandle: + asyncTaskStarted(ptr); + break; + case v8::debug::kDebugDidHandle: + asyncTaskFinished(ptr); + break; + } } V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { @@ -685,15 +692,27 @@ v8::Local<v8::Context> V8Debugger::debuggerContext() const { return m_debuggerContext.Get(m_isolate); } -v8::MaybeLocal<v8::Value> V8Debugger::functionScopes( - v8::Local<v8::Context> context, v8::Local<v8::Function> function) { +v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes( + v8::Local<v8::Context> context, v8::Local<v8::Value> value, + ScopeTargetKind kind) { if (!enabled()) { UNREACHABLE(); return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate)); } - v8::Local<v8::Value> argv[] = {function}; + v8::Local<v8::Value> argv[] = {value}; v8::Local<v8::Value> scopesValue; - if (!callDebuggerMethod("getFunctionScopes", 1, argv).ToLocal(&scopesValue)) + + const char* debuggerMethod = nullptr; + switch (kind) { + case FUNCTION: + debuggerMethod = "getFunctionScopes"; + break; + case GENERATOR: + debuggerMethod = "getGeneratorScopes"; + break; + } + + if (!callDebuggerMethod(debuggerMethod, 1, argv, true).ToLocal(&scopesValue)) return v8::MaybeLocal<v8::Value>(); v8::Local<v8::Value> copied; if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context, @@ -710,16 +729,28 @@ v8::MaybeLocal<v8::Value> V8Debugger::functionScopes( return copied; } +v8::MaybeLocal<v8::Value> V8Debugger::functionScopes( + v8::Local<v8::Context> context, v8::Local<v8::Function> function) { + return getTargetScopes(context, function, FUNCTION); +} + +v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes( + v8::Local<v8::Context> context, v8::Local<v8::Value> generator) { + return getTargetScopes(context, generator, GENERATOR); +} + v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( v8::Local<v8::Context> context, v8::Local<v8::Value> value) { v8::Local<v8::Array> properties; - if (!v8::DebugInterface::GetInternalProperties(m_isolate, value) - .ToLocal(&properties)) + if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties)) return v8::MaybeLocal<v8::Array>(); if (value->IsFunction()) { v8::Local<v8::Function> function = value.As<v8::Function>(); - v8::Local<v8::Value> location = functionLocation(context, function); - if (location->IsObject()) { + v8::Local<v8::Object> location; + if (buildLocation(context, function->ScriptId(), + function->GetScriptLineNumber(), + function->GetScriptColumnNumber()) + .ToLocal(&location)) { createDataProperty( context, properties, properties->Length(), toV8StringInternalized(m_isolate, "[[FunctionLocation]]")); @@ -732,27 +763,29 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( v8::True(m_isolate)); } } - if (!enabled()) return properties; - if (value->IsMap() || value->IsWeakMap() || value->IsSet() || - value->IsWeakSet() || value->IsSetIterator() || value->IsMapIterator()) { - v8::Local<v8::Value> entries = - collectionEntries(context, v8::Local<v8::Object>::Cast(value)); - if (entries->IsArray()) { - createDataProperty(context, properties, properties->Length(), - toV8StringInternalized(m_isolate, "[[Entries]]")); - createDataProperty(context, properties, properties->Length(), entries); - } + v8::Local<v8::Array> entries; + if (collectionsEntries(context, value).ToLocal(&entries)) { + createDataProperty(context, properties, properties->Length(), + toV8StringInternalized(m_isolate, "[[Entries]]")); + createDataProperty(context, properties, properties->Length(), entries); } if (value->IsGeneratorObject()) { - v8::Local<v8::Value> location = - generatorObjectLocation(context, v8::Local<v8::Object>::Cast(value)); - if (location->IsObject()) { + v8::Local<v8::Object> location; + if (generatorObjectLocation(context, value).ToLocal(&location)) { createDataProperty( context, properties, properties->Length(), toV8StringInternalized(m_isolate, "[[GeneratorLocation]]")); createDataProperty(context, properties, properties->Length(), location); } + if (!enabled()) return properties; + v8::Local<v8::Value> scopes; + if (generatorScopes(context, value).ToLocal(&scopes)) { + createDataProperty(context, properties, properties->Length(), + toV8StringInternalized(m_isolate, "[[Scopes]]")); + createDataProperty(context, properties, properties->Length(), scopes); + } } + if (!enabled()) return properties; if (value->IsFunction()) { v8::Local<v8::Function> function = value.As<v8::Function>(); v8::Local<v8::Value> boundFunction = function->GetBoundFunction(); @@ -767,117 +800,16 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( return properties; } -v8::Local<v8::Value> V8Debugger::collectionEntries( - v8::Local<v8::Context> context, v8::Local<v8::Object> object) { - if (!enabled()) { - UNREACHABLE(); - return v8::Undefined(m_isolate); - } - v8::Local<v8::Value> argv[] = {object}; - v8::Local<v8::Value> entriesValue = - callDebuggerMethod("getCollectionEntries", 1, argv).ToLocalChecked(); - if (!entriesValue->IsArray()) return v8::Undefined(m_isolate); - - v8::Local<v8::Array> entries = entriesValue.As<v8::Array>(); - v8::Local<v8::Array> copiedArray = - v8::Array::New(m_isolate, entries->Length()); - if (!copiedArray->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) - return v8::Undefined(m_isolate); - for (uint32_t i = 0; i < entries->Length(); ++i) { - v8::Local<v8::Value> item; - if (!entries->Get(debuggerContext(), i).ToLocal(&item)) - return v8::Undefined(m_isolate); - v8::Local<v8::Value> copied; - if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context, - item) - .ToLocal(&copied)) - return v8::Undefined(m_isolate); - if (!createDataProperty(context, copiedArray, i, copied).FromMaybe(false)) - return v8::Undefined(m_isolate); - } - if (!markArrayEntriesAsInternal(context, - v8::Local<v8::Array>::Cast(copiedArray), - V8InternalValueType::kEntry)) - return v8::Undefined(m_isolate); - return copiedArray; -} - -v8::Local<v8::Value> V8Debugger::generatorObjectLocation( - v8::Local<v8::Context> context, v8::Local<v8::Object> object) { - if (!enabled()) { - UNREACHABLE(); - return v8::Null(m_isolate); - } - v8::Local<v8::Value> argv[] = {object}; - v8::Local<v8::Value> location = - callDebuggerMethod("getGeneratorObjectLocation", 1, argv) - .ToLocalChecked(); - v8::Local<v8::Value> copied; - if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context, - location) - .ToLocal(&copied) || - !copied->IsObject()) - return v8::Null(m_isolate); - if (!markAsInternal(context, v8::Local<v8::Object>::Cast(copied), - V8InternalValueType::kLocation)) - return v8::Null(m_isolate); - return copied; -} - -v8::Local<v8::Value> V8Debugger::functionLocation( - v8::Local<v8::Context> context, v8::Local<v8::Function> function) { - int scriptId = function->ScriptId(); - if (scriptId == v8::UnboundScript::kNoScriptId) return v8::Null(m_isolate); - int lineNumber = function->GetScriptLineNumber(); - int columnNumber = function->GetScriptColumnNumber(); - if (lineNumber == v8::Function::kLineOffsetNotFound || - columnNumber == v8::Function::kLineOffsetNotFound) - return v8::Null(m_isolate); - v8::Local<v8::Object> location = v8::Object::New(m_isolate); - if (!location->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) - return v8::Null(m_isolate); - if (!createDataProperty( - context, location, toV8StringInternalized(m_isolate, "scriptId"), - toV8String(m_isolate, String16::fromInteger(scriptId))) - .FromMaybe(false)) - return v8::Null(m_isolate); - if (!createDataProperty(context, location, - toV8StringInternalized(m_isolate, "lineNumber"), - v8::Integer::New(m_isolate, lineNumber)) - .FromMaybe(false)) - return v8::Null(m_isolate); - if (!createDataProperty(context, location, - toV8StringInternalized(m_isolate, "columnNumber"), - v8::Integer::New(m_isolate, columnNumber)) - .FromMaybe(false)) - return v8::Null(m_isolate); - if (!markAsInternal(context, location, V8InternalValueType::kLocation)) - return v8::Null(m_isolate); - return location; -} - -bool V8Debugger::isPaused() { return !m_pausedContext.IsEmpty(); } - std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace( v8::Local<v8::StackTrace> stackTrace) { int contextGroupId = - m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0; + m_isolate->InContext() + ? m_inspector->contextGroupId(m_isolate->GetCurrentContext()) + : 0; return V8StackTraceImpl::create(this, contextGroupId, stackTrace, V8StackTraceImpl::maxCallStackSizeToCapture); } -int V8Debugger::markContext(const V8ContextInfo& info) { - DCHECK(info.context->GetIsolate() == m_isolate); - int contextId = ++m_lastContextId; - String16 debugData = String16::fromInteger(info.contextGroupId) + "," + - String16::fromInteger(contextId) + "," + - toString16(info.auxData); - v8::Context::Scope contextScope(info.context); - info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex), - toV8String(m_isolate, debugData)); - return contextId; -} - void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { if (depth <= 0) m_maxAsyncCallStackDepthMap.erase(agent); @@ -895,6 +827,34 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { if (!maxAsyncCallStackDepth) allAsyncTasksCanceled(); } +void V8Debugger::registerAsyncTaskIfNeeded(void* task) { + if (m_taskToId.find(task) != m_taskToId.end()) return; + + int id = ++m_lastTaskId; + m_taskToId[task] = id; + m_idToTask[id] = task; + if (static_cast<int>(m_idToTask.size()) > m_maxAsyncCallStacks) { + void* taskToRemove = m_idToTask.begin()->second; + asyncTaskCanceled(taskToRemove); + } +} + +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 + // 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. + std::unique_ptr<V8StackTraceImpl> creationStack = + V8StackTraceImpl::capture(this, 0, 1, String16()); + if (creationStack && !creationStack->isEmpty()) { + m_asyncTaskCreationStacks[task] = std::move(creationStack); + registerAsyncTaskIfNeeded(task); + } +} + void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task, bool recurring) { if (!m_maxAsyncCallStackDepth) return; @@ -906,13 +866,16 @@ void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task, if (!m_maxAsyncCallStackDepth) return; v8::HandleScope scope(m_isolate); int contextGroupId = - m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0; + m_isolate->InContext() + ? m_inspector->contextGroupId(m_isolate->GetCurrentContext()) + : 0; std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture( this, contextGroupId, V8StackTraceImpl::maxCallStackSizeToCapture, taskName); if (chain) { m_asyncTaskStacks[task] = std::move(chain); if (recurring) m_recurringTasks.insert(task); + registerAsyncTaskIfNeeded(task); } } @@ -920,12 +883,20 @@ void V8Debugger::asyncTaskCanceled(void* task) { if (!m_maxAsyncCallStackDepth) return; m_asyncTaskStacks.erase(task); m_recurringTasks.erase(task); + m_parentTask.erase(task); + m_asyncTaskCreationStacks.erase(task); + auto it = m_taskToId.find(task); + if (it == m_taskToId.end()) return; + m_idToTask.erase(it->second); + m_taskToId.erase(it); } void V8Debugger::asyncTaskStarted(void* task) { if (!m_maxAsyncCallStackDepth) return; m_currentTasks.push_back(task); - AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task); + auto parentIt = m_parentTask.find(task); + AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find( + parentIt == m_parentTask.end() ? task : parentIt->second); // Needs to support following order of events: // - asyncTaskScheduled // <-- attached here --> @@ -936,6 +907,10 @@ void V8Debugger::asyncTaskStarted(void* task) { std::unique_ptr<V8StackTraceImpl> stack; if (stackIt != m_asyncTaskStacks.end() && stackIt->second) stack = stackIt->second->cloneImpl(); + auto itCreation = m_asyncTaskCreationStacks.find(task); + if (stack && itCreation != m_asyncTaskCreationStacks.end()) { + stack->setCreation(itCreation->second->cloneImpl()); + } m_currentStacks.push_back(std::move(stack)); } @@ -948,8 +923,9 @@ void V8Debugger::asyncTaskFinished(void* task) { m_currentTasks.pop_back(); m_currentStacks.pop_back(); - if (m_recurringTasks.find(task) == m_recurringTasks.end()) - m_asyncTaskStacks.erase(task); + if (m_recurringTasks.find(task) == m_recurringTasks.end()) { + asyncTaskCanceled(task); + } } void V8Debugger::allAsyncTasksCanceled() { @@ -957,6 +933,11 @@ void V8Debugger::allAsyncTasksCanceled() { m_recurringTasks.clear(); m_currentStacks.clear(); m_currentTasks.clear(); + m_parentTask.clear(); + m_asyncTaskCreationStacks.clear(); + m_idToTask.clear(); + m_taskToId.clear(); + m_lastTaskId = 0; } void V8Debugger::muteScriptParsedEvents() { @@ -973,7 +954,8 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace( if (!m_isolate->InContext()) return nullptr; v8::HandleScope handles(m_isolate); - int contextGroupId = getGroupId(m_isolate->GetCurrentContext()); + int contextGroupId = + m_inspector->contextGroupId(m_isolate->GetCurrentContext()); if (!contextGroupId) return nullptr; size_t stackSize = |