diff options
Diffstat (limited to 'deps/v8/src/inspector')
22 files changed, 802 insertions, 618 deletions
diff --git a/deps/v8/src/inspector/DEPS b/deps/v8/src/inspector/DEPS index 2d77fb7aa7..b69626cdb8 100644 --- a/deps/v8/src/inspector/DEPS +++ b/deps/v8/src/inspector/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+src/base/logging.h", "+src/base/platform/platform.h", "+src/conversions.h", + "+src/unicode-cache.h", "+src/inspector", "+src/tracing", "+src/debug/debug-interface.h", diff --git a/deps/v8/src/inspector/debugger-script.js b/deps/v8/src/inspector/debugger-script.js index d9cb12a09a..89f0d75903 100644 --- a/deps/v8/src/inspector/debugger-script.js +++ b/deps/v8/src/inspector/debugger-script.js @@ -402,10 +402,9 @@ DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror) */ function contextId() { - var mirror = ensureFuncMirror(); - var context = mirror.context(); - if (context && context.data()) - return Number(context.data()); + var context = + ensureFuncMirror().context() || ensureScriptMirror().context(); + if (context && context.data()) return Number(context.data()); return 0; } diff --git a/deps/v8/src/inspector/debugger_script_externs.js b/deps/v8/src/inspector/debugger_script_externs.js index 6f36fb9c41..656bada862 100644 --- a/deps/v8/src/inspector/debugger_script_externs.js +++ b/deps/v8/src/inspector/debugger_script_externs.js @@ -355,6 +355,9 @@ ScriptMirror.prototype.value = function() {} /** @return {number} */ ScriptMirror.prototype.id = function() {} +/** @return {ContextMirror} */ +ScriptMirror.prototype.context = function() {} + /** * @param {number} position * @param {boolean=} includeResourceOffset diff --git a/deps/v8/src/inspector/inspected-context.cc b/deps/v8/src/inspector/inspected-context.cc index 0509683789..27766f200a 100644 --- a/deps/v8/src/inspector/inspected-context.cc +++ b/deps/v8/src/inspector/inspected-context.cc @@ -4,6 +4,7 @@ #include "src/inspector/inspected-context.h" +#include "src/debug/debug-interface.h" #include "src/inspector/injected-script.h" #include "src/inspector/string-util.h" #include "src/inspector/v8-console.h" @@ -24,20 +25,16 @@ InspectedContext::InspectedContext(V8InspectorImpl* inspector, m_humanReadableName(toString16(info.humanReadableName)), m_auxData(toString16(info.auxData)), m_reported(false) { - v8::Isolate* isolate = m_inspector->isolate(); - info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex), - v8::Int32::New(isolate, contextId)); + v8::debug::SetContextId(info.context, contextId); + if (!info.hasMemoryOnConsole) return; + v8::Context::Scope contextScope(info.context); v8::Local<v8::Object> global = info.context->Global(); - v8::Local<v8::Object> console = - m_inspector->console()->createConsole(info.context); - if (info.hasMemoryOnConsole) { - m_inspector->console()->installMemoryGetter(info.context, console); - } - if (!global - ->Set(info.context, toV8StringInternalized(isolate, "console"), - console) - .FromMaybe(false)) { - return; + v8::Local<v8::Value> console; + if (global->Get(info.context, toV8String(m_inspector->isolate(), "console")) + .ToLocal(&console) && + console->IsObject()) { + m_inspector->console()->installMemoryGetter( + info.context, v8::Local<v8::Object>::Cast(console)); } } @@ -46,10 +43,7 @@ InspectedContext::~InspectedContext() { // static int InspectedContext::contextId(v8::Local<v8::Context> context) { - v8::Local<v8::Value> data = - context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex)); - if (data.IsEmpty() || !data->IsInt32()) return 0; - return static_cast<int>(data.As<v8::Int32>()->Value()); + return v8::debug::GetContextId(context); } v8::Local<v8::Context> InspectedContext::context() const { diff --git a/deps/v8/src/inspector/inspector.gyp b/deps/v8/src/inspector/inspector.gyp index 91507bd579..bedab694a8 100644 --- a/deps/v8/src/inspector/inspector.gyp +++ b/deps/v8/src/inspector/inspector.gyp @@ -13,13 +13,7 @@ 'targets': [ { 'target_name': 'inspector_injected_script', 'type': 'none', - 'conditions': [ - ['want_separate_host_toolset==1', { - 'toolsets': ['host', 'target'], - }, { - 'toolsets': ['target'], - }] - ], + 'toolsets': ['target'], 'actions': [ { 'action_name': 'convert_js_to_cpp_char_array', @@ -44,13 +38,7 @@ }, { 'target_name': 'inspector_debugger_script', 'type': 'none', - 'conditions': [ - ['want_separate_host_toolset==1', { - 'toolsets': ['host', 'target'], - }, { - 'toolsets': ['target'], - }] - ], + 'toolsets': ['target'], 'actions': [ { 'action_name': 'convert_js_to_cpp_char_array', @@ -75,13 +63,7 @@ }, { 'target_name': 'protocol_compatibility', 'type': 'none', - 'conditions': [ - ['want_separate_host_toolset==1', { - 'toolsets': ['host', 'target'], - }, { - 'toolsets': ['target'], - }] - ], + 'toolsets': ['target'], 'actions': [ { 'action_name': 'protocol_compatibility', @@ -104,13 +86,7 @@ { 'target_name': 'protocol_generated_sources', 'type': 'none', 'dependencies': [ 'protocol_compatibility' ], - 'conditions': [ - ['want_separate_host_toolset==1', { - 'toolsets': ['host', 'target'], - }, { - 'toolsets': ['target'], - }] - ], + 'toolsets': ['target'], 'actions': [ { 'action_name': 'protocol_generated_sources', diff --git a/deps/v8/src/inspector/js_protocol.json b/deps/v8/src/inspector/js_protocol.json index 7384835fdc..62545cd80d 100644 --- a/deps/v8/src/inspector/js_protocol.json +++ b/deps/v8/src/inspector/js_protocol.json @@ -349,7 +349,7 @@ { "name": "executionContextCreated", "parameters": [ - { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } + { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution context." } ], "description": "Issued when new execution context is created." }, @@ -545,7 +545,7 @@ "name": "getPossibleBreakpoints", "parameters": [ { "name": "start", "$ref": "Location", "description": "Start of range to search possible breakpoint locations in." }, - { "name": "end", "$ref": "Location", "optional": true, "description": "End of range to search possible breakpoint locations in (excluding). When not specifed, end of scripts is used as end of range." }, + { "name": "end", "$ref": "Location", "optional": true, "description": "End of range to search possible breakpoint locations in (excluding). When not specified, end of scripts is used as end of range." }, { "name": "restrictToFunction", "type": "boolean", "optional": true, "description": "Only consider locations which are in the same (non-nested) function as start." } ], "returns": [ @@ -557,7 +557,8 @@ { "name": "continueToLocation", "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to continue to." } + { "name": "location", "$ref": "Location", "description": "Location to continue to." }, + { "name": "targetCallFrames", "type": "string", "enum": ["any", "current"], "optional": true, "experimental": true } ], "description": "Continues execution until specific location is reached." }, @@ -938,7 +939,7 @@ { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } ], - "description": "Sent when new profile recodring is started using console.profile() call." + "description": "Sent when new profile recording is started using console.profile() call." }, { "name": "consoleProfileFinished", @@ -1067,7 +1068,7 @@ }, { "name": "lastSeenObjectId", - "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "description": "If heap objects tracking has been started then backend regularly sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", "parameters": [ { "name": "lastSeenObjectId", "type": "integer" }, { "name": "timestamp", "type": "number" } diff --git a/deps/v8/src/inspector/string-util.cc b/deps/v8/src/inspector/string-util.cc index 31b2db572d..95d4247d14 100644 --- a/deps/v8/src/inspector/string-util.cc +++ b/deps/v8/src/inspector/string-util.cc @@ -4,7 +4,9 @@ #include "src/inspector/string-util.h" +#include "src/conversions.h" #include "src/inspector/protocol/Protocol.h" +#include "src/unicode-cache.h" namespace v8_inspector { @@ -92,6 +94,16 @@ bool stringViewStartsWith(const StringView& string, const char* prefix) { namespace protocol { +// static +double StringUtil::toDouble(const char* s, size_t len, bool* isOk) { + v8::internal::UnicodeCache unicode_cache; + int flags = v8::internal::ALLOW_HEX | v8::internal::ALLOW_OCTAL | + v8::internal::ALLOW_BINARY; + double result = StringToDouble(&unicode_cache, s, flags); + *isOk = !std::isnan(result); + return result; +} + std::unique_ptr<protocol::Value> StringUtil::parseJSON( const StringView& string) { if (!string.length()) return nullptr; diff --git a/deps/v8/src/inspector/string-util.h b/deps/v8/src/inspector/string-util.h index 6f0e3d5ff5..134ff425e1 100644 --- a/deps/v8/src/inspector/string-util.h +++ b/deps/v8/src/inspector/string-util.h @@ -32,6 +32,7 @@ class StringUtil { return String::fromInteger(number); } static String fromDouble(double number) { return String::fromDouble(number); } + static double toDouble(const char* s, size_t len, bool* isOk); static size_t find(const String& s, const char* needle) { return s.find(needle); } diff --git a/deps/v8/src/inspector/test-interface.cc b/deps/v8/src/inspector/test-interface.cc index ead1dc3b81..6167f8be27 100644 --- a/deps/v8/src/inspector/test-interface.cc +++ b/deps/v8/src/inspector/test-interface.cc @@ -15,4 +15,10 @@ void SetMaxAsyncTaskStacksForTest(V8Inspector* inspector, int limit) { ->setMaxAsyncTaskStacksForTest(limit); } -} // v8_inspector +void DumpAsyncTaskStacksStateForTest(V8Inspector* inspector) { + static_cast<V8InspectorImpl*>(inspector) + ->debugger() + ->dumpAsyncTaskStacksStateForTest(); +} + +} // namespace v8_inspector diff --git a/deps/v8/src/inspector/test-interface.h b/deps/v8/src/inspector/test-interface.h index 98bedc2786..70fbca186f 100644 --- a/deps/v8/src/inspector/test-interface.h +++ b/deps/v8/src/inspector/test-interface.h @@ -12,6 +12,7 @@ namespace v8_inspector { class V8Inspector; V8_EXPORT void SetMaxAsyncTaskStacksForTest(V8Inspector* inspector, int limit); +V8_EXPORT void DumpAsyncTaskStacksStateForTest(V8Inspector* inspector); } // v8_inspector diff --git a/deps/v8/src/inspector/v8-console.cc b/deps/v8/src/inspector/v8-console.cc index 69e58dfabd..0d3c03a4da 100644 --- a/deps/v8/src/inspector/v8-console.cc +++ b/deps/v8/src/inspector/v8-console.cc @@ -25,11 +25,11 @@ namespace { class ConsoleHelper { public: - explicit ConsoleHelper(const v8::FunctionCallbackInfo<v8::Value>& info, - V8InspectorImpl* inspector) + ConsoleHelper(const v8::debug::ConsoleCallArguments& info, + V8InspectorImpl* inspector) : m_info(info), - m_isolate(info.GetIsolate()), - m_context(info.GetIsolate()->GetCurrentContext()), + m_isolate(inspector->isolate()), + m_context(m_isolate->GetCurrentContext()), m_inspector(inspector), m_contextId(InspectedContext::contextId(m_context)), m_groupId(m_inspector->contextGroupId(m_contextId)) {} @@ -145,7 +145,7 @@ class ConsoleHelper { } private: - const v8::FunctionCallbackInfo<v8::Value>& m_info; + const v8::debug::ConsoleCallArguments& m_info; v8::Isolate* m_isolate; v8::Local<v8::Context> m_context; V8InspectorImpl* m_inspector = nullptr; @@ -190,66 +190,63 @@ void createBoundFunctionProperty(v8::Local<v8::Context> context, V8Console::V8Console(V8InspectorImpl* inspector) : m_inspector(inspector) {} -void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Debug(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kDebug); } -void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Error(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kError); } -void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Info(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kInfo); } -void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Log(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kLog); } -void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Warn(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kWarning); } -void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Dir(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kDir); } -void V8Console::dirxmlCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::DirXml(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kDirXML); } -void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Table(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector).reportCall(ConsoleAPIType::kTable); } -void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Trace(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector) .reportCallWithDefaultArgument(ConsoleAPIType::kTrace, String16("console.trace")); } -void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Group(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector) .reportCallWithDefaultArgument(ConsoleAPIType::kStartGroup, String16("console.group")); } -void V8Console::groupCollapsedCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::GroupCollapsed(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector) .reportCallWithDefaultArgument(ConsoleAPIType::kStartGroupCollapsed, String16("console.groupCollapsed")); } -void V8Console::groupEndCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::GroupEnd(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector) .reportCallWithDefaultArgument(ConsoleAPIType::kEndGroup, String16("console.groupEnd")); } -void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Clear(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper helper(info, m_inspector); if (!helper.groupId()) return; m_inspector->client()->consoleClear(helper.groupId()); @@ -257,13 +254,13 @@ void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { String16("console.clear")); } -void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Count(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper helper(info, m_inspector); String16 title = helper.firstArgToString(String16()); String16 identifier; if (title.isEmpty()) { std::unique_ptr<V8StackTraceImpl> stackTrace = - V8StackTraceImpl::capture(m_inspector->debugger(), 0, 1); + V8StackTraceImpl::capture(m_inspector->debugger(), helper.groupId(), 1); if (stackTrace && !stackTrace->isEmpty()) { identifier = toString16(stackTrace->topSourceURL()) + ":" + String16::fromInteger(stackTrace->topLineNumber()); @@ -280,16 +277,15 @@ void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { title.isEmpty() ? countString : (title + ": " + countString)); } -void V8Console::assertCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Assert(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper helper(info, m_inspector); - if (helper.firstArgToBoolean(false)) return; + DCHECK(!helper.firstArgToBoolean(false)); std::vector<v8::Local<v8::Value>> arguments; for (int i = 1; i < info.Length(); ++i) arguments.push_back(info[i]); if (info.Length() < 2) arguments.push_back( - toV8String(info.GetIsolate(), String16("console.assert"))); + toV8String(m_inspector->isolate(), String16("console.assert"))); helper.reportCall(ConsoleAPIType::kAssert, arguments); if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent()) @@ -297,31 +293,28 @@ void V8Console::assertCallback( protocol::Debugger::Paused::ReasonEnum::Assert, nullptr); } -void V8Console::markTimelineCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::MarkTimeline(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector) .reportDeprecatedCall("V8Console#markTimelineDeprecated", "'console.markTimeline' is " "deprecated. Please use " "'console.timeStamp' instead."); - timeStampCallback(info); + TimeStamp(info); } -void V8Console::profileCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Profile(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper helper(info, m_inspector); if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) profilerAgent->consoleProfile(helper.firstArgToString(String16())); } -void V8Console::profileEndCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::ProfileEnd(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper helper(info, m_inspector); if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) profilerAgent->consoleProfileEnd(helper.firstArgToString(String16())); } -static void timeFunction(const v8::FunctionCallbackInfo<v8::Value>& info, +static void timeFunction(const v8::debug::ConsoleCallArguments& info, bool timelinePrefix, V8InspectorImpl* inspector) { ConsoleHelper helper(info, inspector); String16 protocolTitle = helper.firstArgToString("default"); @@ -330,7 +323,7 @@ static void timeFunction(const v8::FunctionCallbackInfo<v8::Value>& info, helper.consoleMessageStorage()->time(helper.contextId(), protocolTitle); } -static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info, +static void timeEndFunction(const v8::debug::ConsoleCallArguments& info, bool timelinePrefix, V8InspectorImpl* inspector) { ConsoleHelper helper(info, inspector); String16 protocolTitle = helper.firstArgToString("default"); @@ -343,8 +336,7 @@ static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info, helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message); } -void V8Console::timelineCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Timeline(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector) .reportDeprecatedCall("V8Console#timeline", "'console.timeline' is deprecated. Please use " @@ -352,8 +344,7 @@ void V8Console::timelineCallback( timeFunction(info, true, m_inspector); } -void V8Console::timelineEndCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::TimelineEnd(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper(info, m_inspector) .reportDeprecatedCall("V8Console#timelineEnd", "'console.timelineEnd' is " @@ -362,17 +353,15 @@ void V8Console::timelineEndCallback( timeEndFunction(info, true, m_inspector); } -void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::Time(const v8::debug::ConsoleCallArguments& info) { timeFunction(info, false, m_inspector); } -void V8Console::timeEndCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::TimeEnd(const v8::debug::ConsoleCallArguments& info) { timeEndFunction(info, false, m_inspector); } -void V8Console::timeStampCallback( - const v8::FunctionCallbackInfo<v8::Value>& info) { +void V8Console::TimeStamp(const v8::debug::ConsoleCallArguments& info) { ConsoleHelper helper(info, m_inspector); String16 title = helper.firstArgToString(String16()); m_inspector->client()->consoleTimeStamp(toStringView(title)); @@ -400,7 +389,8 @@ void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); info.GetReturnValue().Set(v8::Array::New(isolate)); - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); v8::Local<v8::Object> obj; if (!helper.firstArgAsObject().ToLocal(&obj)) return; v8::Local<v8::Array> names; @@ -414,7 +404,8 @@ void V8Console::valuesCallback( v8::Isolate* isolate = info.GetIsolate(); info.GetReturnValue().Set(v8::Array::New(isolate)); - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); v8::Local<v8::Object> obj; if (!helper.firstArgAsObject().ToLocal(&obj)) return; v8::Local<v8::Array> names; @@ -453,7 +444,8 @@ static void setFunctionBreakpoint(ConsoleHelper& helper, void V8Console::debugFunctionCallback( const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); v8::Local<v8::Function> function; if (!helper.firstArgAsFunction().ToLocal(&function)) return; setFunctionBreakpoint(helper, function, @@ -463,7 +455,8 @@ void V8Console::debugFunctionCallback( void V8Console::undebugFunctionCallback( const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); v8::Local<v8::Function> function; if (!helper.firstArgAsFunction().ToLocal(&function)) return; setFunctionBreakpoint(helper, function, @@ -473,7 +466,8 @@ void V8Console::undebugFunctionCallback( void V8Console::monitorFunctionCallback( const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); v8::Local<v8::Function> function; if (!helper.firstArgAsFunction().ToLocal(&function)) return; v8::Local<v8::Value> name = function->GetName(); @@ -496,7 +490,8 @@ void V8Console::monitorFunctionCallback( void V8Console::unmonitorFunctionCallback( const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); v8::Local<v8::Function> function; if (!helper.firstArgAsFunction().ToLocal(&function)) return; setFunctionBreakpoint(helper, function, @@ -506,7 +501,8 @@ void V8Console::unmonitorFunctionCallback( void V8Console::lastEvaluationResultCallback( const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); InjectedScript* injectedScript = helper.injectedScript(); if (!injectedScript) return; info.GetReturnValue().Set(injectedScript->lastEvaluationResult()); @@ -517,7 +513,8 @@ static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info, if (info.Length() < 1) return; if (!copyToClipboard) info.GetReturnValue().Set(info[0]); - ConsoleHelper helper(info, inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, inspector); InjectedScript* injectedScript = helper.injectedScript(); if (!injectedScript) return; std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject; @@ -547,7 +544,8 @@ void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info, unsigned num) { DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize); - ConsoleHelper helper(info, m_inspector); + v8::debug::ConsoleCallArguments args(info); + ConsoleHelper helper(args, m_inspector); if (V8InspectorSessionImpl* session = helper.currentSession()) { V8InspectorSession::Inspectable* object = session->inspectedObject(num); v8::Isolate* isolate = info.GetIsolate(); @@ -558,71 +556,6 @@ void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info, } } -v8::Local<v8::Object> V8Console::createConsole(v8::Local<v8::Context> context) { - v8::Context::Scope contextScope(context); - v8::Isolate* isolate = context->GetIsolate(); - v8::MicrotasksScope microtasksScope(isolate, - v8::MicrotasksScope::kDoNotRunMicrotasks); - - v8::Local<v8::Object> console = v8::Object::New(isolate); - bool success = - console->SetPrototype(context, v8::Object::New(isolate)).FromMaybe(false); - DCHECK(success); - USE(success); - - v8::Local<v8::External> data = v8::External::New(isolate, this); - createBoundFunctionProperty(context, console, data, "debug", - &V8Console::call<&V8Console::debugCallback>); - createBoundFunctionProperty(context, console, data, "error", - &V8Console::call<&V8Console::errorCallback>); - createBoundFunctionProperty(context, console, data, "info", - &V8Console::call<&V8Console::infoCallback>); - createBoundFunctionProperty(context, console, data, "log", - &V8Console::call<&V8Console::logCallback>); - createBoundFunctionProperty(context, console, data, "warn", - &V8Console::call<&V8Console::warnCallback>); - createBoundFunctionProperty(context, console, data, "dir", - &V8Console::call<&V8Console::dirCallback>); - createBoundFunctionProperty(context, console, data, "dirxml", - &V8Console::call<&V8Console::dirxmlCallback>); - createBoundFunctionProperty(context, console, data, "table", - &V8Console::call<&V8Console::tableCallback>); - createBoundFunctionProperty(context, console, data, "trace", - &V8Console::call<&V8Console::traceCallback>); - createBoundFunctionProperty(context, console, data, "group", - &V8Console::call<&V8Console::groupCallback>); - createBoundFunctionProperty( - context, console, data, "groupCollapsed", - &V8Console::call<&V8Console::groupCollapsedCallback>); - createBoundFunctionProperty(context, console, data, "groupEnd", - &V8Console::call<&V8Console::groupEndCallback>); - createBoundFunctionProperty(context, console, data, "clear", - &V8Console::call<&V8Console::clearCallback>); - createBoundFunctionProperty(context, console, data, "count", - &V8Console::call<&V8Console::countCallback>); - createBoundFunctionProperty(context, console, data, "assert", - &V8Console::call<&V8Console::assertCallback>); - createBoundFunctionProperty( - context, console, data, "markTimeline", - &V8Console::call<&V8Console::markTimelineCallback>); - createBoundFunctionProperty(context, console, data, "profile", - &V8Console::call<&V8Console::profileCallback>); - createBoundFunctionProperty(context, console, data, "profileEnd", - &V8Console::call<&V8Console::profileEndCallback>); - createBoundFunctionProperty(context, console, data, "timeline", - &V8Console::call<&V8Console::timelineCallback>); - createBoundFunctionProperty( - context, console, data, "timelineEnd", - &V8Console::call<&V8Console::timelineEndCallback>); - createBoundFunctionProperty(context, console, data, "time", - &V8Console::call<&V8Console::timeCallback>); - createBoundFunctionProperty(context, console, data, "timeEnd", - &V8Console::call<&V8Console::timeEndCallback>); - createBoundFunctionProperty(context, console, data, "timeStamp", - &V8Console::call<&V8Console::timeStampCallback>); - return console; -} - void V8Console::installMemoryGetter(v8::Local<v8::Context> context, v8::Local<v8::Object> console) { v8::Isolate* isolate = context->GetIsolate(); @@ -654,24 +587,24 @@ v8::Local<v8::Object> V8Console::createCommandLineAPI( v8::Local<v8::External> data = v8::External::New(isolate, this); createBoundFunctionProperty(context, commandLineAPI, data, "dir", - &V8Console::call<&V8Console::dirCallback>, + &V8Console::call<&V8Console::Dir>, "function dir(value) { [Command Line API] }"); createBoundFunctionProperty(context, commandLineAPI, data, "dirxml", - &V8Console::call<&V8Console::dirxmlCallback>, + &V8Console::call<&V8Console::DirXml>, "function dirxml(value) { [Command Line API] }"); createBoundFunctionProperty(context, commandLineAPI, data, "profile", - &V8Console::call<&V8Console::profileCallback>, + &V8Console::call<&V8Console::Profile>, "function profile(title) { [Command Line API] }"); createBoundFunctionProperty( context, commandLineAPI, data, "profileEnd", - &V8Console::call<&V8Console::profileEndCallback>, + &V8Console::call<&V8Console::ProfileEnd>, "function profileEnd(title) { [Command Line API] }"); createBoundFunctionProperty(context, commandLineAPI, data, "clear", - &V8Console::call<&V8Console::clearCallback>, + &V8Console::call<&V8Console::Clear>, "function clear() { [Command Line API] }"); createBoundFunctionProperty( context, commandLineAPI, data, "table", - &V8Console::call<&V8Console::tableCallback>, + &V8Console::call<&V8Console::Table>, "function table(data, [columns]) { [Command Line API] }"); createBoundFunctionProperty(context, commandLineAPI, data, "keys", diff --git a/deps/v8/src/inspector/v8-console.h b/deps/v8/src/inspector/v8-console.h index 0b833b3c94..e31133c4e1 100644 --- a/deps/v8/src/inspector/v8-console.h +++ b/deps/v8/src/inspector/v8-console.h @@ -8,6 +8,7 @@ #include "src/base/macros.h" #include "include/v8.h" +#include "src/debug/interface-types.h" namespace v8_inspector { @@ -16,9 +17,8 @@ class V8InspectorImpl; // Console API // https://console.spec.whatwg.org/#console-interface -class V8Console { +class V8Console : public v8::debug::ConsoleDelegate { public: - v8::Local<v8::Object> createConsole(v8::Local<v8::Context> context); v8::Local<v8::Object> createCommandLineAPI(v8::Local<v8::Context> context); void installMemoryGetter(v8::Local<v8::Context> context, v8::Local<v8::Object> console); @@ -49,29 +49,29 @@ class V8Console { explicit V8Console(V8InspectorImpl* inspector); private: - void debugCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void errorCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void infoCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void logCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void warnCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void dirCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void tableCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void traceCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void groupCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void groupCollapsedCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void clearCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void countCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void assertCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void profileCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void profileEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void timelineCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void timeCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); - void timeStampCallback(const v8::FunctionCallbackInfo<v8::Value>&); + void Debug(const v8::debug::ConsoleCallArguments&) override; + void Error(const v8::debug::ConsoleCallArguments&) override; + void Info(const v8::debug::ConsoleCallArguments&) override; + void Log(const v8::debug::ConsoleCallArguments&) override; + void Warn(const v8::debug::ConsoleCallArguments&) override; + void Dir(const v8::debug::ConsoleCallArguments&) override; + void DirXml(const v8::debug::ConsoleCallArguments&) override; + void Table(const v8::debug::ConsoleCallArguments&) override; + void Trace(const v8::debug::ConsoleCallArguments&) override; + void Group(const v8::debug::ConsoleCallArguments&) override; + void GroupCollapsed(const v8::debug::ConsoleCallArguments&) override; + void GroupEnd(const v8::debug::ConsoleCallArguments&) override; + void Clear(const v8::debug::ConsoleCallArguments&) override; + void Count(const v8::debug::ConsoleCallArguments&) override; + void Assert(const v8::debug::ConsoleCallArguments&) override; + void MarkTimeline(const v8::debug::ConsoleCallArguments&) override; + void Profile(const v8::debug::ConsoleCallArguments&) override; + void ProfileEnd(const v8::debug::ConsoleCallArguments&) override; + void Timeline(const v8::debug::ConsoleCallArguments&) override; + void TimelineEnd(const v8::debug::ConsoleCallArguments&) override; + void Time(const v8::debug::ConsoleCallArguments&) override; + void TimeEnd(const v8::debug::ConsoleCallArguments&) override; + void TimeStamp(const v8::debug::ConsoleCallArguments&) override; template <void (V8Console::*func)(const v8::FunctionCallbackInfo<v8::Value>&)> static void call(const v8::FunctionCallbackInfo<v8::Value>& info) { @@ -79,6 +79,13 @@ class V8Console { static_cast<V8Console*>(info.Data().As<v8::External>()->Value()); (console->*func)(info); } + template <void (V8Console::*func)(const v8::debug::ConsoleCallArguments&)> + static void call(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8Console* console = + static_cast<V8Console*>(info.Data().As<v8::External>()->Value()); + v8::debug::ConsoleCallArguments args(info); + (console->*func)(args); + } // TODO(foolip): There is no spec for the Memory Info API, see blink-dev: // https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ diff --git a/deps/v8/src/inspector/v8-debugger-agent-impl.cc b/deps/v8/src/inspector/v8-debugger-agent-impl.cc index 293c2d43c7..7b03c96c0a 100644 --- a/deps/v8/src/inspector/v8-debugger-agent-impl.cc +++ b/deps/v8/src/inspector/v8-debugger-agent-impl.cc @@ -34,7 +34,6 @@ using protocol::Debugger::BreakpointId; using protocol::Debugger::CallFrame; using protocol::Runtime::ExceptionDetails; using protocol::Runtime::ScriptId; -using protocol::Runtime::StackTrace; using protocol::Runtime::RemoteObject; namespace DebuggerAgentState { @@ -238,7 +237,7 @@ Response V8DebuggerAgentImpl::disable() { v8::debug::NoBreakOnException); m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0); - if (isPaused()) m_debugger->continueProgram(); + if (isPaused()) m_debugger->continueProgram(m_session->contextGroupId()); m_debugger->disable(); JavaScriptCallFrames emptyCallFrames; m_pausedCallFrames.swap(emptyCallFrames); @@ -248,7 +247,6 @@ Response V8DebuggerAgentImpl::disable() { m_scripts.clear(); m_breakpointIdToDebuggerBreakpointIds.clear(); m_debugger->setAsyncCallStackDepth(this, 0); - m_continueToLocationBreakpointId = String16(); clearBreakDetails(); m_skipAllPauses = false; m_state->setBoolean(DebuggerAgentState::skipAllPauses, false); @@ -457,11 +455,15 @@ Response V8DebuggerAgentImpl::getPossibleBreakpoints( } auto it = m_scripts.find(scriptId); if (it == m_scripts.end()) return Response::Error("Script not found"); - std::vector<v8::debug::BreakLocation> v8Locations; - if (!it->second->getPossibleBreakpoints( - v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations)) { - return Response::InternalError(); + { + v8::HandleScope handleScope(m_isolate); + v8::Local<v8::Context> debuggerContext = + v8::debug::GetDebugContext(m_isolate); + v8::Context::Scope contextScope(debuggerContext); + v8::TryCatch tryCatch(m_isolate); + it->second->getPossibleBreakpoints( + v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations); } *locations = protocol::Array<protocol::Debugger::BreakLocation>::create(); @@ -481,21 +483,14 @@ Response V8DebuggerAgentImpl::getPossibleBreakpoints( } Response V8DebuggerAgentImpl::continueToLocation( - std::unique_ptr<protocol::Debugger::Location> location) { + std::unique_ptr<protocol::Debugger::Location> location, + Maybe<String16> targetCallFrames) { if (!enabled()) return Response::Error(kDebuggerNotEnabled); - if (!m_continueToLocationBreakpointId.isEmpty()) { - m_debugger->removeBreakpoint(m_continueToLocationBreakpointId); - m_continueToLocationBreakpointId = ""; - } - - ScriptBreakpoint breakpoint(location->getScriptId(), - location->getLineNumber(), - location->getColumnNumber(0), String16()); - - m_continueToLocationBreakpointId = m_debugger->setBreakpoint( - breakpoint, &breakpoint.line_number, &breakpoint.column_number); - // TODO(kozyatinskiy): Return actual line and column number. - return resume(); + if (!isPaused()) return Response::Error(kDebuggerNotPaused); + return m_debugger->continueToLocation( + m_session->contextGroupId(), std::move(location), + targetCallFrames.fromMaybe( + protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any)); } bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId, @@ -598,7 +593,8 @@ Response V8DebuggerAgentImpl::searchInContent( Response V8DebuggerAgentImpl::setScriptSource( const String16& scriptId, const String16& newContent, Maybe<bool> dryRun, Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames, - Maybe<bool>* stackChanged, Maybe<StackTrace>* asyncStackTrace, + Maybe<bool>* stackChanged, + Maybe<protocol::Runtime::StackTrace>* asyncStackTrace, Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) { if (!enabled()) return Response::Error(kDebuggerNotEnabled); @@ -631,7 +627,7 @@ Response V8DebuggerAgentImpl::setScriptSource( Response V8DebuggerAgentImpl::restartFrame( const String16& callFrameId, std::unique_ptr<Array<CallFrame>>* newCallFrames, - Maybe<StackTrace>* asyncStackTrace) { + Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) { if (!isPaused()) return Response::Error(kDebuggerNotPaused); InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(), callFrameId); @@ -715,7 +711,7 @@ Response V8DebuggerAgentImpl::pause() { Response V8DebuggerAgentImpl::resume() { if (!isPaused()) return Response::Error(kDebuggerNotPaused); m_session->releaseObjectGroup(kBacktraceObjectGroup); - m_debugger->continueProgram(); + m_debugger->continueProgram(m_session->contextGroupId()); return Response::OK(); } @@ -1028,11 +1024,14 @@ Response V8DebuggerAgentImpl::currentCallFrames( return Response::OK(); } -std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() { - if (!isPaused()) return nullptr; - V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain(); - return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger) - : nullptr; +std::unique_ptr<protocol::Runtime::StackTrace> +V8DebuggerAgentImpl::currentAsyncStackTrace() { + std::shared_ptr<AsyncStackTrace> asyncParent = + m_debugger->currentAsyncParent(); + if (!asyncParent) return nullptr; + return asyncParent->buildInspectorObject( + m_debugger->currentAsyncCreation().get(), + m_debugger->maxAsyncCallChainDepth() - 1); } bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); } @@ -1226,11 +1225,6 @@ void V8DebuggerAgentImpl::didPause(int contextId, m_frontend.paused(std::move(protocolCallFrames), breakReason, std::move(breakAuxData), std::move(hitBreakpointIds), currentAsyncStackTrace()); - - if (!m_continueToLocationBreakpointId.isEmpty()) { - m_debugger->removeBreakpoint(m_continueToLocationBreakpointId); - m_continueToLocationBreakpointId = ""; - } } void V8DebuggerAgentImpl::didContinue() { diff --git a/deps/v8/src/inspector/v8-debugger-agent-impl.h b/deps/v8/src/inspector/v8-debugger-agent-impl.h index 5fa251ca98..c9433e20f6 100644 --- a/deps/v8/src/inspector/v8-debugger-agent-impl.h +++ b/deps/v8/src/inspector/v8-debugger-agent-impl.h @@ -23,7 +23,6 @@ class V8DebuggerScript; class V8InspectorImpl; class V8InspectorSessionImpl; class V8Regex; -class V8StackTraceImpl; using protocol::Maybe; using protocol::Response; @@ -57,8 +56,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { Maybe<String16> optionalCondition, String16*, std::unique_ptr<protocol::Debugger::Location>* actualLocation) override; Response removeBreakpoint(const String16& breakpointId) override; - Response continueToLocation( - std::unique_ptr<protocol::Debugger::Location>) override; + Response continueToLocation(std::unique_ptr<protocol::Debugger::Location>, + Maybe<String16> targetCallFrames) override; Response searchInContent( const String16& scriptId, const String16& query, Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex, @@ -185,7 +184,6 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend { ScriptsMap m_scripts; BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds; DebugServerBreakpointToBreakpointIdAndSourceMap m_serverBreakpoints; - String16 m_continueToLocationBreakpointId; using BreakReason = std::pair<String16, std::unique_ptr<protocol::DictionaryValue>>; diff --git a/deps/v8/src/inspector/v8-debugger-script.cc b/deps/v8/src/inspector/v8-debugger-script.cc index d151ab821f..e0aaceb888 100644 --- a/deps/v8/src/inspector/v8-debugger-script.cc +++ b/deps/v8/src/inspector/v8-debugger-script.cc @@ -138,11 +138,7 @@ class ActualScript : public V8DebuggerScript { m_endColumn = m_startColumn; } - v8::Local<v8::Value> contextData; - if (script->ContextData().ToLocal(&contextData) && contextData->IsInt32()) { - m_executionContextId = - static_cast<int>(contextData.As<v8::Int32>()->Value()); - } + USE(script->ContextId().To(&m_executionContextId)); if (script->Source().ToLocal(&tmp)) { m_source = toProtocolString(tmp); diff --git a/deps/v8/src/inspector/v8-debugger.cc b/deps/v8/src/inspector/v8-debugger.cc index 87c864cd38..86a48401a6 100644 --- a/deps/v8/src/inspector/v8-debugger.cc +++ b/deps/v8/src/inspector/v8-debugger.cc @@ -21,9 +21,6 @@ namespace v8_inspector { namespace { -// 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) { @@ -32,11 +29,8 @@ inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* 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 contextId; + if (!script->ContextId().To(&contextId)) return nullptr; int contextGroupId = inspector->contextGroupId(contextId); if (!contextGroupId) return nullptr; return inspector->enabledDebuggerAgentForGroup(contextGroupId); @@ -136,6 +130,17 @@ v8::MaybeLocal<v8::Object> generatorObjectLocation( suspendedLocation.GetColumnNumber()); } +template <typename Map> +void cleanupExpiredWeakPointers(Map& map) { + for (auto it = map.begin(); it != map.end();) { + if (it->second.expired()) { + it = map.erase(it); + } else { + ++it; + } + } +} + } // namespace static bool inLiveEditScope = false; @@ -164,10 +169,8 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector) m_inspector(inspector), m_enableCount(0), m_breakpointsActivated(true), - m_runningNestedMessageLoop(false), m_ignoreScriptParsedEventsCounter(0), m_maxAsyncCallStacks(kMaxAsyncTaskStacks), - m_lastTaskId(0), m_maxAsyncCallStackDepth(0), m_pauseOnExceptionsState(v8::debug::NoBreakOnException), m_wasmTranslation(isolate) {} @@ -191,6 +194,7 @@ void V8Debugger::disable() { if (--m_enableCount) return; DCHECK(enabled()); clearBreakpoints(); + clearContinueToLocation(); m_debuggerScript.Reset(); m_debuggerContext.Reset(); allAsyncTasksCanceled(); @@ -212,10 +216,12 @@ void V8Debugger::getCompiledScripts( for (size_t i = 0; i < scripts.Size(); ++i) { v8::Local<v8::debug::Script> script = scripts.Get(i); if (!script->WasCompiled()) continue; - v8::Local<v8::Value> contextData; - if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) + if (script->IsEmbedded()) { + result.push_back(V8DebuggerScript::Create(m_isolate, script, false)); continue; - int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); + } + int contextId; + if (!script->ContextId().To(&contextId)) continue; if (m_inspector->contextGroupId(contextId) != contextGroupId) continue; result.push_back(V8DebuggerScript::Create(m_isolate, script, false)); } @@ -358,7 +364,8 @@ bool V8Debugger::breakProgram(int targetContextGroupId) { return m_inspector->enabledDebuggerAgentForGroup(targetContextGroupId); } -void V8Debugger::continueProgram() { +void V8Debugger::continueProgram(int targetContextGroupId) { + if (m_pausedContextGroupId != targetContextGroupId) return; if (isPaused()) m_inspector->client()->quitMessageLoopOnPause(); m_pausedContext.Clear(); m_executionState.Clear(); @@ -370,7 +377,7 @@ void V8Debugger::stepIntoStatement(int targetContextGroupId) { DCHECK(targetContextGroupId); m_targetContextGroupId = targetContextGroupId; v8::debug::PrepareStep(m_isolate, v8::debug::StepIn); - continueProgram(); + continueProgram(targetContextGroupId); } void V8Debugger::stepOverStatement(int targetContextGroupId) { @@ -379,7 +386,7 @@ void V8Debugger::stepOverStatement(int targetContextGroupId) { DCHECK(targetContextGroupId); m_targetContextGroupId = targetContextGroupId; v8::debug::PrepareStep(m_isolate, v8::debug::StepNext); - continueProgram(); + continueProgram(targetContextGroupId); } void V8Debugger::stepOutOfFunction(int targetContextGroupId) { @@ -388,7 +395,7 @@ void V8Debugger::stepOutOfFunction(int targetContextGroupId) { DCHECK(targetContextGroupId); m_targetContextGroupId = targetContextGroupId; v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); - continueProgram(); + continueProgram(targetContextGroupId); } void V8Debugger::scheduleStepIntoAsync( @@ -405,6 +412,58 @@ void V8Debugger::scheduleStepIntoAsync( m_stepIntoAsyncCallback = std::move(callback); } +Response V8Debugger::continueToLocation( + int targetContextGroupId, + std::unique_ptr<protocol::Debugger::Location> location, + const String16& targetCallFrames) { + DCHECK(isPaused()); + DCHECK(!m_executionState.IsEmpty()); + DCHECK(targetContextGroupId); + m_targetContextGroupId = targetContextGroupId; + ScriptBreakpoint breakpoint(location->getScriptId(), + location->getLineNumber(), + location->getColumnNumber(0), String16()); + int lineNumber = 0; + int columnNumber = 0; + m_continueToLocationBreakpointId = + setBreakpoint(breakpoint, &lineNumber, &columnNumber); + if (!m_continueToLocationBreakpointId.isEmpty()) { + m_continueToLocationTargetCallFrames = targetCallFrames; + if (m_continueToLocationTargetCallFrames != + protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) { + m_continueToLocationStack = captureStackTrace(true); + DCHECK(m_continueToLocationStack); + } + continueProgram(targetContextGroupId); + // TODO(kozyatinskiy): Return actual line and column number. + return Response::OK(); + } else { + return Response::Error("Cannot continue to specified location"); + } +} + +bool V8Debugger::shouldContinueToCurrentLocation() { + if (m_continueToLocationTargetCallFrames == + protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) { + return true; + } + std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true); + if (m_continueToLocationTargetCallFrames == + protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) { + return m_continueToLocationStack->isEqualIgnoringTopFrame( + currentStack.get()); + } + return true; +} + +void V8Debugger::clearContinueToLocation() { + if (m_continueToLocationBreakpointId.isEmpty()) return; + removeBreakpoint(m_continueToLocationBreakpointId); + m_continueToLocationBreakpointId = String16(); + m_continueToLocationTargetCallFrames = String16(); + m_continueToLocationStack.reset(); +} + Response V8Debugger::setScriptSource( const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails, @@ -560,11 +619,17 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, breakpointIds.push_back(String16::fromInteger( hitBreakpointNumber->Int32Value(debuggerContext()).FromJust())); } + if (breakpointIds.size() == 1 && + breakpointIds[0] == m_continueToLocationBreakpointId) { + v8::Context::Scope contextScope(pausedContext); + if (!shouldContinueToCurrentLocation()) return; + } } + clearContinueToLocation(); m_pausedContext = pausedContext; m_executionState = executionState; - m_runningNestedMessageLoop = true; + m_pausedContextGroupId = contextGroupId; agent->didPause(InspectedContext::contextId(pausedContext), exception, breakpointIds, isPromiseRejection, isUncaught, m_scheduledOOMBreak); @@ -576,7 +641,7 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, CHECK(!context.IsEmpty() && context != v8::debug::GetDebugContext(m_isolate)); m_inspector->client()->runMessageLoopOnPause(groupId); - m_runningNestedMessageLoop = false; + m_pausedContextGroupId = 0; } // The agent may have been removed in the nested loop. agent = m_inspector->enabledDebuggerAgentForGroup(groupId); @@ -643,8 +708,7 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script, end); } -void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, - v8::debug::PromiseDebugActionType type, +void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, int parentId, bool createdByUser) { // Async task events from Promises are given misaligned pointers to prevent @@ -655,10 +719,7 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, switch (type) { case v8::debug::kDebugPromiseCreated: asyncTaskCreatedForStack(task, parentTask); - if (createdByUser && parentTask) { - v8::Context::Scope contextScope(context); - asyncTaskCandidateForStepping(task); - } + if (createdByUser && parentTask) asyncTaskCandidateForStepping(task); break; case v8::debug::kDebugEnqueueAsyncFunction: asyncTaskScheduledForStack("async function", task, true); @@ -669,10 +730,6 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, case v8::debug::kDebugEnqueuePromiseReject: asyncTaskScheduledForStack("Promise.reject", task, true); break; - case v8::debug::kDebugPromiseCollected: - asyncTaskCanceledForStack(task); - asyncTaskCanceledForStepping(task); - break; case v8::debug::kDebugWillHandle: asyncTaskStartedForStack(task); asyncTaskStartedForStepping(task); @@ -684,9 +741,16 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, } } -V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { - if (!m_currentStacks.size()) return nullptr; - return m_currentStacks.back().get(); +std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() { + // TODO(kozyatinskiy): implement creation chain as parent without hack. + if (!m_currentAsyncCreation.empty() && m_currentAsyncCreation.back()) { + return m_currentAsyncCreation.back(); + } + return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back(); +} + +std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() { + return nullptr; } void V8Debugger::compileDebuggerScript() { @@ -827,8 +891,8 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( } std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace( - v8::Local<v8::StackTrace> stackTrace) { - return V8StackTraceImpl::create(this, currentContextGroupId(), stackTrace, + v8::Local<v8::StackTrace> v8StackTrace) { + return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace, V8StackTraceImpl::maxCallStackSizeToCapture); } @@ -849,31 +913,19 @@ 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; - asyncTaskCanceledForStack(taskToRemove); - } -} - void V8Debugger::asyncTaskCreatedForStack(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 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. - std::unique_ptr<V8StackTraceImpl> creationStack = - V8StackTraceImpl::capture(this, 0, 1, String16()); - if (creationStack && !creationStack->isEmpty()) { - m_asyncTaskCreationStacks[task] = std::move(creationStack); - registerAsyncTaskIfNeeded(task); + std::shared_ptr<AsyncStackTrace> asyncCreation = + AsyncStackTrace::capture(this, currentContextGroupId(), String16(), + V8StackTraceImpl::maxCallStackSizeToCapture); + // Passing one as maxStackSize forces no async chain for the new stack. + if (asyncCreation && !asyncCreation->isEmpty()) { + m_asyncTaskCreationStacks[task] = asyncCreation; + m_allAsyncStacks.push_back(std::move(asyncCreation)); + ++m_asyncStacksCount; + collectOldAsyncStacksIfNeeded(); } } @@ -902,13 +954,15 @@ 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( - this, currentContextGroupId(), - V8StackTraceImpl::maxCallStackSizeToCapture, taskName); - if (chain) { - m_asyncTaskStacks[task] = std::move(chain); + std::shared_ptr<AsyncStackTrace> asyncStack = + AsyncStackTrace::capture(this, currentContextGroupId(), taskName, + V8StackTraceImpl::maxCallStackSizeToCapture); + if (asyncStack) { + m_asyncTaskStacks[task] = asyncStack; if (recurring) m_recurringTasks.insert(task); - registerAsyncTaskIfNeeded(task); + m_allAsyncStacks.push_back(std::move(asyncStack)); + ++m_asyncStacksCount; + collectOldAsyncStacksIfNeeded(); } } @@ -918,18 +972,10 @@ void V8Debugger::asyncTaskCanceledForStack(void* 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::asyncTaskStartedForStack(void* task) { if (!m_maxAsyncCallStackDepth) return; - m_currentTasks.push_back(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 --> @@ -937,25 +983,40 @@ void V8Debugger::asyncTaskStartedForStack(void* task) { // - asyncTaskCanceled <-- canceled before finished // <-- async stack requested here --> // - asyncTaskFinished - std::unique_ptr<V8StackTraceImpl> stack; - if (stackIt != m_asyncTaskStacks.end() && stackIt->second) - stack = stackIt->second->cloneImpl(); + m_currentTasks.push_back(task); + auto parentIt = m_parentTask.find(task); + AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find( + parentIt == m_parentTask.end() ? task : parentIt->second); + if (stackIt != m_asyncTaskStacks.end()) { + m_currentAsyncParent.push_back(stackIt->second.lock()); + } else { + m_currentAsyncParent.emplace_back(); + } auto itCreation = m_asyncTaskCreationStacks.find(task); - if (stack && itCreation != m_asyncTaskCreationStacks.end()) { - stack->setCreation(itCreation->second->cloneImpl()); + if (itCreation != m_asyncTaskCreationStacks.end()) { + m_currentAsyncCreation.push_back(itCreation->second.lock()); + // TODO(kozyatinskiy): implement it without hack. + if (m_currentAsyncParent.back()) { + m_currentAsyncCreation.back()->setDescription( + m_currentAsyncParent.back()->description()); + m_currentAsyncParent.back().reset(); + } + } else { + m_currentAsyncCreation.emplace_back(); } - m_currentStacks.push_back(std::move(stack)); } 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; - + if (!m_currentTasks.size()) return; DCHECK(m_currentTasks.back() == task); m_currentTasks.pop_back(); - m_currentStacks.pop_back(); + DCHECK(m_currentAsyncParent.size() == m_currentAsyncCreation.size()); + m_currentAsyncParent.pop_back(); + m_currentAsyncCreation.pop_back(); + if (m_recurringTasks.find(task) == m_recurringTasks.end()) { asyncTaskCanceledForStack(task); } @@ -992,13 +1053,15 @@ void V8Debugger::asyncTaskCanceledForStepping(void* task) { void V8Debugger::allAsyncTasksCanceled() { m_asyncTaskStacks.clear(); m_recurringTasks.clear(); - m_currentStacks.clear(); + m_currentAsyncParent.clear(); + m_currentAsyncCreation.clear(); m_currentTasks.clear(); m_parentTask.clear(); m_asyncTaskCreationStacks.clear(); - m_idToTask.clear(); - m_taskToId.clear(); - m_lastTaskId = 0; + + m_framesCache.clear(); + m_allAsyncStacks.clear(); + m_asyncStacksCount = 0; } void V8Debugger::muteScriptParsedEvents() { @@ -1018,11 +1081,10 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace( int contextGroupId = currentContextGroupId(); if (!contextGroupId) return nullptr; - size_t stackSize = - fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1; - if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) + int stackSize = 1; + if (fullStack || m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) { stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; - + } return V8StackTraceImpl::capture(this, contextGroupId, stackSize); } @@ -1031,4 +1093,68 @@ int V8Debugger::currentContextGroupId() { return m_inspector->contextGroupId(m_isolate->GetCurrentContext()); } +void V8Debugger::collectOldAsyncStacksIfNeeded() { + if (m_asyncStacksCount <= m_maxAsyncCallStacks) return; + int halfOfLimitRoundedUp = + m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2; + while (m_asyncStacksCount > halfOfLimitRoundedUp) { + m_allAsyncStacks.pop_front(); + --m_asyncStacksCount; + } + cleanupExpiredWeakPointers(m_asyncTaskStacks); + cleanupExpiredWeakPointers(m_asyncTaskCreationStacks); + for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) { + if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) { + it = m_recurringTasks.erase(it); + } else { + ++it; + } + } + for (auto it = m_parentTask.begin(); it != m_parentTask.end();) { + if (m_asyncTaskCreationStacks.find(it->second) == + m_asyncTaskCreationStacks.end() && + m_asyncTaskStacks.find(it->second) == m_asyncTaskStacks.end()) { + it = m_parentTask.erase(it); + } else { + ++it; + } + } + cleanupExpiredWeakPointers(m_framesCache); +} + +std::shared_ptr<StackFrame> V8Debugger::symbolize( + v8::Local<v8::StackFrame> v8Frame) { + auto it = m_framesCache.end(); + int frameId = 0; + if (m_maxAsyncCallStackDepth) { + frameId = v8::debug::GetStackFrameId(v8Frame); + it = m_framesCache.find(frameId); + } + if (it != m_framesCache.end() && it->second.lock()) return it->second.lock(); + std::shared_ptr<StackFrame> frame(new StackFrame(v8Frame)); + // TODO(clemensh): Figure out a way to do this translation only right before + // sending the stack trace over wire. + if (v8Frame->IsWasm()) frame->translate(&m_wasmTranslation); + if (m_maxAsyncCallStackDepth) { + m_framesCache[frameId] = frame; + } + return frame; +} + +void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) { + m_maxAsyncCallStacks = 0; + collectOldAsyncStacksIfNeeded(); + m_maxAsyncCallStacks = limit; +} + +void V8Debugger::dumpAsyncTaskStacksStateForTest() { + fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount); + fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size()); + fprintf(stdout, "Created async tasks: %zu\n", + m_asyncTaskCreationStacks.size()); + fprintf(stdout, "Async tasks with parent: %zu\n", m_parentTask.size()); + fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size()); + fprintf(stdout, "\n"); +} + } // namespace v8_inspector diff --git a/deps/v8/src/inspector/v8-debugger.h b/deps/v8/src/inspector/v8-debugger.h index 83cc8afb0d..a15c288c0d 100644 --- a/deps/v8/src/inspector/v8-debugger.h +++ b/deps/v8/src/inspector/v8-debugger.h @@ -5,6 +5,7 @@ #ifndef V8_INSPECTOR_V8DEBUGGER_H_ #define V8_INSPECTOR_V8DEBUGGER_H_ +#include <list> #include <vector> #include "src/base/macros.h" @@ -20,7 +21,10 @@ namespace v8_inspector { +class AsyncStackTrace; struct ScriptBreakpoint; +class StackFrame; +class V8Debugger; class V8DebuggerAgentImpl; class V8InspectorImpl; class V8StackTraceImpl; @@ -35,6 +39,7 @@ class V8Debugger : public v8::debug::DebugDelegate { ~V8Debugger(); bool enabled() const; + v8::Isolate* isolate() const { return m_isolate; } String16 setBreakpoint(const ScriptBreakpoint&, int* actualLineNumber, int* actualColumnNumber); @@ -46,7 +51,7 @@ class V8Debugger : public v8::debug::DebugDelegate { void setPauseOnExceptionsState(v8::debug::ExceptionBreakState); bool canBreakProgram(); bool breakProgram(int targetContextGroupId); - void continueProgram(); + void continueProgram(int targetContextGroupId); void setPauseOnNextStatement(bool, int targetContextGroupId); void stepIntoStatement(int targetContextGroupId); @@ -56,6 +61,10 @@ class V8Debugger : public v8::debug::DebugDelegate { std::unique_ptr<ScheduleStepIntoAsyncCallback> callback, int targetContextGroupId); + Response continueToLocation(int targetContextGroupId, + std::unique_ptr<protocol::Debugger::Location>, + const String16& targetCallFramess); + Response setScriptSource( const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, protocol::Maybe<protocol::Runtime::ExceptionDetails>*, @@ -72,12 +81,17 @@ class V8Debugger : public v8::debug::DebugDelegate { void enable(); void disable(); - bool isPaused() const { return m_runningNestedMessageLoop; } + bool isPaused() const { return m_pausedContextGroupId; } v8::Local<v8::Context> pausedContext() { return m_pausedContext; } int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; } - V8StackTraceImpl* currentAsyncCallChain(); void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int); + + std::shared_ptr<AsyncStackTrace> currentAsyncParent(); + std::shared_ptr<AsyncStackTrace> currentAsyncCreation(); + + std::shared_ptr<StackFrame> symbolize(v8::Local<v8::StackFrame> v8Frame); + std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>); std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack); @@ -98,7 +112,8 @@ class V8Debugger : public v8::debug::DebugDelegate { WasmTranslation* wasmTranslation() { return &m_wasmTranslation; } - void setMaxAsyncTaskStacksForTest(int limit) { m_maxAsyncCallStacks = limit; } + void setMaxAsyncTaskStacksForTest(int limit); + void dumpAsyncTaskStacksStateForTest(); private: void compileDebuggerScript(); @@ -108,6 +123,8 @@ class V8Debugger : public v8::debug::DebugDelegate { bool catchExceptions); v8::Local<v8::Context> debuggerContext() const; void clearBreakpoints(); + void clearContinueToLocation(); + bool shouldContinueToCurrentLocation(); static void v8OOMCallback(void* data); @@ -143,11 +160,8 @@ class V8Debugger : public v8::debug::DebugDelegate { void asyncTaskFinishedForStepping(void* task); void asyncTaskCanceledForStepping(void* task); - void registerAsyncTaskIfNeeded(void* task); - // v8::debug::DebugEventListener implementation. - void PromiseEventOccurred(v8::Local<v8::Context> context, - v8::debug::PromiseDebugActionType type, int id, + void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, int parentId, bool createdByUser) override; void ScriptCompiled(v8::Local<v8::debug::Script> script, bool has_compile_error) override; @@ -172,26 +186,36 @@ class V8Debugger : public v8::debug::DebugDelegate { v8::Global<v8::Context> m_debuggerContext; v8::Local<v8::Object> m_executionState; v8::Local<v8::Context> m_pausedContext; - bool m_runningNestedMessageLoop; int m_ignoreScriptParsedEventsCounter; bool m_scheduledOOMBreak = false; int m_targetContextGroupId = 0; + int m_pausedContextGroupId = 0; + String16 m_continueToLocationBreakpointId; + String16 m_continueToLocationTargetCallFrames; + std::unique_ptr<V8StackTraceImpl> m_continueToLocationStack; using AsyncTaskToStackTrace = - protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>; + protocol::HashMap<void*, std::weak_ptr<AsyncStackTrace>>; AsyncTaskToStackTrace m_asyncTaskStacks; AsyncTaskToStackTrace m_asyncTaskCreationStacks; - int m_maxAsyncCallStacks; - std::map<int, void*> m_idToTask; - std::unordered_map<void*, int> m_taskToId; - int m_lastTaskId; protocol::HashSet<void*> m_recurringTasks; + protocol::HashMap<void*, void*> m_parentTask; + + int m_maxAsyncCallStacks; int m_maxAsyncCallStackDepth; + std::vector<void*> m_currentTasks; - std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks; + std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncParent; + std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncCreation; + + void collectOldAsyncStacksIfNeeded(); + int m_asyncStacksCount = 0; + // V8Debugger owns all the async stacks, while most of the other references + // are weak, which allows to collect some stacks when there are too many. + std::list<std::shared_ptr<AsyncStackTrace>> m_allAsyncStacks; + std::map<int, std::weak_ptr<StackFrame>> m_framesCache; + protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap; - protocol::HashMap<void*, void*> m_parentTask; - protocol::HashMap<void*, void*> m_firstNextTask; void* m_taskWithScheduledBreak = nullptr; std::unique_ptr<ScheduleStepIntoAsyncCallback> m_stepIntoAsyncCallback; diff --git a/deps/v8/src/inspector/v8-function-call.cc b/deps/v8/src/inspector/v8-function-call.cc index b8c86d3da0..0fcca70cb7 100644 --- a/deps/v8/src/inspector/v8-function-call.cc +++ b/deps/v8/src/inspector/v8-function-call.cc @@ -75,6 +75,8 @@ v8::Local<v8::Value> V8FunctionCall::call(bool& hadException, } v8::Local<v8::Value> V8FunctionCall::callWithoutExceptionHandling() { + v8::Context::Scope contextScope(m_context); + v8::Local<v8::Object> thisObject = v8::Local<v8::Object>::Cast(m_value); v8::Local<v8::Value> value; if (!thisObject->Get(m_context, m_name).ToLocal(&value)) diff --git a/deps/v8/src/inspector/v8-inspector-impl.cc b/deps/v8/src/inspector/v8-inspector-impl.cc index 705fd793de..3c55507c5a 100644 --- a/deps/v8/src/inspector/v8-inspector-impl.cc +++ b/deps/v8/src/inspector/v8-inspector-impl.cc @@ -56,9 +56,13 @@ V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate, m_debugger(new V8Debugger(isolate, this)), m_capturingStackTracesCount(0), m_lastExceptionId(0), - m_lastContextId(0) {} + m_lastContextId(0) { + v8::debug::SetConsoleDelegate(m_isolate, console()); +} -V8InspectorImpl::~V8InspectorImpl() {} +V8InspectorImpl::~V8InspectorImpl() { + v8::debug::SetConsoleDelegate(m_isolate, nullptr); +} int V8InspectorImpl::contextGroupId(v8::Local<v8::Context> context) { return contextGroupId(InspectedContext::contextId(context)); diff --git a/deps/v8/src/inspector/v8-regex.cc b/deps/v8/src/inspector/v8-regex.cc index 47af70d360..0bab4364c4 100644 --- a/deps/v8/src/inspector/v8-regex.cc +++ b/deps/v8/src/inspector/v8-regex.cc @@ -49,6 +49,7 @@ int V8Regex::match(const String16& string, int startFrom, v8::Isolate* isolate = m_inspector->isolate(); v8::HandleScope handleScope(isolate); v8::Local<v8::Context> context = m_inspector->regexContext(); + v8::Context::Scope contextScope(context); v8::MicrotasksScope microtasks(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); v8::TryCatch tryCatch(isolate); diff --git a/deps/v8/src/inspector/v8-stack-trace-impl.cc b/deps/v8/src/inspector/v8-stack-trace-impl.cc index dddad36202..9db6b47caf 100644 --- a/deps/v8/src/inspector/v8-stack-trace-impl.cc +++ b/deps/v8/src/inspector/v8-stack-trace-impl.cc @@ -4,12 +4,10 @@ #include "src/inspector/v8-stack-trace-impl.h" -#include "src/inspector/string-util.h" -#include "src/inspector/v8-debugger-agent-impl.h" -#include "src/inspector/v8-debugger.h" -#include "src/inspector/v8-inspector-impl.h" +#include <algorithm> -#include "include/v8-version.h" +#include "src/inspector/v8-debugger.h" +#include "src/inspector/wasm-translation.h" namespace v8_inspector { @@ -17,270 +15,212 @@ namespace { static const v8::StackTrace::StackTraceOptions stackTraceOptions = static_cast<v8::StackTrace::StackTraceOptions>( - v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset | - v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL | - v8::StackTrace::kFunctionName | + v8::StackTrace::kDetailed | v8::StackTrace::kExposeFramesAcrossSecurityOrigins); -V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame, - WasmTranslation* wasmTranslation, - int contextGroupId) { - String16 scriptId = String16::fromInteger(frame->GetScriptId()); - String16 sourceName; - v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL()); - if (!sourceNameValue.IsEmpty()) - sourceName = toProtocolString(sourceNameValue); - - String16 functionName; - v8::Local<v8::String> functionNameValue(frame->GetFunctionName()); - if (!functionNameValue.IsEmpty()) - functionName = toProtocolString(functionNameValue); - - int sourceLineNumber = frame->GetLineNumber() - 1; - int sourceColumn = frame->GetColumn() - 1; - // TODO(clemensh): Figure out a way to do this translation only right before - // sending the stack trace over wire. - if (frame->IsWasm()) { - wasmTranslation->TranslateWasmScriptLocationToProtocolLocation( - &scriptId, &sourceLineNumber, &sourceColumn); +std::vector<std::shared_ptr<StackFrame>> toFramesVector( + V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace, + int maxStackSize) { + DCHECK(debugger->isolate()->InContext()); + int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize); + std::vector<std::shared_ptr<StackFrame>> frames; + for (int i = 0; i < frameCount; ++i) { + frames.push_back(debugger->symbolize(v8StackTrace->GetFrame(i))); } - return V8StackTraceImpl::Frame(functionName, scriptId, sourceName, - sourceLineNumber + 1, sourceColumn + 1); + return frames; } -void toFramesVector(v8::Local<v8::StackTrace> stackTrace, - std::vector<V8StackTraceImpl::Frame>& frames, - size_t maxStackSize, v8::Isolate* isolate, - V8Debugger* debugger, int contextGroupId) { - DCHECK(isolate->InContext()); - int frameCount = stackTrace->GetFrameCount(); - if (frameCount > static_cast<int>(maxStackSize)) - frameCount = static_cast<int>(maxStackSize); - WasmTranslation* wasmTranslation = debugger->wasmTranslation(); - for (int i = 0; i < frameCount; i++) { - v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i); - frames.push_back(toFrame(stackFrame, wasmTranslation, contextGroupId)); +void calculateAsyncChain(V8Debugger* debugger, int contextGroupId, + std::shared_ptr<AsyncStackTrace>* asyncParent, + std::shared_ptr<AsyncStackTrace>* asyncCreation, + int* maxAsyncDepth) { + *asyncParent = debugger->currentAsyncParent(); + *asyncCreation = debugger->currentAsyncCreation(); + if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth(); + + DCHECK(!*asyncParent || !*asyncCreation || + (*asyncParent)->contextGroupId() == + (*asyncCreation)->contextGroupId()); + // Do not accidentally append async call chain from another group. This should + // not happen if we have proper instrumentation, but let's double-check to be + // safe. + if (contextGroupId && *asyncParent && + (*asyncParent)->contextGroupId() != contextGroupId) { + asyncParent->reset(); + asyncCreation->reset(); + if (maxAsyncDepth) *maxAsyncDepth = 0; + return; + } + + // Only the top stack in the chain may be empty and doesn't contain creation + // stack, so ensure that second stack is non-empty (it's the top of appended + // chain). + if (*asyncParent && !(*asyncCreation) && !(*asyncParent)->creation().lock() && + (*asyncParent)->isEmpty()) { + *asyncParent = (*asyncParent)->parent().lock(); } } +std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon( + const std::vector<std::shared_ptr<StackFrame>>& frames, + const String16& description, + const std::shared_ptr<AsyncStackTrace>& asyncParent, + const std::shared_ptr<AsyncStackTrace>& asyncCreation, int maxAsyncDepth) { + if (asyncParent && frames.empty() && + description == asyncParent->description() && !asyncCreation) { + return asyncParent->buildInspectorObject(nullptr, maxAsyncDepth); + } + + std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> + inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create(); + for (size_t i = 0; i < frames.size(); i++) { + inspectorFrames->addItem(frames[i]->buildInspectorObject()); + } + std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = + protocol::Runtime::StackTrace::create() + .setCallFrames(std::move(inspectorFrames)) + .build(); + if (!description.isEmpty()) stackTrace->setDescription(description); + if (asyncParent && maxAsyncDepth > 0) { + stackTrace->setParent(asyncParent->buildInspectorObject(asyncCreation.get(), + maxAsyncDepth - 1)); + } + return stackTrace; +} + } // namespace -V8StackTraceImpl::Frame::Frame() - : m_functionName("undefined"), - m_scriptId(""), - m_scriptName("undefined"), - m_lineNumber(0), - m_columnNumber(0) {} - -V8StackTraceImpl::Frame::Frame(const String16& functionName, - const String16& scriptId, - const String16& scriptName, int lineNumber, - int column) - : m_functionName(functionName), - m_scriptId(scriptId), - m_scriptName(scriptName), - m_lineNumber(lineNumber), - m_columnNumber(column) { - DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo); - DCHECK(m_columnNumber != v8::Message::kNoColumnInfo); +StackFrame::StackFrame(v8::Local<v8::StackFrame> v8Frame) + : m_functionName(toProtocolString(v8Frame->GetFunctionName())), + m_scriptId(String16::fromInteger(v8Frame->GetScriptId())), + m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())), + m_lineNumber(v8Frame->GetLineNumber() - 1), + m_columnNumber(v8Frame->GetColumn() - 1) { + DCHECK(m_lineNumber + 1 != v8::Message::kNoLineNumberInfo); + DCHECK(m_columnNumber + 1 != v8::Message::kNoColumnInfo); } -V8StackTraceImpl::Frame::~Frame() {} +void StackFrame::translate(WasmTranslation* wasmTranslation) { + wasmTranslation->TranslateWasmScriptLocationToProtocolLocation( + &m_scriptId, &m_lineNumber, &m_columnNumber); +} + +const String16& StackFrame::functionName() const { return m_functionName; } + +const String16& StackFrame::scriptId() const { return m_scriptId; } -// buildInspectorObject() and SourceLocation's toTracedValue() should set the -// same fields. -// If either of them is modified, the other should be also modified. -std::unique_ptr<protocol::Runtime::CallFrame> -V8StackTraceImpl::Frame::buildInspectorObject() const { +const String16& StackFrame::sourceURL() const { return m_sourceURL; } + +int StackFrame::lineNumber() const { return m_lineNumber; } + +int StackFrame::columnNumber() const { return m_columnNumber; } + +std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject() + const { return protocol::Runtime::CallFrame::create() .setFunctionName(m_functionName) .setScriptId(m_scriptId) - .setUrl(m_scriptName) - .setLineNumber(m_lineNumber - 1) - .setColumnNumber(m_columnNumber - 1) + .setUrl(m_sourceURL) + .setLineNumber(m_lineNumber) + .setColumnNumber(m_columnNumber) .build(); } -V8StackTraceImpl::Frame V8StackTraceImpl::Frame::clone() const { - return Frame(m_functionName, m_scriptId, m_scriptName, m_lineNumber, - m_columnNumber); +bool StackFrame::isEqual(StackFrame* frame) const { + return m_scriptId == frame->m_scriptId && + m_lineNumber == frame->m_lineNumber && + m_columnNumber == frame->m_columnNumber; } // static void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions( v8::Isolate* isolate, bool capture) { isolate->SetCaptureStackTraceForUncaughtExceptions( - capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions); + capture, V8StackTraceImpl::maxCallStackSizeToCapture); } // static std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create( V8Debugger* debugger, int contextGroupId, - v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize, - const String16& description) { + v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) { DCHECK(debugger); - v8::Isolate* isolate = debugger->inspector()->isolate(); - v8::HandleScope scope(isolate); - std::vector<V8StackTraceImpl::Frame> frames; - if (!stackTrace.IsEmpty()) - toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger, - contextGroupId); - - int maxAsyncCallChainDepth = 1; - V8StackTraceImpl* asyncCallChain = nullptr; - if (maxStackSize > 1) { - asyncCallChain = debugger->currentAsyncCallChain(); - maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth(); - } - // Do not accidentally append async call chain from another group. This should - // not - // happen if we have proper instrumentation, but let's double-check to be - // safe. - if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId && - asyncCallChain->m_contextGroupId != contextGroupId) { - asyncCallChain = nullptr; - maxAsyncCallChainDepth = 1; - } - - // Only the top stack in the chain may be empty and doesn't contain creation - // stack , so ensure that second stack is non-empty (it's the top of appended - // chain). - if (asyncCallChain && asyncCallChain->isEmpty() && - !asyncCallChain->m_creation) { - asyncCallChain = asyncCallChain->m_parent.get(); - } - - if (stackTrace.IsEmpty() && !asyncCallChain) return nullptr; - std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl( - contextGroupId, description, frames, - asyncCallChain ? asyncCallChain->cloneImpl() : nullptr)); + v8::Isolate* isolate = debugger->isolate(); + v8::HandleScope scope(isolate); - // Crop to not exceed maxAsyncCallChainDepth. - V8StackTraceImpl* deepest = result.get(); - while (deepest && maxAsyncCallChainDepth) { - deepest = deepest->m_parent.get(); - maxAsyncCallChainDepth--; + std::vector<std::shared_ptr<StackFrame>> frames; + if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) { + frames = toFramesVector(debugger, v8StackTrace, maxStackSize); } - if (deepest) deepest->m_parent.reset(); - return result; + int maxAsyncDepth = 0; + std::shared_ptr<AsyncStackTrace> asyncParent; + std::shared_ptr<AsyncStackTrace> asyncCreation; + calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation, + &maxAsyncDepth); + if (frames.empty() && !asyncCreation && !asyncParent) return nullptr; + return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl( + std::move(frames), maxAsyncDepth, asyncParent, asyncCreation)); } // static std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture( - V8Debugger* debugger, int contextGroupId, size_t maxStackSize, - const String16& description) { + V8Debugger* debugger, int contextGroupId, int maxStackSize) { DCHECK(debugger); - v8::Isolate* isolate = debugger->inspector()->isolate(); + v8::Isolate* isolate = debugger->isolate(); v8::HandleScope handleScope(isolate); - v8::Local<v8::StackTrace> stackTrace; + v8::Local<v8::StackTrace> v8StackTrace; if (isolate->InContext()) { - stackTrace = v8::StackTrace::CurrentStackTrace( - isolate, static_cast<int>(maxStackSize), stackTraceOptions); + v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize, + stackTraceOptions); } - return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace, - maxStackSize, description); + return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace, + maxStackSize); } -std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() { - std::vector<Frame> framesCopy(m_frames); - std::unique_ptr<V8StackTraceImpl> copy( - new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy, - m_parent ? m_parent->cloneImpl() : nullptr)); - if (m_creation) copy->setCreation(m_creation->cloneImpl()); - return copy; -} - -std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { - std::vector<Frame> frames; - for (size_t i = 0; i < m_frames.size(); i++) - frames.push_back(m_frames.at(i).clone()); - return std::unique_ptr<V8StackTraceImpl>( - new V8StackTraceImpl(m_contextGroupId, m_description, frames, nullptr)); -} - -V8StackTraceImpl::V8StackTraceImpl(int contextGroupId, - const String16& description, - std::vector<Frame>& frames, - std::unique_ptr<V8StackTraceImpl> parent) - : m_contextGroupId(contextGroupId), - m_description(description), - m_parent(std::move(parent)) { - m_frames.swap(frames); -} +V8StackTraceImpl::V8StackTraceImpl( + std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth, + std::shared_ptr<AsyncStackTrace> asyncParent, + std::shared_ptr<AsyncStackTrace> asyncCreation) + : m_frames(std::move(frames)), + m_maxAsyncDepth(maxAsyncDepth), + m_asyncParent(asyncParent), + m_asyncCreation(asyncCreation) {} V8StackTraceImpl::~V8StackTraceImpl() {} -void V8StackTraceImpl::setCreation(std::unique_ptr<V8StackTraceImpl> creation) { - m_creation = std::move(creation); - // When async call chain is empty but doesn't contain useful schedule stack - // and parent async call chain contains creationg stack but doesn't - // synchronous we can merge them together. - // e.g. Promise ThenableJob. - if (m_parent && isEmpty() && m_description == m_parent->m_description && - !m_parent->m_creation) { - m_frames.swap(m_parent->m_frames); - m_parent = std::move(m_parent->m_parent); - } +std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { + return std::unique_ptr<V8StackTrace>( + new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(), + std::shared_ptr<AsyncStackTrace>())); } +bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); } + StringView V8StackTraceImpl::topSourceURL() const { - DCHECK(m_frames.size()); - return toStringView(m_frames[0].m_scriptName); + return toStringView(m_frames[0]->sourceURL()); } int V8StackTraceImpl::topLineNumber() const { - DCHECK(m_frames.size()); - return m_frames[0].m_lineNumber; + return m_frames[0]->lineNumber() + 1; } int V8StackTraceImpl::topColumnNumber() const { - DCHECK(m_frames.size()); - return m_frames[0].m_columnNumber; -} - -StringView V8StackTraceImpl::topFunctionName() const { - DCHECK(m_frames.size()); - return toStringView(m_frames[0].m_functionName); + return m_frames[0]->columnNumber() + 1; } StringView V8StackTraceImpl::topScriptId() const { - DCHECK(m_frames.size()); - return toStringView(m_frames[0].m_scriptId); + return toStringView(m_frames[0]->scriptId()); } -std::unique_ptr<protocol::Runtime::StackTrace> -V8StackTraceImpl::buildInspectorObjectImpl() const { - std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = - protocol::Array<protocol::Runtime::CallFrame>::create(); - for (size_t i = 0; i < m_frames.size(); i++) - frames->addItem(m_frames.at(i).buildInspectorObject()); - - std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = - protocol::Runtime::StackTrace::create() - .setCallFrames(std::move(frames)) - .build(); - if (!m_description.isEmpty()) stackTrace->setDescription(m_description); - if (m_parent) stackTrace->setParent(m_parent->buildInspectorObjectImpl()); - if (m_creation && m_creation->m_frames.size()) { - stackTrace->setPromiseCreationFrame( - m_creation->m_frames[0].buildInspectorObject()); - } - return stackTrace; +StringView V8StackTraceImpl::topFunctionName() const { + return toStringView(m_frames[0]->functionName()); } std::unique_ptr<protocol::Runtime::StackTrace> -V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) const { - DCHECK(debugger); - v8::HandleScope handleScope(debugger->inspector()->isolate()); - // Next call collapses possible empty stack and ensures - // maxAsyncCallChainDepth. - std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create( - debugger, m_contextGroupId, v8::Local<v8::StackTrace>(), - V8StackTraceImpl::maxCallStackSizeToCapture); - if (!fullChain || !fullChain->m_parent) return nullptr; - return fullChain->m_parent->buildInspectorObjectImpl(); +V8StackTraceImpl::buildInspectorObjectImpl() const { + return buildInspectorObjectCommon(m_frames, String16(), m_asyncParent.lock(), + m_asyncCreation.lock(), m_maxAsyncDepth); } std::unique_ptr<protocol::Runtime::API::StackTrace> @@ -291,20 +231,139 @@ V8StackTraceImpl::buildInspectorObject() const { std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { String16Builder stackTrace; for (size_t i = 0; i < m_frames.size(); ++i) { - const Frame& frame = m_frames[i]; + const StackFrame& frame = *m_frames[i]; stackTrace.append("\n at " + (frame.functionName().length() ? frame.functionName() : "(anonymous function)")); stackTrace.append(" ("); stackTrace.append(frame.sourceURL()); stackTrace.append(':'); - stackTrace.append(String16::fromInteger(frame.lineNumber())); + stackTrace.append(String16::fromInteger(frame.lineNumber() + 1)); stackTrace.append(':'); - stackTrace.append(String16::fromInteger(frame.columnNumber())); + stackTrace.append(String16::fromInteger(frame.columnNumber() + 1)); stackTrace.append(')'); } String16 string = stackTrace.toString(); return StringBufferImpl::adopt(string); } +bool V8StackTraceImpl::isEqualIgnoringTopFrame( + V8StackTraceImpl* stackTrace) const { + StackFrameIterator current(this); + StackFrameIterator target(stackTrace); + + current.next(); + target.next(); + while (!current.done() && !target.done()) { + if (!current.frame()->isEqual(target.frame())) { + return false; + } + current.next(); + target.next(); + } + return current.done() == target.done(); +} + +V8StackTraceImpl::StackFrameIterator::StackFrameIterator( + const V8StackTraceImpl* stackTrace) + : m_currentIt(stackTrace->m_frames.begin()), + m_currentEnd(stackTrace->m_frames.end()), + m_parent(stackTrace->m_asyncParent.lock().get()) {} + +void V8StackTraceImpl::StackFrameIterator::next() { + if (m_currentIt == m_currentEnd) return; + ++m_currentIt; + while (m_currentIt == m_currentEnd && m_parent) { + const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames(); + m_currentIt = frames.begin(); + if (m_parent->description() == "async function") ++m_currentIt; + m_currentEnd = frames.end(); + m_parent = m_parent->parent().lock().get(); + } +} + +bool V8StackTraceImpl::StackFrameIterator::done() { + return m_currentIt == m_currentEnd; +} + +StackFrame* V8StackTraceImpl::StackFrameIterator::frame() { + return m_currentIt->get(); +} + +// static +std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture( + V8Debugger* debugger, int contextGroupId, const String16& description, + int maxStackSize) { + DCHECK(debugger); + + v8::Isolate* isolate = debugger->isolate(); + v8::HandleScope handleScope(isolate); + + std::vector<std::shared_ptr<StackFrame>> frames; + if (isolate->InContext()) { + v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace( + isolate, maxStackSize, stackTraceOptions); + frames = toFramesVector(debugger, v8StackTrace, maxStackSize); + } + + std::shared_ptr<AsyncStackTrace> asyncParent; + std::shared_ptr<AsyncStackTrace> asyncCreation; + calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation, + nullptr); + + if (frames.empty() && !asyncCreation && !asyncParent) return nullptr; + + // When async call chain is empty but doesn't contain useful schedule stack + // and parent async call chain contains creationg stack but doesn't + // synchronous we can merge them together. + // e.g. Promise ThenableJob. + if (asyncParent && frames.empty() && + asyncParent->m_description == description && !asyncCreation) { + return asyncParent; + } + + DCHECK(contextGroupId || asyncParent); + if (!contextGroupId && asyncParent) { + contextGroupId = asyncParent->m_contextGroupId; + } + return std::shared_ptr<AsyncStackTrace>( + new AsyncStackTrace(contextGroupId, description, std::move(frames), + asyncParent, asyncCreation)); +} + +AsyncStackTrace::AsyncStackTrace( + int contextGroupId, const String16& description, + std::vector<std::shared_ptr<StackFrame>> frames, + std::shared_ptr<AsyncStackTrace> asyncParent, + std::shared_ptr<AsyncStackTrace> asyncCreation) + : m_contextGroupId(contextGroupId), + m_description(description), + m_frames(std::move(frames)), + m_asyncParent(asyncParent), + m_asyncCreation(asyncCreation) { + DCHECK(m_contextGroupId); +} + +std::unique_ptr<protocol::Runtime::StackTrace> +AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation, + int maxAsyncDepth) const { + return buildInspectorObjectCommon(m_frames, m_description, + m_asyncParent.lock(), + m_asyncCreation.lock(), maxAsyncDepth); +} + +int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; } + +const String16& AsyncStackTrace::description() const { return m_description; } + +std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const { + return m_asyncParent; +} + +std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const { + return m_asyncCreation; +} + +bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); } + } // namespace v8_inspector diff --git a/deps/v8/src/inspector/v8-stack-trace-impl.h b/deps/v8/src/inspector/v8-stack-trace-impl.h index f8b53d0a65..5ce051bd5c 100644 --- a/deps/v8/src/inspector/v8-stack-trace-impl.h +++ b/deps/v8/src/inspector/v8-stack-trace-impl.h @@ -5,96 +5,142 @@ #ifndef V8_INSPECTOR_V8STACKTRACEIMPL_H_ #define V8_INSPECTOR_V8STACKTRACEIMPL_H_ +#include <memory> #include <vector> +#include "include/v8-inspector.h" +#include "include/v8.h" #include "src/base/macros.h" -#include "src/inspector/protocol/Forward.h" #include "src/inspector/protocol/Runtime.h" - -#include "include/v8-inspector.h" +#include "src/inspector/string-16.h" namespace v8_inspector { -class TracedValue; +class AsyncStackTrace; class V8Debugger; +class WasmTranslation; -// Note: async stack trace may have empty top stack with non-empty tail to -// indicate -// that current native-only state had some async story. -// On the other hand, any non-top async stack is guaranteed to be non-empty. -class V8StackTraceImpl final : public V8StackTrace { +class StackFrame { public: - static const size_t maxCallStackSizeToCapture = 200; + explicit StackFrame(v8::Local<v8::StackFrame> frame); + ~StackFrame() = default; - class Frame { - public: - Frame(); - Frame(const String16& functionName, const String16& scriptId, - const String16& scriptName, int lineNumber, int column = 0); - ~Frame(); - - const String16& functionName() const { return m_functionName; } - const String16& scriptId() const { return m_scriptId; } - const String16& sourceURL() const { return m_scriptName; } - int lineNumber() const { return m_lineNumber; } - int columnNumber() const { return m_columnNumber; } - Frame clone() const; + void translate(WasmTranslation* wasmTranslation); - private: - friend class V8StackTraceImpl; - std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const; - void toTracedValue(TracedValue*) const; - - String16 m_functionName; - String16 m_scriptId; - String16 m_scriptName; - int m_lineNumber; - int m_columnNumber; - }; + const String16& functionName() const; + const String16& scriptId() const; + const String16& sourceURL() const; + int lineNumber() const; // 0-based. + int columnNumber() const; // 0-based. + std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const; + bool isEqual(StackFrame* frame) const; + + private: + String16 m_functionName; + String16 m_scriptId; + String16 m_sourceURL; + int m_lineNumber; // 0-based. + int m_columnNumber; // 0-based. +}; +class V8StackTraceImpl : public V8StackTrace { + public: static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*, bool capture); - static std::unique_ptr<V8StackTraceImpl> create( - V8Debugger*, int contextGroupId, v8::Local<v8::StackTrace>, - size_t maxStackSize, const String16& description = String16()); - static std::unique_ptr<V8StackTraceImpl> capture( - V8Debugger*, int contextGroupId, size_t maxStackSize, - const String16& description = String16()); - - // This method drops the async chain. Use cloneImpl() instead. - std::unique_ptr<V8StackTrace> clone() override; - std::unique_ptr<V8StackTraceImpl> cloneImpl(); - std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectForTail( - V8Debugger*) const; + static const int maxCallStackSizeToCapture = 200; + static std::unique_ptr<V8StackTraceImpl> create(V8Debugger*, + int contextGroupId, + v8::Local<v8::StackTrace>, + int maxStackSize); + static std::unique_ptr<V8StackTraceImpl> capture(V8Debugger*, + int contextGroupId, + int maxStackSize); + + ~V8StackTraceImpl() override; std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl() const; - ~V8StackTraceImpl() override; // V8StackTrace implementation. - bool isEmpty() const override { return !m_frames.size(); }; + // This method drops the async stack trace. + std::unique_ptr<V8StackTrace> clone() override; + bool isEmpty() const override; StringView topSourceURL() const override; - int topLineNumber() const override; - int topColumnNumber() const override; + int topLineNumber() const override; // 1-based. + int topColumnNumber() const override; // 1-based. StringView topScriptId() const override; StringView topFunctionName() const override; std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject() const override; std::unique_ptr<StringBuffer> toString() const override; - void setCreation(std::unique_ptr<V8StackTraceImpl> creation); + bool isEqualIgnoringTopFrame(V8StackTraceImpl* stackTrace) const; private: - V8StackTraceImpl(int contextGroupId, const String16& description, - std::vector<Frame>& frames, - std::unique_ptr<V8StackTraceImpl> parent); + V8StackTraceImpl(std::vector<std::shared_ptr<StackFrame>> frames, + int maxAsyncDepth, + std::shared_ptr<AsyncStackTrace> asyncParent, + std::shared_ptr<AsyncStackTrace> asyncCreation); + + class StackFrameIterator { + public: + explicit StackFrameIterator(const V8StackTraceImpl* stackTrace); + + void next(); + StackFrame* frame(); + bool done(); + + private: + std::vector<std::shared_ptr<StackFrame>>::const_iterator m_currentIt; + std::vector<std::shared_ptr<StackFrame>>::const_iterator m_currentEnd; + AsyncStackTrace* m_parent; + }; + + std::vector<std::shared_ptr<StackFrame>> m_frames; + int m_maxAsyncDepth; + std::weak_ptr<AsyncStackTrace> m_asyncParent; + std::weak_ptr<AsyncStackTrace> m_asyncCreation; + + DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl); +}; + +class AsyncStackTrace { + public: + static std::shared_ptr<AsyncStackTrace> capture(V8Debugger*, + int contextGroupId, + const String16& description, + int maxStackSize); + + std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObject( + AsyncStackTrace* asyncCreation, int maxAsyncDepth) const; + + int contextGroupId() const; + const String16& description() const; + std::weak_ptr<AsyncStackTrace> parent() const; + std::weak_ptr<AsyncStackTrace> creation() const; + bool isEmpty() const; + + void setDescription(const String16& description) { + // TODO(kozyatinskiy): implement it without hack. + m_description = description; + } + const std::vector<std::shared_ptr<StackFrame>>& frames() const { + return m_frames; + } + + private: + AsyncStackTrace(int contextGroupId, const String16& description, + std::vector<std::shared_ptr<StackFrame>> frames, + std::shared_ptr<AsyncStackTrace> asyncParent, + std::shared_ptr<AsyncStackTrace> asyncCreation); int m_contextGroupId; String16 m_description; - std::vector<Frame> m_frames; - std::unique_ptr<V8StackTraceImpl> m_parent; - std::unique_ptr<V8StackTraceImpl> m_creation; - DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl); + std::vector<std::shared_ptr<StackFrame>> m_frames; + std::weak_ptr<AsyncStackTrace> m_asyncParent; + std::weak_ptr<AsyncStackTrace> m_asyncCreation; + + DISALLOW_COPY_AND_ASSIGN(AsyncStackTrace); }; } // namespace v8_inspector |