5 #include "src/inspector/v8-debugger-agent-impl.h" 9 #include "src/debug/debug-interface.h" 10 #include "src/inspector/injected-script.h" 11 #include "src/inspector/inspected-context.h" 12 #include "src/inspector/protocol/Protocol.h" 13 #include "src/inspector/remote-object-id.h" 14 #include "src/inspector/search-util.h" 15 #include "src/inspector/string-util.h" 16 #include "src/inspector/v8-debugger-script.h" 17 #include "src/inspector/v8-debugger.h" 18 #include "src/inspector/v8-inspector-impl.h" 19 #include "src/inspector/v8-inspector-session-impl.h" 20 #include "src/inspector/v8-regex.h" 21 #include "src/inspector/v8-runtime-agent-impl.h" 22 #include "src/inspector/v8-stack-trace-impl.h" 23 #include "src/inspector/v8-value-utils.h" 25 #include "include/v8-inspector.h" 29 using protocol::Array;
30 using protocol::Maybe;
31 using protocol::Debugger::BreakpointId;
32 using protocol::Debugger::CallFrame;
33 using protocol::Runtime::ExceptionDetails;
34 using protocol::Runtime::ScriptId;
35 using protocol::Runtime::RemoteObject;
36 using protocol::Debugger::Scope;
38 namespace DebuggerAgentState {
39 static const char pauseOnExceptionsState[] =
"pauseOnExceptionsState";
40 static const char asyncCallStackDepth[] =
"asyncCallStackDepth";
41 static const char blackboxPattern[] =
"blackboxPattern";
42 static const char debuggerEnabled[] =
"debuggerEnabled";
43 static const char skipAllPauses[] =
"skipAllPauses";
45 static const char breakpointsByRegex[] =
"breakpointsByRegex";
46 static const char breakpointsByUrl[] =
"breakpointsByUrl";
47 static const char breakpointsByScriptHash[] =
"breakpointsByScriptHash";
48 static const char breakpointHints[] =
"breakpointHints";
52 static const char kBacktraceObjectGroup[] =
"backtrace";
53 static const char kDebuggerNotEnabled[] =
"Debugger agent is not enabled";
54 static const char kDebuggerNotPaused[] =
55 "Can only perform operation while paused.";
57 static const size_t kBreakpointHintMaxLength = 128;
58 static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;
60 static const int kMaxScriptFailedToParseScripts = 1000;
64 void TranslateLocation(protocol::Debugger::Location* location,
65 WasmTranslation* wasmTranslation) {
66 String16 scriptId = location->getScriptId();
67 int lineNumber = location->getLineNumber();
68 int columnNumber = location->getColumnNumber(-1);
69 if (wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
70 &scriptId, &lineNumber, &columnNumber)) {
71 location->setScriptId(std::move(scriptId));
72 location->setLineNumber(lineNumber);
73 location->setColumnNumber(columnNumber);
77 enum class BreakpointType {
87 String16 generateBreakpointId(BreakpointType type,
88 const String16& scriptSelector,
int lineNumber,
90 String16Builder builder;
91 builder.appendNumber(static_cast<int>(type));
93 builder.appendNumber(lineNumber);
95 builder.appendNumber(columnNumber);
97 builder.append(scriptSelector);
98 return builder.toString();
101 String16 generateBreakpointId(BreakpointType type,
103 String16Builder builder;
104 builder.appendNumber(static_cast<int>(type));
106 builder.appendNumber(v8::debug::GetDebuggingId(
function));
107 return builder.toString();
110 bool parseBreakpointId(
const String16& breakpointId, BreakpointType* type,
111 String16* scriptSelector =
nullptr,
112 int* lineNumber =
nullptr,
int* columnNumber =
nullptr) {
113 size_t typeLineSeparator = breakpointId.find(
':');
114 if (typeLineSeparator == String16::kNotFound)
return false;
116 int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
117 if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
118 rawType > static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
121 if (type) *type =
static_cast<BreakpointType
>(rawType);
122 if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
123 rawType ==
static_cast<int>(BreakpointType::kMonitorCommand) ||
124 rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
129 size_t lineColumnSeparator = breakpointId.find(
':', typeLineSeparator + 1);
130 if (lineColumnSeparator == String16::kNotFound)
return false;
131 size_t columnSelectorSeparator =
132 breakpointId.find(
':', lineColumnSeparator + 1);
133 if (columnSelectorSeparator == String16::kNotFound)
return false;
134 if (scriptSelector) {
135 *scriptSelector = breakpointId.substring(columnSelectorSeparator + 1);
138 *lineNumber = breakpointId
139 .substring(typeLineSeparator + 1,
140 lineColumnSeparator - typeLineSeparator - 1)
146 .substring(lineColumnSeparator + 1,
147 columnSelectorSeparator - lineColumnSeparator - 1)
153 bool positionComparator(
const std::pair<int, int>& a,
154 const std::pair<int, int>& b) {
155 if (a.first != b.first)
return a.first < b.first;
156 return a.second < b.second;
159 String16 breakpointHint(
const V8DebuggerScript& script,
int lineNumber,
161 int offset = script.offset(lineNumber, columnNumber);
162 if (offset == V8DebuggerScript::kNoOffset)
return String16();
164 script.source(offset, kBreakpointHintMaxLength).stripWhiteSpace();
165 for (
size_t i = 0;
i < hint.length(); ++
i) {
166 if (hint[
i] ==
'\r' || hint[
i] ==
'\n' || hint[
i] ==
';') {
167 return hint.substring(0,
i);
173 void adjustBreakpointLocation(
const V8DebuggerScript& script,
174 const String16& hint,
int* lineNumber,
176 if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
178 if (hint.isEmpty())
return;
179 intptr_t sourceOffset = script.offset(*lineNumber, *columnNumber);
180 if (sourceOffset == V8DebuggerScript::kNoOffset)
return;
182 intptr_t searchRegionOffset = std::max(
183 sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
184 size_t offset = sourceOffset - searchRegionOffset;
185 String16 searchArea = script.source(searchRegionOffset,
186 offset + kBreakpointHintMaxSearchOffset);
188 size_t nextMatch = searchArea.find(hint, offset);
189 size_t prevMatch = searchArea.reverseFind(hint, offset);
190 if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
194 if (nextMatch == String16::kNotFound) {
195 bestMatch = prevMatch;
196 }
else if (prevMatch == String16::kNotFound) {
197 bestMatch = nextMatch;
199 bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
201 bestMatch += searchRegionOffset;
203 script.location(static_cast<int>(bestMatch));
204 if (hintPosition.IsEmpty())
return;
205 *lineNumber = hintPosition.GetLineNumber();
206 *columnNumber = hintPosition.GetColumnNumber();
209 String16 breakLocationType(v8::debug::BreakLocationType type) {
211 case v8::debug::kCallBreakLocation:
212 return protocol::Debugger::BreakLocation::TypeEnum::Call;
213 case v8::debug::kReturnBreakLocation:
214 return protocol::Debugger::BreakLocation::TypeEnum::Return;
215 case v8::debug::kDebuggerStatementBreakLocation:
216 return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
217 case v8::debug::kCommonBreakLocation:
225 String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
227 case v8::debug::ScopeIterator::ScopeTypeGlobal:
228 return Scope::TypeEnum::Global;
229 case v8::debug::ScopeIterator::ScopeTypeLocal:
230 return Scope::TypeEnum::Local;
231 case v8::debug::ScopeIterator::ScopeTypeWith:
232 return Scope::TypeEnum::With;
233 case v8::debug::ScopeIterator::ScopeTypeClosure:
234 return Scope::TypeEnum::Closure;
235 case v8::debug::ScopeIterator::ScopeTypeCatch:
236 return Scope::TypeEnum::Catch;
237 case v8::debug::ScopeIterator::ScopeTypeBlock:
238 return Scope::TypeEnum::Block;
239 case v8::debug::ScopeIterator::ScopeTypeScript:
240 return Scope::TypeEnum::Script;
241 case v8::debug::ScopeIterator::ScopeTypeEval:
242 return Scope::TypeEnum::Eval;
243 case v8::debug::ScopeIterator::ScopeTypeModule:
244 return Scope::TypeEnum::Module;
253 InjectedScript* injectedScript,
254 std::unique_ptr<Array<Scope>>* scopes) {
255 *scopes = Array<Scope>::create();
256 if (!injectedScript)
return Response::OK();
257 if (iterator->Done())
return Response::OK();
259 String16 scriptId = String16::fromInteger(iterator->GetScriptId());
261 for (; !iterator->Done(); iterator->Advance()) {
262 std::unique_ptr<RemoteObject> object;
264 injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
265 WrapMode::kNoPreview, &object);
266 if (!result.isSuccess())
return result;
268 auto scope = Scope::create()
269 .setType(scopeType(iterator->GetType()))
270 .setObject(std::move(
object))
273 String16 name = toProtocolStringWithTypeCheck(
274 isolate, iterator->GetFunctionDebugName());
275 if (!name.isEmpty()) scope->setName(name);
277 if (iterator->HasLocationInfo()) {
279 scope->setStartLocation(protocol::Debugger::Location::create()
280 .setScriptId(scriptId)
281 .setLineNumber(start.GetLineNumber())
282 .setColumnNumber(start.GetColumnNumber())
286 scope->setEndLocation(protocol::Debugger::Location::create()
287 .setScriptId(scriptId)
288 .setLineNumber(end.GetLineNumber())
289 .setColumnNumber(end.GetColumnNumber())
292 (*scopes)->addItem(std::move(scope));
294 return Response::OK();
297 protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue*
object,
298 const String16& key) {
299 protocol::DictionaryValue* value =
object->getObject(key);
300 if (value)
return value;
301 std::unique_ptr<protocol::DictionaryValue> newDictionary =
302 protocol::DictionaryValue::create();
303 value = newDictionary.get();
304 object->setObject(key, std::move(newDictionary));
309 V8DebuggerAgentImpl::V8DebuggerAgentImpl(
310 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
311 protocol::DictionaryValue* state)
312 : m_inspector(session->inspector()),
313 m_debugger(m_inspector->debugger()),
317 m_frontend(frontendChannel),
318 m_isolate(m_inspector->isolate()) {}
320 V8DebuggerAgentImpl::~V8DebuggerAgentImpl() =
default;
322 void V8DebuggerAgentImpl::enableImpl() {
324 m_state->setBoolean(DebuggerAgentState::debuggerEnabled,
true);
325 m_debugger->enable();
327 std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts;
328 m_debugger->getCompiledScripts(m_session->contextGroupId(), compiledScripts);
329 for (
size_t i = 0;
i < compiledScripts.size();
i++)
330 didParseSource(std::move(compiledScripts[
i]),
true);
332 m_breakpointsActive =
true;
333 m_debugger->setBreakpointsActive(
true);
337 v8::debug::kException,
false,
false,
false);
341 Response V8DebuggerAgentImpl::enable(String16* outDebuggerId) {
342 *outDebuggerId = debuggerIdToString(
343 m_debugger->debuggerIdFor(m_session->contextGroupId()));
344 if (enabled())
return Response::OK();
346 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
347 return Response::Error(
"Script execution is prohibited");
350 return Response::OK();
353 Response V8DebuggerAgentImpl::disable() {
354 if (!enabled())
return Response::OK();
356 m_state->remove(DebuggerAgentState::breakpointsByRegex);
357 m_state->remove(DebuggerAgentState::breakpointsByUrl);
358 m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
359 m_state->remove(DebuggerAgentState::breakpointHints);
361 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
362 v8::debug::NoBreakOnException);
363 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
365 if (m_breakpointsActive) {
366 m_debugger->setBreakpointsActive(
false);
367 m_breakpointsActive =
false;
369 m_blackboxedPositions.clear();
370 m_blackboxPattern.reset();
371 resetBlackboxedStateCache();
373 for (
const auto& it : m_debuggerBreakpointIdToBreakpointId) {
374 v8::debug::RemoveBreakpoint(m_isolate, it.first);
376 m_breakpointIdToDebuggerBreakpointIds.clear();
377 m_debuggerBreakpointIdToBreakpointId.clear();
378 m_debugger->setAsyncCallStackDepth(
this, 0);
380 m_skipAllPauses =
false;
381 m_state->setBoolean(DebuggerAgentState::skipAllPauses,
false);
382 m_state->remove(DebuggerAgentState::blackboxPattern);
384 m_state->setBoolean(DebuggerAgentState::debuggerEnabled,
false);
385 m_debugger->disable();
386 return Response::OK();
389 void V8DebuggerAgentImpl::restore() {
391 if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled,
false))
393 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
398 int pauseState = v8::debug::NoBreakOnException;
399 m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
400 setPauseOnExceptionsImpl(pauseState);
403 m_state->booleanProperty(DebuggerAgentState::skipAllPauses,
false);
405 int asyncCallStackDepth = 0;
406 m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
407 &asyncCallStackDepth);
408 m_debugger->setAsyncCallStackDepth(
this, asyncCallStackDepth);
410 String16 blackboxPattern;
411 if (m_state->getString(DebuggerAgentState::blackboxPattern,
413 setBlackboxPattern(blackboxPattern);
417 Response V8DebuggerAgentImpl::setBreakpointsActive(
bool active) {
418 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
419 if (m_breakpointsActive == active)
return Response::OK();
420 m_breakpointsActive = active;
421 m_debugger->setBreakpointsActive(active);
422 if (!active && !m_breakReason.empty()) {
424 m_debugger->setPauseOnNextCall(
false, m_session->contextGroupId());
426 return Response::OK();
429 Response V8DebuggerAgentImpl::setSkipAllPauses(
bool skip) {
430 m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
431 m_skipAllPauses = skip;
432 return Response::OK();
435 static bool matches(V8InspectorImpl* inspector,
const V8DebuggerScript& script,
436 BreakpointType type,
const String16& selector) {
438 case BreakpointType::kByUrl:
439 return script.sourceURL() == selector;
440 case BreakpointType::kByScriptHash:
441 return script.hash() == selector;
442 case BreakpointType::kByUrlRegex: {
443 V8Regex regex(inspector, selector,
true);
444 return regex.match(script.sourceURL()) != -1;
452 Response V8DebuggerAgentImpl::setBreakpointByUrl(
453 int lineNumber, Maybe<String16> optionalURL,
454 Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash,
455 Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition,
456 String16* outBreakpointId,
457 std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
458 *locations = Array<protocol::Debugger::Location>::create();
460 int specified = (optionalURL.isJust() ? 1 : 0) +
461 (optionalURLRegex.isJust() ? 1 : 0) +
462 (optionalScriptHash.isJust() ? 1 : 0);
463 if (specified != 1) {
464 return Response::Error(
465 "Either url or urlRegex or scriptHash must be specified.");
467 int columnNumber = 0;
468 if (optionalColumnNumber.isJust()) {
469 columnNumber = optionalColumnNumber.fromJust();
470 if (columnNumber < 0)
return Response::Error(
"Incorrect column number");
473 BreakpointType type = BreakpointType::kByUrl;
475 if (optionalURLRegex.isJust()) {
476 selector = optionalURLRegex.fromJust();
477 type = BreakpointType::kByUrlRegex;
478 }
else if (optionalURL.isJust()) {
479 selector = optionalURL.fromJust();
480 type = BreakpointType::kByUrl;
481 }
else if (optionalScriptHash.isJust()) {
482 selector = optionalScriptHash.fromJust();
483 type = BreakpointType::kByScriptHash;
486 String16 condition = optionalCondition.fromMaybe(String16());
487 String16 breakpointId =
488 generateBreakpointId(type, selector, lineNumber, columnNumber);
489 protocol::DictionaryValue* breakpoints;
491 case BreakpointType::kByUrlRegex:
493 getOrCreateObject(m_state, DebuggerAgentState::breakpointsByRegex);
495 case BreakpointType::kByUrl:
496 breakpoints = getOrCreateObject(
497 getOrCreateObject(m_state, DebuggerAgentState::breakpointsByUrl),
500 case BreakpointType::kByScriptHash:
501 breakpoints = getOrCreateObject(
502 getOrCreateObject(m_state,
503 DebuggerAgentState::breakpointsByScriptHash),
510 if (breakpoints->get(breakpointId)) {
511 return Response::Error(
"Breakpoint at specified location already exists.");
515 for (
const auto& script : m_scripts) {
516 if (!matches(m_inspector, *script.second, type, selector))
continue;
517 if (!hint.isEmpty()) {
518 adjustBreakpointLocation(*script.second, hint, &lineNumber,
521 std::unique_ptr<protocol::Debugger::Location> location = setBreakpointImpl(
522 breakpointId, script.first, condition, lineNumber, columnNumber);
523 if (location && type != BreakpointType::kByUrlRegex) {
524 hint = breakpointHint(*script.second, lineNumber, columnNumber);
526 if (location) (*locations)->addItem(std::move(location));
528 breakpoints->setString(breakpointId, condition);
529 if (!hint.isEmpty()) {
530 protocol::DictionaryValue* breakpointHints =
531 getOrCreateObject(m_state, DebuggerAgentState::breakpointHints);
532 breakpointHints->setString(breakpointId, hint);
534 *outBreakpointId = breakpointId;
535 return Response::OK();
538 Response V8DebuggerAgentImpl::setBreakpoint(
539 std::unique_ptr<protocol::Debugger::Location> location,
540 Maybe<String16> optionalCondition, String16* outBreakpointId,
541 std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
542 String16 breakpointId = generateBreakpointId(
543 BreakpointType::kByScriptId, location->getScriptId(),
544 location->getLineNumber(), location->getColumnNumber(0));
545 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
546 m_breakpointIdToDebuggerBreakpointIds.end()) {
547 return Response::Error(
"Breakpoint at specified location already exists.");
549 *actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
550 optionalCondition.fromMaybe(String16()),
551 location->getLineNumber(),
552 location->getColumnNumber(0));
553 if (!*actualLocation)
return Response::Error(
"Could not resolve breakpoint");
554 *outBreakpointId = breakpointId;
555 return Response::OK();
558 Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
559 const String16& functionObjectId, Maybe<String16> optionalCondition,
560 String16* outBreakpointId) {
561 InjectedScript::ObjectScope scope(m_session, functionObjectId);
562 Response response = scope.initialize();
563 if (!response.isSuccess())
return response;
564 if (!scope.object()->IsFunction()) {
565 return Response::Error(
"Could not find function with given id");
569 String16 breakpointId =
570 generateBreakpointId(BreakpointType::kBreakpointAtEntry,
function);
571 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
572 m_breakpointIdToDebuggerBreakpointIds.end()) {
573 return Response::Error(
"Breakpoint at specified location already exists.");
576 toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
577 setBreakpointImpl(breakpointId,
function, condition);
578 *outBreakpointId = breakpointId;
579 return Response::OK();
582 Response V8DebuggerAgentImpl::removeBreakpoint(
const String16& breakpointId) {
583 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
586 if (!parseBreakpointId(breakpointId, &type, &selector)) {
587 return Response::OK();
589 protocol::DictionaryValue* breakpoints =
nullptr;
591 case BreakpointType::kByUrl: {
592 protocol::DictionaryValue* breakpointsByUrl =
593 m_state->getObject(DebuggerAgentState::breakpointsByUrl);
594 if (breakpointsByUrl) {
595 breakpoints = breakpointsByUrl->getObject(selector);
598 case BreakpointType::kByScriptHash: {
599 protocol::DictionaryValue* breakpointsByScriptHash =
600 m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
601 if (breakpointsByScriptHash) {
602 breakpoints = breakpointsByScriptHash->getObject(selector);
605 case BreakpointType::kByUrlRegex:
606 breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
611 if (breakpoints) breakpoints->remove(breakpointId);
612 protocol::DictionaryValue* breakpointHints =
613 m_state->getObject(DebuggerAgentState::breakpointHints);
614 if (breakpointHints) breakpointHints->remove(breakpointId);
615 removeBreakpointImpl(breakpointId);
616 return Response::OK();
619 void V8DebuggerAgentImpl::removeBreakpointImpl(
const String16& breakpointId) {
621 BreakpointIdToDebuggerBreakpointIdsMap::iterator
622 debuggerBreakpointIdsIterator =
623 m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
624 if (debuggerBreakpointIdsIterator ==
625 m_breakpointIdToDebuggerBreakpointIds.end()) {
628 for (
const auto&
id : debuggerBreakpointIdsIterator->second) {
629 v8::debug::RemoveBreakpoint(m_isolate,
id);
630 m_debuggerBreakpointIdToBreakpointId.erase(
id);
632 m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
635 Response V8DebuggerAgentImpl::getPossibleBreakpoints(
636 std::unique_ptr<protocol::Debugger::Location> start,
637 Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
638 std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
640 String16 scriptId = start->getScriptId();
642 if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
643 return Response::Error(
644 "start.lineNumber and start.columnNumber should be >= 0");
647 start->getColumnNumber(0));
650 if (end.fromJust()->getScriptId() != scriptId)
651 return Response::Error(
"Locations should contain the same scriptId");
652 int line = end.fromJust()->getLineNumber();
653 int column = end.fromJust()->getColumnNumber(0);
654 if (line < 0 || column < 0)
655 return Response::Error(
656 "end.lineNumber and end.columnNumber should be >= 0");
659 auto it = m_scripts.find(scriptId);
660 if (it == m_scripts.end())
return Response::Error(
"Script not found");
661 std::vector<v8::debug::BreakLocation> v8Locations;
664 int contextId = it->second->executionContextId();
665 InspectedContext* inspected = m_inspector->getContext(contextId);
667 return Response::Error(
"Cannot retrive script context");
670 v8::MicrotasksScope microtasks(m_isolate,
671 v8::MicrotasksScope::kDoNotRunMicrotasks);
672 v8::TryCatch tryCatch(m_isolate);
673 it->second->getPossibleBreakpoints(
674 v8Start, v8End, restrictToFunction.fromMaybe(
false), &v8Locations);
677 *locations = protocol::Array<protocol::Debugger::BreakLocation>::create();
678 for (
size_t i = 0;
i < v8Locations.size(); ++
i) {
679 std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
680 protocol::Debugger::BreakLocation::create()
681 .setScriptId(scriptId)
682 .setLineNumber(v8Locations[
i].GetLineNumber())
683 .setColumnNumber(v8Locations[
i].GetColumnNumber())
685 if (v8Locations[
i].type() != v8::debug::kCommonBreakLocation) {
686 breakLocation->setType(breakLocationType(v8Locations[
i].type()));
688 (*locations)->addItem(std::move(breakLocation));
690 return Response::OK();
693 Response V8DebuggerAgentImpl::continueToLocation(
694 std::unique_ptr<protocol::Debugger::Location> location,
695 Maybe<String16> targetCallFrames) {
696 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
697 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
698 ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
699 if (it == m_scripts.end()) {
700 return Response::Error(
"Cannot continue to specified location");
702 V8DebuggerScript* script = it->second.get();
703 int contextId = script->executionContextId();
704 InspectedContext* inspected = m_inspector->getContext(contextId);
706 return Response::Error(
"Cannot continue to specified location");
708 return m_debugger->continueToLocation(
709 m_session->contextGroupId(), script, std::move(location),
710 targetCallFrames.fromMaybe(
711 protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
714 Response V8DebuggerAgentImpl::getStackTrace(
715 std::unique_ptr<protocol::Runtime::StackTraceId> inStackTraceId,
716 std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
718 int64_t id = inStackTraceId->getId().toInteger64(&isOk);
719 std::pair<int64_t, int64_t> debuggerId;
720 if (inStackTraceId->hasDebuggerId()) {
722 m_debugger->debuggerIdFor(inStackTraceId->getDebuggerId(String16()));
724 debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
726 V8StackTraceId v8StackTraceId(
id, debuggerId);
727 if (!isOk || v8StackTraceId.IsInvalid()) {
728 return Response::Error(
"Invalid stack trace id");
731 m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
733 return Response::Error(
"Stack trace with given id is not found");
735 *outStackTrace = stack->buildInspectorObject(
736 m_debugger, m_debugger->maxAsyncCallChainDepth());
737 return Response::OK();
740 bool V8DebuggerAgentImpl::isFunctionBlackboxed(
const String16& scriptId,
743 ScriptsMap::iterator it = m_scripts.find(scriptId);
744 if (it == m_scripts.end()) {
748 if (m_blackboxPattern) {
749 const String16& scriptSourceURL = it->second->sourceURL();
750 if (!scriptSourceURL.isEmpty() &&
751 m_blackboxPattern->match(scriptSourceURL) != -1)
754 auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
755 if (itBlackboxedPositions == m_blackboxedPositions.end())
return false;
757 const std::vector<std::pair<int, int>>& ranges =
758 itBlackboxedPositions->second;
759 auto itStartRange = std::lower_bound(
760 ranges.begin(), ranges.end(),
761 std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
763 auto itEndRange = std::lower_bound(
764 itStartRange, ranges.end(),
765 std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
770 return itStartRange == itEndRange &&
771 std::distance(ranges.begin(), itStartRange) % 2;
774 bool V8DebuggerAgentImpl::acceptsPause(
bool isOOMBreak)
const {
775 return enabled() && (isOOMBreak || !m_skipAllPauses);
778 std::unique_ptr<protocol::Debugger::Location>
779 V8DebuggerAgentImpl::setBreakpointImpl(
const String16& breakpointId,
780 const String16& scriptId,
781 const String16& condition,
782 int lineNumber,
int columnNumber) {
786 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
787 if (scriptIterator == m_scripts.end())
return nullptr;
788 V8DebuggerScript* script = scriptIterator->second.get();
789 if (lineNumber < script->startLine() || script->endLine() < lineNumber) {
795 int contextId = script->executionContextId();
796 InspectedContext* inspected = m_inspector->getContext(contextId);
797 if (!inspected)
return nullptr;
801 if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
806 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
807 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
808 debuggerBreakpointId);
810 return protocol::Debugger::Location::create()
811 .setScriptId(scriptId)
812 .setLineNumber(location.GetLineNumber())
813 .setColumnNumber(location.GetColumnNumber())
817 void V8DebuggerAgentImpl::setBreakpointImpl(
const String16& breakpointId,
821 if (!v8::debug::SetFunctionBreakpoint(
function, condition,
822 &debuggerBreakpointId)) {
825 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
826 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
827 debuggerBreakpointId);
830 Response V8DebuggerAgentImpl::searchInContent(
831 const String16& scriptId,
const String16& query,
832 Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
833 std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
835 ScriptsMap::iterator it = m_scripts.find(scriptId);
836 if (it == m_scripts.end())
837 return Response::Error(
"No script for id: " + scriptId);
839 std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
840 searchInTextByLinesImpl(m_session, it->second->source(0), query,
841 optionalCaseSensitive.fromMaybe(
false),
842 optionalIsRegex.fromMaybe(
false));
843 *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
844 for (
size_t i = 0;
i < matches.size(); ++
i)
845 (*results)->addItem(std::move(matches[
i]));
846 return Response::OK();
849 Response V8DebuggerAgentImpl::setScriptSource(
850 const String16& scriptId,
const String16& newContent, Maybe<bool> dryRun,
851 Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
852 Maybe<bool>* stackChanged,
853 Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
854 Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
855 Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
856 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
858 ScriptsMap::iterator it = m_scripts.find(scriptId);
859 if (it == m_scripts.end()) {
860 return Response::Error(
"No script with given id found");
862 int contextId = it->second->executionContextId();
863 InspectedContext* inspected = m_inspector->getContext(contextId);
865 return Response::InternalError();
872 it->second->setSource(newContent, dryRun.fromMaybe(
false), &result);
873 if (result.status != v8::debug::LiveEditResult::OK) {
874 *optOutCompileError =
875 protocol::Runtime::ExceptionDetails::create()
876 .setExceptionId(m_inspector->nextExceptionId())
877 .setText(toProtocolString(m_isolate, result.message))
878 .setLineNumber(result.line_number != -1 ? result.line_number - 1
880 .setColumnNumber(result.column_number != -1 ? result.column_number
883 return Response::OK();
885 *stackChanged = result.stack_changed;
887 std::unique_ptr<Array<CallFrame>> callFrames;
888 Response response = currentCallFrames(&callFrames);
889 if (!response.isSuccess())
return response;
890 *newCallFrames = std::move(callFrames);
891 *asyncStackTrace = currentAsyncStackTrace();
892 *asyncStackTraceId = currentExternalStackTrace();
893 return Response::OK();
896 Response V8DebuggerAgentImpl::restartFrame(
897 const String16& callFrameId,
898 std::unique_ptr<Array<CallFrame>>* newCallFrames,
899 Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
900 Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
901 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
902 InjectedScript::CallFrameScope scope(m_session, callFrameId);
903 Response response = scope.initialize();
904 if (!response.isSuccess())
return response;
905 int frameOrdinal =
static_cast<int>(scope.frameOrdinal());
906 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
908 return Response::Error(
"Could not find call frame with given id");
910 if (!it->Restart()) {
911 return Response::InternalError();
913 response = currentCallFrames(newCallFrames);
914 if (!response.isSuccess())
return response;
915 *asyncStackTrace = currentAsyncStackTrace();
916 *asyncStackTraceId = currentExternalStackTrace();
917 return Response::OK();
920 Response V8DebuggerAgentImpl::getScriptSource(
const String16& scriptId,
921 String16* scriptSource) {
922 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
923 ScriptsMap::iterator it = m_scripts.find(scriptId);
924 if (it == m_scripts.end())
925 return Response::Error(
"No script for id: " + scriptId);
926 *scriptSource = it->second->source(0);
927 return Response::OK();
930 void V8DebuggerAgentImpl::pushBreakDetails(
931 const String16& breakReason,
932 std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
933 m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
936 void V8DebuggerAgentImpl::popBreakDetails() {
937 if (m_breakReason.empty())
return;
938 m_breakReason.pop_back();
941 void V8DebuggerAgentImpl::clearBreakDetails() {
942 std::vector<BreakReason> emptyBreakReason;
943 m_breakReason.swap(emptyBreakReason);
946 void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
947 const String16& breakReason,
948 std::unique_ptr<protocol::DictionaryValue> data) {
949 if (isPaused() || !acceptsPause(
false) || !m_breakpointsActive)
return;
950 if (m_breakReason.empty()) {
951 m_debugger->setPauseOnNextCall(
true, m_session->contextGroupId());
953 pushBreakDetails(breakReason, std::move(data));
956 void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
957 if (isPaused() || !acceptsPause(
false) || !m_breakpointsActive)
return;
958 if (m_breakReason.size() == 1) {
959 m_debugger->setPauseOnNextCall(
false, m_session->contextGroupId());
964 Response V8DebuggerAgentImpl::pause() {
965 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
966 if (isPaused())
return Response::OK();
967 if (m_debugger->canBreakProgram()) {
968 m_debugger->interruptAndBreak(m_session->contextGroupId());
970 if (m_breakReason.empty()) {
971 m_debugger->setPauseOnNextCall(
true, m_session->contextGroupId());
973 pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other,
nullptr);
975 return Response::OK();
978 Response V8DebuggerAgentImpl::resume() {
979 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
980 m_session->releaseObjectGroup(kBacktraceObjectGroup);
981 m_debugger->continueProgram(m_session->contextGroupId());
982 return Response::OK();
985 Response V8DebuggerAgentImpl::stepOver() {
986 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
987 m_session->releaseObjectGroup(kBacktraceObjectGroup);
988 m_debugger->stepOverStatement(m_session->contextGroupId());
989 return Response::OK();
992 Response V8DebuggerAgentImpl::stepInto(Maybe<bool> inBreakOnAsyncCall) {
993 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
994 m_session->releaseObjectGroup(kBacktraceObjectGroup);
995 m_debugger->stepIntoStatement(m_session->contextGroupId(),
996 inBreakOnAsyncCall.fromMaybe(
false));
997 return Response::OK();
1000 Response V8DebuggerAgentImpl::stepOut() {
1001 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
1002 m_session->releaseObjectGroup(kBacktraceObjectGroup);
1003 m_debugger->stepOutOfFunction(m_session->contextGroupId());
1004 return Response::OK();
1007 void V8DebuggerAgentImpl::scheduleStepIntoAsync(
1008 std::unique_ptr<ScheduleStepIntoAsyncCallback> callback) {
1010 callback->sendFailure(Response::Error(kDebuggerNotPaused));
1013 m_debugger->scheduleStepIntoAsync(std::move(callback),
1014 m_session->contextGroupId());
1017 Response V8DebuggerAgentImpl::pauseOnAsyncCall(
1018 std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1020 int64_t stackTraceId = inParentStackTraceId->getId().toInteger64(&isOk);
1022 return Response::Error(
"Invalid stack trace id");
1024 m_debugger->pauseOnAsyncCall(m_session->contextGroupId(), stackTraceId,
1025 inParentStackTraceId->getDebuggerId(String16()));
1026 return Response::OK();
1029 Response V8DebuggerAgentImpl::setPauseOnExceptions(
1030 const String16& stringPauseState) {
1031 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
1032 v8::debug::ExceptionBreakState pauseState;
1033 if (stringPauseState ==
"none") {
1034 pauseState = v8::debug::NoBreakOnException;
1035 }
else if (stringPauseState ==
"all") {
1036 pauseState = v8::debug::BreakOnAnyException;
1037 }
else if (stringPauseState ==
"uncaught") {
1038 pauseState = v8::debug::BreakOnUncaughtException;
1040 return Response::Error(
"Unknown pause on exceptions mode: " +
1043 setPauseOnExceptionsImpl(pauseState);
1044 return Response::OK();
1047 void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(
int pauseState) {
1050 m_debugger->setPauseOnExceptionsState(
1051 static_cast<v8::debug::ExceptionBreakState>(pauseState));
1052 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
1055 Response V8DebuggerAgentImpl::evaluateOnCallFrame(
1056 const String16& callFrameId,
const String16& expression,
1057 Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
1058 Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
1059 Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
1060 std::unique_ptr<RemoteObject>* result,
1061 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
1062 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
1063 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1064 Response response = scope.initialize();
1065 if (!response.isSuccess())
return response;
1066 if (includeCommandLineAPI.fromMaybe(
false)) scope.installCommandLineAPI();
1067 if (silent.fromMaybe(
false)) scope.ignoreExceptionsAndMuteConsole();
1069 int frameOrdinal =
static_cast<int>(scope.frameOrdinal());
1070 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1072 return Response::Error(
"Could not find call frame with given id");
1077 V8InspectorImpl::EvaluateScope evaluateScope(m_isolate);
1078 if (timeout.isJust()) {
1079 response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
1080 if (!response.isSuccess())
return response;
1082 maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
1083 throwOnSideEffect.fromMaybe(
false));
1087 response = scope.initialize();
1088 if (!response.isSuccess())
return response;
1089 WrapMode mode = generatePreview.fromMaybe(
false) ? WrapMode::kWithPreview
1090 : WrapMode::kNoPreview;
1091 if (returnByValue.fromMaybe(
false)) mode = WrapMode::kForceValue;
1092 return scope.injectedScript()->wrapEvaluateResult(
1093 maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(
""), mode,
1094 result, exceptionDetails);
1097 Response V8DebuggerAgentImpl::setVariableValue(
1098 int scopeNumber,
const String16& variableName,
1099 std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
1100 const String16& callFrameId) {
1101 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
1102 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
1103 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1104 Response response = scope.initialize();
1105 if (!response.isSuccess())
return response;
1107 response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
1109 if (!response.isSuccess())
return response;
1111 int frameOrdinal =
static_cast<int>(scope.frameOrdinal());
1112 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1114 return Response::Error(
"Could not find call frame with given id");
1116 auto scopeIterator = it->GetScopeIterator();
1117 while (!scopeIterator->Done() && scopeNumber > 0) {
1119 scopeIterator->Advance();
1121 if (scopeNumber != 0) {
1122 return Response::Error(
"Could not find scope with given number");
1125 if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
1127 scope.tryCatch().HasCaught()) {
1128 return Response::InternalError();
1130 return Response::OK();
1133 Response V8DebuggerAgentImpl::setReturnValue(
1134 std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
1135 if (!enabled())
return Response::Error(kDebuggerNotEnabled);
1136 if (!isPaused())
return Response::Error(kDebuggerNotPaused);
1137 auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1138 if (iterator->Done()) {
1139 return Response::Error(
"Could not find top call frame");
1141 if (iterator->GetReturnValue().IsEmpty()) {
1142 return Response::Error(
1143 "Could not update return value at non-return position");
1145 InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
1146 Response response = scope.initialize();
1147 if (!response.isSuccess())
return response;
1149 response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
1151 if (!response.isSuccess())
return response;
1152 v8::debug::SetReturnValue(m_isolate, newValue);
1153 return Response::OK();
1156 Response V8DebuggerAgentImpl::setAsyncCallStackDepth(
int depth) {
1157 if (!enabled() && !m_session->runtimeAgent()->enabled()) {
1158 return Response::Error(kDebuggerNotEnabled);
1160 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
1161 m_debugger->setAsyncCallStackDepth(
this, depth);
1162 return Response::OK();
1165 Response V8DebuggerAgentImpl::setBlackboxPatterns(
1166 std::unique_ptr<protocol::Array<String16>> patterns) {
1167 if (!patterns->length()) {
1168 m_blackboxPattern =
nullptr;
1169 resetBlackboxedStateCache();
1170 m_state->remove(DebuggerAgentState::blackboxPattern);
1171 return Response::OK();
1174 String16Builder patternBuilder;
1175 patternBuilder.append(
'(');
1176 for (
size_t i = 0;
i < patterns->length() - 1; ++
i) {
1177 patternBuilder.append(patterns->get(
i));
1178 patternBuilder.append(
"|");
1180 patternBuilder.append(patterns->get(patterns->length() - 1));
1181 patternBuilder.append(
')');
1182 String16 pattern = patternBuilder.toString();
1183 Response response = setBlackboxPattern(pattern);
1184 if (!response.isSuccess())
return response;
1185 resetBlackboxedStateCache();
1186 m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
1187 return Response::OK();
1190 Response V8DebuggerAgentImpl::setBlackboxPattern(
const String16& pattern) {
1191 std::unique_ptr<V8Regex> regex(
new V8Regex(
1192 m_inspector, pattern,
true ,
false ));
1193 if (!regex->isValid())
1194 return Response::Error(
"Pattern parser error: " + regex->errorMessage());
1195 m_blackboxPattern = std::move(regex);
1196 return Response::OK();
1199 void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
1200 for (
const auto& it : m_scripts) {
1201 it.second->resetBlackboxedStateCache();
1205 Response V8DebuggerAgentImpl::setBlackboxedRanges(
1206 const String16& scriptId,
1207 std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
1209 auto it = m_scripts.find(scriptId);
1210 if (it == m_scripts.end())
1211 return Response::Error(
"No script with passed id.");
1213 if (!inPositions->length()) {
1214 m_blackboxedPositions.erase(scriptId);
1215 it->second->resetBlackboxedStateCache();
1216 return Response::OK();
1219 std::vector<std::pair<int, int>> positions;
1220 positions.reserve(inPositions->length());
1221 for (
size_t i = 0;
i < inPositions->length(); ++
i) {
1222 protocol::Debugger::ScriptPosition* position = inPositions->get(
i);
1223 if (position->getLineNumber() < 0)
1224 return Response::Error(
"Position missing 'line' or 'line' < 0.");
1225 if (position->getColumnNumber() < 0)
1226 return Response::Error(
"Position missing 'column' or 'column' < 0.");
1227 positions.push_back(
1228 std::make_pair(position->getLineNumber(), position->getColumnNumber()));
1231 for (
size_t i = 1;
i < positions.size(); ++
i) {
1232 if (positions[
i - 1].first < positions[
i].first)
continue;
1233 if (positions[
i - 1].first == positions[
i].first &&
1234 positions[
i - 1].second < positions[
i].second)
1236 return Response::Error(
1237 "Input positions array is not sorted or contains duplicate values.");
1240 m_blackboxedPositions[scriptId] = positions;
1241 it->second->resetBlackboxedStateCache();
1242 return Response::OK();
1245 Response V8DebuggerAgentImpl::currentCallFrames(
1246 std::unique_ptr<Array<CallFrame>>* result) {
1248 *result = Array<CallFrame>::create();
1249 return Response::OK();
1252 *result = Array<CallFrame>::create();
1253 auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1254 int frameOrdinal = 0;
1255 for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
1256 int contextId = iterator->GetContextId();
1257 InjectedScript* injectedScript =
nullptr;
1258 if (contextId) m_session->findInjectedScript(contextId, injectedScript);
1259 String16 callFrameId =
1260 RemoteCallFrameId::serialize(contextId, frameOrdinal);
1264 std::unique_ptr<Array<Scope>> scopes;
1265 auto scopeIterator = iterator->GetScopeIterator();
1267 buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
1268 if (!res.isSuccess())
return res;
1270 std::unique_ptr<RemoteObject> protocolReceiver;
1271 if (injectedScript) {
1273 if (iterator->GetReceiver().ToLocal(&receiver)) {
1275 injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
1276 WrapMode::kNoPreview, &protocolReceiver);
1277 if (!res.isSuccess())
return res;
1280 if (!protocolReceiver) {
1281 protocolReceiver = RemoteObject::create()
1282 .setType(RemoteObject::TypeEnum::Undefined)
1288 std::unique_ptr<protocol::Debugger::Location> location =
1289 protocol::Debugger::Location::create()
1290 .setScriptId(String16::fromInteger(script->Id()))
1291 .setLineNumber(loc.GetLineNumber())
1292 .setColumnNumber(loc.GetColumnNumber())
1294 TranslateLocation(location.get(), m_debugger->wasmTranslation());
1295 String16 scriptId = String16::fromInteger(script->Id());
1296 ScriptsMap::iterator scriptIterator =
1297 m_scripts.find(location->getScriptId());
1299 if (scriptIterator != m_scripts.end()) {
1300 url = scriptIterator->second->sourceURL();
1303 auto frame = CallFrame::create()
1304 .setCallFrameId(callFrameId)
1305 .setFunctionName(toProtocolString(
1306 m_isolate, iterator->GetFunctionDebugName()))
1307 .setLocation(std::move(location))
1309 .setScopeChain(std::move(scopes))
1310 .setThis(std::move(protocolReceiver))
1315 frame->setFunctionLocation(
1316 protocol::Debugger::Location::create()
1317 .setScriptId(String16::fromInteger(func->
ScriptId()))
1324 if (!returnValue.
IsEmpty() && injectedScript) {
1325 std::unique_ptr<RemoteObject> value;
1326 res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
1327 WrapMode::kNoPreview, &value);
1328 if (!res.isSuccess())
return res;
1329 frame->setReturnValue(std::move(value));
1331 (*result)->addItem(std::move(frame));
1333 return Response::OK();
1336 std::unique_ptr<protocol::Runtime::StackTrace>
1337 V8DebuggerAgentImpl::currentAsyncStackTrace() {
1338 std::shared_ptr<AsyncStackTrace> asyncParent =
1339 m_debugger->currentAsyncParent();
1340 if (!asyncParent)
return nullptr;
1341 return asyncParent->buildInspectorObject(
1342 m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
1345 std::unique_ptr<protocol::Runtime::StackTraceId>
1346 V8DebuggerAgentImpl::currentExternalStackTrace() {
1347 V8StackTraceId externalParent = m_debugger->currentExternalParent();
1348 if (externalParent.IsInvalid())
return nullptr;
1349 return protocol::Runtime::StackTraceId::create()
1350 .setId(stackTraceIdToString(externalParent.id))
1351 .setDebuggerId(debuggerIdToString(externalParent.debugger_id))
1355 std::unique_ptr<protocol::Runtime::StackTraceId>
1356 V8DebuggerAgentImpl::currentScheduledAsyncCall() {
1358 m_debugger->scheduledAsyncCall();
1359 if (scheduledAsyncCall.IsInvalid())
return nullptr;
1360 std::unique_ptr<protocol::Runtime::StackTraceId> asyncCallStackTrace =
1361 protocol::Runtime::StackTraceId::create()
1362 .setId(stackTraceIdToString(scheduledAsyncCall.id))
1365 if (scheduledAsyncCall.debugger_id.first ||
1366 scheduledAsyncCall.debugger_id.second) {
1367 asyncCallStackTrace->setDebuggerId(
1368 debuggerIdToString(scheduledAsyncCall.debugger_id));
1370 return asyncCallStackTrace;
1373 bool V8DebuggerAgentImpl::isPaused()
const {
1374 return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
1377 void V8DebuggerAgentImpl::didParseSource(
1378 std::unique_ptr<V8DebuggerScript> script,
bool success) {
1381 DCHECK(!script->isSourceLoadedLazily());
1382 String16 scriptSource = script->source(0);
1383 script->setSourceURL(findSourceURL(scriptSource,
false));
1384 script->setSourceMappingURL(findSourceMapURL(scriptSource,
false));
1387 int contextId = script->executionContextId();
1388 int contextGroupId = m_inspector->contextGroupId(contextId);
1389 InspectedContext* inspected =
1390 m_inspector->getContext(contextGroupId, contextId);
1391 std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1395 executionContextAuxData = protocol::DictionaryValue::cast(
1396 protocol::StringUtil::parseJSON(inspected->auxData()));
1398 bool isLiveEdit = script->isLiveEdit();
1399 bool hasSourceURLComment = script->hasSourceURLComment();
1400 bool isModule = script->isModule();
1401 String16 scriptId = script->scriptId();
1402 String16 scriptURL = script->sourceURL();
1404 m_scripts[scriptId] = std::move(script);
1406 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1407 DCHECK(scriptIterator != m_scripts.end());
1408 V8DebuggerScript* scriptRef = scriptIterator->second.get();
1413 scriptRef->resetBlackboxedStateCache();
1415 Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
1416 Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
1417 std::move(executionContextAuxData));
1418 const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit :
nullptr;
1419 const bool* hasSourceURLParam =
1420 hasSourceURLComment ? &hasSourceURLComment :
nullptr;
1421 const bool* isModuleParam = isModule ? &isModule :
nullptr;
1422 std::unique_ptr<V8StackTraceImpl> stack =
1423 V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
1424 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
1425 stack && !stack->isEmpty()
1426 ? stack->buildInspectorObjectImpl(m_debugger, 0)
1431 if (scriptRef->isSourceLoadedLazily()) {
1432 m_frontend.scriptParsed(
1433 scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(),
1434 std::move(executionContextAuxDataParam), isLiveEditParam,
1435 std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0,
1436 std::move(stackTrace));
1438 m_frontend.scriptParsed(
1439 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1440 scriptRef->endLine(), scriptRef->endColumn(), contextId,
1441 scriptRef->hash(), std::move(executionContextAuxDataParam),
1442 isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
1443 isModuleParam, scriptRef->length(), std::move(stackTrace));
1446 m_frontend.scriptFailedToParse(
1447 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1448 scriptRef->endLine(), scriptRef->endColumn(), contextId,
1449 scriptRef->hash(), std::move(executionContextAuxDataParam),
1450 std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
1451 scriptRef->length(), std::move(stackTrace));
1455 if (scriptURL.isEmpty()) {
1456 m_failedToParseAnonymousScriptIds.push_back(scriptId);
1457 cleanupOldFailedToParseAnonymousScriptsIfNeeded();
1462 std::vector<protocol::DictionaryValue*> potentialBreakpoints;
1463 if (!scriptURL.isEmpty()) {
1464 protocol::DictionaryValue* breakpointsByUrl =
1465 m_state->getObject(DebuggerAgentState::breakpointsByUrl);
1466 if (breakpointsByUrl) {
1467 potentialBreakpoints.push_back(breakpointsByUrl->getObject(scriptURL));
1469 potentialBreakpoints.push_back(
1470 m_state->getObject(DebuggerAgentState::breakpointsByRegex));
1472 protocol::DictionaryValue* breakpointsByScriptHash =
1473 m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
1474 if (breakpointsByScriptHash) {
1475 potentialBreakpoints.push_back(
1476 breakpointsByScriptHash->getObject(scriptRef->hash()));
1478 protocol::DictionaryValue* breakpointHints =
1479 m_state->getObject(DebuggerAgentState::breakpointHints);
1480 for (
auto breakpoints : potentialBreakpoints) {
1481 if (!breakpoints)
continue;
1482 for (
size_t i = 0;
i < breakpoints->size(); ++
i) {
1483 auto breakpointWithCondition = breakpoints->at(
i);
1484 String16 breakpointId = breakpointWithCondition.first;
1486 BreakpointType type;
1489 int columnNumber = 0;
1490 parseBreakpointId(breakpointId, &type, &selector, &lineNumber,
1493 if (!matches(m_inspector, *scriptRef, type, selector))
continue;
1495 breakpointWithCondition.second->asString(&condition);
1498 breakpointHints && breakpointHints->getString(breakpointId, &hint);
1500 adjustBreakpointLocation(*scriptRef, hint, &lineNumber, &columnNumber);
1502 std::unique_ptr<protocol::Debugger::Location> location =
1503 setBreakpointImpl(breakpointId, scriptId, condition, lineNumber,
1506 m_frontend.breakpointResolved(breakpointId, std::move(location));
1511 void V8DebuggerAgentImpl::didPause(
1513 const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
1514 v8::debug::ExceptionType exceptionType,
bool isUncaught,
bool isOOMBreak,
1518 std::vector<BreakReason> hitReasons;
1521 hitReasons.push_back(
1522 std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM,
nullptr));
1523 }
else if (isAssert) {
1524 hitReasons.push_back(std::make_pair(
1525 protocol::Debugger::Paused::ReasonEnum::Assert,
nullptr));
1526 }
else if (!exception.
IsEmpty()) {
1527 InjectedScript* injectedScript =
nullptr;
1528 m_session->findInjectedScript(contextId, injectedScript);
1529 if (injectedScript) {
1530 String16 breakReason =
1531 exceptionType == v8::debug::kPromiseRejection
1532 ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1533 : protocol::Debugger::Paused::ReasonEnum::Exception;
1534 std::unique_ptr<protocol::Runtime::RemoteObject> obj;
1535 injectedScript->wrapObject(exception, kBacktraceObjectGroup,
1536 WrapMode::kNoPreview, &obj);
1537 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1539 breakAuxData = obj->toValue();
1540 breakAuxData->setBoolean(
"uncaught", isUncaught);
1542 breakAuxData =
nullptr;
1544 hitReasons.push_back(
1545 std::make_pair(breakReason, std::move(breakAuxData)));
1549 std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
1551 for (
const auto&
id : hitBreakpoints) {
1552 auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(
id);
1553 if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
1556 const String16& breakpointId = breakpointIterator->second;
1557 hitBreakpointIds->addItem(breakpointId);
1558 BreakpointType type;
1559 parseBreakpointId(breakpointId, &type);
1560 if (type != BreakpointType::kDebugCommand)
continue;
1561 hitReasons.push_back(std::make_pair(
1562 protocol::Debugger::Paused::ReasonEnum::DebugCommand,
nullptr));
1565 for (
size_t i = 0;
i < m_breakReason.size(); ++
i) {
1566 hitReasons.push_back(std::move(m_breakReason[
i]));
1568 clearBreakDetails();
1570 String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1571 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1572 if (hitReasons.size() == 1) {
1573 breakReason = hitReasons[0].first;
1574 breakAuxData = std::move(hitReasons[0].second);
1575 }
else if (hitReasons.size() > 1) {
1576 breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
1577 std::unique_ptr<protocol::ListValue> reasons =
1578 protocol::ListValue::create();
1579 for (
size_t i = 0;
i < hitReasons.size(); ++
i) {
1580 std::unique_ptr<protocol::DictionaryValue> reason =
1581 protocol::DictionaryValue::create();
1582 reason->setString(
"reason", hitReasons[
i].first);
1583 if (hitReasons[
i].second)
1584 reason->setObject(
"auxData", std::move(hitReasons[
i].second));
1585 reasons->pushValue(std::move(reason));
1587 breakAuxData = protocol::DictionaryValue::create();
1588 breakAuxData->setArray(
"reasons", std::move(reasons));
1591 std::unique_ptr<Array<CallFrame>> protocolCallFrames;
1592 Response response = currentCallFrames(&protocolCallFrames);
1593 if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
1595 m_frontend.paused(std::move(protocolCallFrames), breakReason,
1596 std::move(breakAuxData), std::move(hitBreakpointIds),
1597 currentAsyncStackTrace(), currentExternalStackTrace(),
1598 currentScheduledAsyncCall());
1601 void V8DebuggerAgentImpl::didContinue() {
1602 clearBreakDetails();
1603 m_frontend.resumed();
1606 void V8DebuggerAgentImpl::breakProgram(
1607 const String16& breakReason,
1608 std::unique_ptr<protocol::DictionaryValue> data) {
1609 if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram())
return;
1610 std::vector<BreakReason> currentScheduledReason;
1611 currentScheduledReason.swap(m_breakReason);
1612 pushBreakDetails(breakReason, std::move(data));
1614 int contextGroupId = m_session->contextGroupId();
1615 int sessionId = m_session->sessionId();
1616 V8InspectorImpl* inspector = m_inspector;
1617 m_debugger->breakProgram(contextGroupId);
1619 if (!inspector->sessionById(contextGroupId, sessionId))
return;
1620 if (!enabled())
return;
1623 m_breakReason.swap(currentScheduledReason);
1624 if (!m_breakReason.empty()) {
1625 m_debugger->setPauseOnNextCall(
true, m_session->contextGroupId());
1631 BreakpointSource source) {
1632 String16 breakpointId = generateBreakpointId(
1633 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1634 : BreakpointType::kMonitorCommand,
1636 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
1637 m_breakpointIdToDebuggerBreakpointIds.end()) {
1640 setBreakpointImpl(breakpointId,
function, condition);
1644 BreakpointSource source) {
1645 String16 breakpointId = generateBreakpointId(
1646 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1647 : BreakpointType::kMonitorCommand,
1649 removeBreakpointImpl(breakpointId);
1652 void V8DebuggerAgentImpl::reset() {
1653 if (!enabled())
return;
1654 m_blackboxedPositions.clear();
1655 resetBlackboxedStateCache();
1657 m_breakpointIdToDebuggerBreakpointIds.clear();
1660 void V8DebuggerAgentImpl::cleanupOldFailedToParseAnonymousScriptsIfNeeded() {
1661 if (m_failedToParseAnonymousScriptIds.size() <=
1662 kMaxScriptFailedToParseScripts)
1664 static_assert(kMaxScriptFailedToParseScripts > 100,
1665 "kMaxScriptFailedToParseScripts should be greater then 100");
1666 while (m_failedToParseAnonymousScriptIds.size() >
1667 kMaxScriptFailedToParseScripts - 100 + 1) {
1668 String16 scriptId = m_failedToParseAnonymousScriptIds.front();
1669 m_failedToParseAnonymousScriptIds.pop_front();
1670 m_scripts.erase(scriptId);
int GetScriptLineNumber() const
static V8_INLINE Local< T > Cast(Local< S > that)
V8_INLINE bool IsEmpty() const
int GetScriptColumnNumber() const