Index: src/inspector/v8-debugger-agent-impl.cc |
diff --git a/src/inspector/v8-debugger-agent-impl.cc b/src/inspector/v8-debugger-agent-impl.cc |
index 0e455cb9b90f4575daa605a92959cc2353755af6..8b7521d974b0da1de2a6a62cf4054014f4471e04 100644 |
--- a/src/inspector/v8-debugger-agent-impl.cc |
+++ b/src/inspector/v8-debugger-agent-impl.cc |
@@ -43,6 +43,7 @@ static const char pauseOnExceptionsState[] = "pauseOnExceptionsState"; |
static const char asyncCallStackDepth[] = "asyncCallStackDepth"; |
static const char blackboxPattern[] = "blackboxPattern"; |
static const char debuggerEnabled[] = "debuggerEnabled"; |
+static const char skipAllPauses[] = "skipAllPauses"; |
// Breakpoint properties. |
static const char url[] = "url"; |
@@ -50,7 +51,7 @@ static const char isRegex[] = "isRegex"; |
static const char lineNumber[] = "lineNumber"; |
static const char columnNumber[] = "columnNumber"; |
static const char condition[] = "condition"; |
-static const char skipAllPauses[] = "skipAllPauses"; |
+static const char hint[] = "hint"; |
} // namespace DebuggerAgentState |
@@ -59,6 +60,9 @@ static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled"; |
static const char kDebuggerNotPaused[] = |
"Can only perform operation while paused."; |
+static const size_t kBreakpointHintMaxLength = 128; |
+static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10; |
+ |
namespace { |
void TranslateWasmStackTraceLocations(Array<CallFrame>* stackTrace, |
@@ -119,6 +123,54 @@ std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation( |
.build(); |
} |
+String16 breakpointHint(const V8DebuggerScript& script, |
+ const ScriptBreakpoint& breakpoint) { |
+ int offset = script.offset(breakpoint.line_number, breakpoint.column_number); |
+ if (offset == V8DebuggerScript::kNoOffset) return String16(); |
+ const String16& source = script.source(); |
+ String16 hint = |
+ source.substring(offset, kBreakpointHintMaxLength).stripWhiteSpace(); |
+ for (size_t i = 0; i < hint.length(); ++i) { |
+ if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') { |
+ return hint.substring(0, i); |
+ } |
+ } |
+ return hint; |
+} |
+ |
+void adjustBreakpointLocation(const V8DebuggerScript& script, |
+ const String16& hint, |
+ ScriptBreakpoint* breakpoint) { |
+ if (hint.isEmpty()) return; |
+ intptr_t sourceOffset = |
+ script.offset(breakpoint->line_number, breakpoint->column_number); |
+ if (sourceOffset == V8DebuggerScript::kNoOffset) return; |
+ |
+ intptr_t searchRegionOffset = std::max( |
+ sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0)); |
+ size_t offset = sourceOffset - searchRegionOffset; |
+ String16 searchArea = script.source().substring( |
+ searchRegionOffset, offset + kBreakpointHintMaxSearchOffset); |
+ |
+ size_t nextMatch = searchArea.find(hint, offset); |
+ size_t prevMatch = searchArea.reverseFind(hint, offset); |
+ if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) { |
+ return; |
+ } |
+ size_t bestMatch; |
+ if (nextMatch == String16::kNotFound) { |
+ bestMatch = prevMatch; |
+ } else if (prevMatch == String16::kNotFound) { |
+ bestMatch = nextMatch; |
+ } else { |
+ bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch; |
+ } |
+ bestMatch += searchRegionOffset; |
+ v8::debug::Location hintPosition = script.location(bestMatch); |
+ if (hintPosition.IsEmpty()) return; |
+ breakpoint->line_number = hintPosition.GetLineNumber(); |
+ breakpoint->column_number = hintPosition.GetColumnNumber(); |
+} |
} // namespace |
V8DebuggerAgentImpl::V8DebuggerAgentImpl( |
@@ -239,7 +291,7 @@ Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) { |
static std::unique_ptr<protocol::DictionaryValue> |
buildObjectForBreakpointCookie(const String16& url, int lineNumber, |
int columnNumber, const String16& condition, |
- bool isRegex) { |
+ bool isRegex, const String16& hint) { |
std::unique_ptr<protocol::DictionaryValue> breakpointObject = |
protocol::DictionaryValue::create(); |
breakpointObject->setString(DebuggerAgentState::url, url); |
@@ -247,6 +299,9 @@ buildObjectForBreakpointCookie(const String16& url, int lineNumber, |
breakpointObject->setInteger(DebuggerAgentState::columnNumber, columnNumber); |
breakpointObject->setString(DebuggerAgentState::condition, condition); |
breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex); |
+ if (!hint.isEmpty()) { |
+ breakpointObject->setString(DebuggerAgentState::hint, hint); |
+ } |
return breakpointObject; |
} |
@@ -293,20 +348,23 @@ Response V8DebuggerAgentImpl::setBreakpointByUrl( |
if (breakpointsCookie->get(breakpointId)) |
return Response::Error("Breakpoint at specified location already exists."); |
- breakpointsCookie->setObject( |
- breakpointId, buildObjectForBreakpointCookie( |
- url, lineNumber, columnNumber, condition, isRegex)); |
- |
+ String16 hint; |
ScriptBreakpoint breakpoint(String16(), lineNumber, columnNumber, condition); |
for (const auto& script : m_scripts) { |
if (!matches(m_inspector, script.second->sourceURL(), url, isRegex)) |
continue; |
breakpoint.script_id = script.first; |
std::unique_ptr<protocol::Debugger::Location> location = |
- resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource); |
+ resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource, hint); |
+ if (!isRegex) hint = breakpointHint(*script.second, breakpoint); |
if (location) (*locations)->addItem(std::move(location)); |
} |
+ breakpointsCookie->setObject( |
+ breakpointId, |
+ buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, |
+ isRegex, hint)); |
+ |
*outBreakpointId = breakpointId; |
return Response::OK(); |
} |
@@ -325,8 +383,8 @@ Response V8DebuggerAgentImpl::setBreakpoint( |
m_breakpointIdToDebuggerBreakpointIds.end()) { |
return Response::Error("Breakpoint at specified location already exists."); |
} |
- *actualLocation = |
- resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource); |
+ *actualLocation = resolveBreakpoint( |
+ breakpointId, breakpoint, UserBreakpointSource, /* hint */ String16()); |
if (!*actualLocation) return Response::Error("Could not resolve breakpoint"); |
*outBreakpointId = breakpointId; |
return Response::OK(); |
@@ -457,7 +515,8 @@ bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId, |
std::unique_ptr<protocol::Debugger::Location> |
V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId, |
const ScriptBreakpoint& breakpoint, |
- BreakpointSource source) { |
+ BreakpointSource source, |
+ const String16& hint) { |
v8::HandleScope handles(m_isolate); |
DCHECK(enabled()); |
// FIXME: remove these checks once crbug.com/520702 is resolved. |
@@ -471,6 +530,8 @@ V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId, |
// Translate from protocol location to v8 location for the debugger. |
ScriptBreakpoint translatedBreakpoint = breakpoint; |
+ adjustBreakpointLocation(*scriptIterator->second, hint, |
+ &translatedBreakpoint); |
m_debugger->wasmTranslation()->TranslateProtocolLocationToWasmScriptLocation( |
&translatedBreakpoint.script_id, &translatedBreakpoint.line_number, |
&translatedBreakpoint.column_number); |
@@ -505,7 +566,7 @@ Response V8DebuggerAgentImpl::searchInContent( |
return Response::Error("No script for id: " + scriptId); |
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches = |
- searchInTextByLinesImpl(m_session, it->second->source(m_isolate), query, |
+ searchInTextByLinesImpl(m_session, it->second->source(), query, |
optionalCaseSensitive.fromMaybe(false), |
optionalIsRegex.fromMaybe(false)); |
*results = protocol::Array<protocol::Debugger::SearchMatch>::create(); |
@@ -538,7 +599,7 @@ Response V8DebuggerAgentImpl::setScriptSource( |
&m_pausedCallFrames, stackChanged, &compileError); |
if (!response.isSuccess() || compileError) return response; |
- it->second->setSource(newSource); |
+ it->second->setSource(newContent); |
std::unique_ptr<Array<CallFrame>> callFrames; |
response = currentCallFrames(&callFrames); |
if (!response.isSuccess()) return response; |
@@ -583,8 +644,7 @@ Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId, |
ScriptsMap::iterator it = m_scripts.find(scriptId); |
if (it == m_scripts.end()) |
return Response::Error("No script for id: " + scriptId); |
- v8::HandleScope handles(m_isolate); |
- *scriptSource = it->second->source(m_isolate); |
+ *scriptSource = it->second->source(); |
return Response::OK(); |
} |
@@ -993,7 +1053,7 @@ bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); } |
void V8DebuggerAgentImpl::didParseSource( |
std::unique_ptr<V8DebuggerScript> script, bool success) { |
v8::HandleScope handles(m_isolate); |
- String16 scriptSource = script->source(m_isolate); |
+ String16 scriptSource = script->source(); |
if (!success) script->setSourceURL(findSourceURL(scriptSource, false)); |
if (!success) |
script->setSourceMappingURL(findSourceMapURL(scriptSource, false)); |
@@ -1036,14 +1096,14 @@ void V8DebuggerAgentImpl::didParseSource( |
m_frontend.scriptParsed( |
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(), |
scriptRef->endLine(), scriptRef->endColumn(), contextId, |
- scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam), |
+ scriptRef->hash(), std::move(executionContextAuxDataParam), |
isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam, |
isModuleParam); |
else |
m_frontend.scriptFailedToParse( |
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(), |
scriptRef->endLine(), scriptRef->endColumn(), contextId, |
- scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam), |
+ scriptRef->hash(), std::move(executionContextAuxDataParam), |
std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam); |
if (scriptURL.isEmpty() || !success) return; |
@@ -1069,8 +1129,15 @@ void V8DebuggerAgentImpl::didParseSource( |
&breakpoint.column_number); |
breakpointObject->getString(DebuggerAgentState::condition, |
&breakpoint.condition); |
+ String16 hint; |
+ bool hasHint = breakpointObject->getString(DebuggerAgentState::hint, &hint); |
std::unique_ptr<protocol::Debugger::Location> location = |
- resolveBreakpoint(cookie.first, breakpoint, UserBreakpointSource); |
+ resolveBreakpoint(cookie.first, breakpoint, UserBreakpointSource, hint); |
+ if (!hasHint) { |
+ hint = breakpointHint(*scriptRef, breakpoint); |
+ if (!hint.isEmpty()) |
+ breakpointObject->setString(DebuggerAgentState::hint, hint); |
+ } |
if (location) |
m_frontend.breakpointResolved(cookie.first, std::move(location)); |
} |
@@ -1209,7 +1276,7 @@ void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId, |
const String16& condition) { |
ScriptBreakpoint breakpoint(scriptId, lineNumber, columnNumber, condition); |
String16 breakpointId = generateBreakpointId(breakpoint, source); |
- resolveBreakpoint(breakpointId, breakpoint, source); |
+ resolveBreakpoint(breakpointId, breakpoint, source, /* hint */ String16()); |
} |
void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId, |