v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
v8-debugger-agent-impl.cc
Go to the documentation of this file.
1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <algorithm>
8#include <memory>
9
10#include "../../third_party/inspector_protocol/crdtp/json.h"
11#include "include/v8-context.h"
12#include "include/v8-function.h"
17#include "src/inspector/crc32.h"
20#include "src/inspector/protocol/Debugger.h"
21#include "src/inspector/protocol/Protocol.h"
33
34namespace v8_inspector {
35
36using protocol::Array;
37using protocol::Debugger::BreakpointId;
38using protocol::Debugger::CallFrame;
39using protocol::Debugger::Scope;
40using protocol::Runtime::ExceptionDetails;
41using protocol::Runtime::RemoteObject;
42using protocol::Runtime::ScriptId;
43
44namespace InstrumentationEnum =
45 protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum;
46
47namespace DebuggerAgentState {
48static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
49static const char asyncCallStackDepth[] = "asyncCallStackDepth";
50static const char blackboxPattern[] = "blackboxPattern";
51static const char skipAnonymousScripts[] = "skipAnonymousScripts";
52static const char debuggerEnabled[] = "debuggerEnabled";
53static const char breakpointsActiveWhenEnabled[] = "breakpointsActive";
54static const char skipAllPauses[] = "skipAllPauses";
55
56static const char breakpointsByRegex[] = "breakpointsByRegex";
57static const char breakpointsByUrl[] = "breakpointsByUrl";
58static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
59static const char breakpointHints[] = "breakpointHints";
60static const char breakpointHintText[] = "text";
61static const char breakpointHintPrefixHash[] = "prefixHash";
62static const char breakpointHintPrefixLength[] = "prefixLen";
63static const char instrumentationBreakpoints[] = "instrumentationBreakpoints";
64static const char maxScriptCacheSize[] = "maxScriptCacheSize";
65
66} // namespace DebuggerAgentState
67
68static const char kBacktraceObjectGroup[] = "backtrace";
69static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
70static const char kDebuggerNotPaused[] =
71 "Can only perform operation while paused.";
72
73static const size_t kBreakpointHintMaxLength = 128;
74static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;
75// Limit the number of breakpoints returned, as we otherwise may exceed
76// the maximum length of a message in mojo (see https://crbug.com/1105172).
77static const size_t kMaxNumBreakpoints = 1000;
78
79#if V8_ENABLE_WEBASSEMBLY
80// TODO(1099680): getScriptSource and getWasmBytecode return Wasm wire bytes
81// as protocol::Binary, which is encoded as JSON string in the communication
82// to the DevTools front-end and hence leads to either crashing the renderer
83// that is being debugged or the renderer that's running the front-end if we
84// allow arbitrarily big Wasm byte sequences here. Ideally we would find a
85// different way to transfer the wire bytes (middle- to long-term), but as a
86// short-term solution, we should at least not crash.
87static constexpr size_t kWasmBytecodeMaxLength =
88 (v8::String::kMaxLength / 4) * 3;
89static constexpr const char kWasmBytecodeExceedsTransferLimit[] =
90 "WebAssembly bytecode exceeds the transfer limit";
91#endif // V8_ENABLE_WEBASSEMBLY
92
93namespace {
94
95enum class BreakpointType {
96 kByUrl = 1,
97 kByUrlRegex,
98 kByScriptHash,
99 kByScriptId,
100 kDebugCommand,
101 kMonitorCommand,
102 kBreakpointAtEntry,
103 kInstrumentationBreakpoint
104};
105
106String16 generateBreakpointId(BreakpointType type,
107 const String16& scriptSelector, int lineNumber,
108 int columnNumber) {
109 String16Builder builder;
110 builder.appendNumber(static_cast<int>(type));
111 builder.append(':');
112 builder.appendNumber(lineNumber);
113 builder.append(':');
114 builder.appendNumber(columnNumber);
115 builder.append(':');
116 builder.append(scriptSelector);
117 return builder.toString();
118}
119
120String16 generateBreakpointId(BreakpointType type,
121 v8::Local<v8::Function> function) {
122 String16Builder builder;
123 builder.appendNumber(static_cast<int>(type));
124 builder.append(':');
125 builder.appendNumber(v8::debug::GetDebuggingId(function));
126 return builder.toString();
127}
128
129String16 generateInstrumentationBreakpointId(const String16& instrumentation) {
130 String16Builder builder;
131 builder.appendNumber(
132 static_cast<int>(BreakpointType::kInstrumentationBreakpoint));
133 builder.append(':');
134 builder.append(instrumentation);
135 return builder.toString();
136}
137
138bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
139 String16* scriptSelector = nullptr,
140 int* lineNumber = nullptr, int* columnNumber = nullptr) {
141 size_t typeLineSeparator = breakpointId.find(':');
142 if (typeLineSeparator == String16::kNotFound) return false;
143
144 int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
145 if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
146 rawType > static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
147 return false;
148 }
149 if (type) *type = static_cast<BreakpointType>(rawType);
150 if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
151 rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
152 rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry) ||
153 rawType == static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
154 // The script and source position are not encoded in this case.
155 return true;
156 }
157
158 size_t lineColumnSeparator = breakpointId.find(':', typeLineSeparator + 1);
159 if (lineColumnSeparator == String16::kNotFound) return false;
160 size_t columnSelectorSeparator =
161 breakpointId.find(':', lineColumnSeparator + 1);
162 if (columnSelectorSeparator == String16::kNotFound) return false;
163 if (scriptSelector) {
164 *scriptSelector = breakpointId.substring(columnSelectorSeparator + 1);
165 }
166 if (lineNumber) {
167 *lineNumber = breakpointId
168 .substring(typeLineSeparator + 1,
169 lineColumnSeparator - typeLineSeparator - 1)
170 .toInteger();
171 }
172 if (columnNumber) {
173 *columnNumber =
174 breakpointId
175 .substring(lineColumnSeparator + 1,
176 columnSelectorSeparator - lineColumnSeparator - 1)
177 .toInteger();
178 }
179 return true;
180}
181
182bool positionComparator(const std::pair<int, int>& a,
183 const std::pair<int, int>& b) {
184 if (a.first != b.first) return a.first < b.first;
185 return a.second < b.second;
186}
187
188std::unique_ptr<protocol::DictionaryValue> breakpointHint(
189 const V8DebuggerScript& script, int breakpointLineNumber,
190 int breakpointColumnNumber, int actualLineNumber, int actualColumnNumber) {
191 int actualOffset;
192 int breakpointOffset;
193 if (!script.offset(actualLineNumber, actualColumnNumber).To(&actualOffset) ||
194 !script.offset(breakpointLineNumber, breakpointColumnNumber)
195 .To(&breakpointOffset)) {
196 return {};
197 }
198
199 auto hintObject = protocol::DictionaryValue::create();
200 String16 rawHint = script.source(actualOffset, kBreakpointHintMaxLength);
201 std::pair<size_t, size_t> offsetAndLength =
203 String16 hint =
204 rawHint.substring(offsetAndLength.first, offsetAndLength.second);
205 for (size_t i = 0; i < hint.length(); ++i) {
206 if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') {
207 hint = hint.substring(0, i);
208 break;
209 }
210 }
211 hintObject->setString(DebuggerAgentState::breakpointHintText, hint);
212
213 // Also store the hash of the text between the requested breakpoint location
214 // and the actual breakpoint location. If we see the same prefix text next
215 // time, we will keep the breakpoint at the same location (so that
216 // breakpoints do not slide around on reloads without any edits).
217 if (breakpointOffset <= actualOffset) {
218 size_t length = actualOffset - breakpointOffset + offsetAndLength.first;
219 String16 prefix = script.source(breakpointOffset, length);
220 int crc32 = computeCrc32(prefix);
221 hintObject->setInteger(DebuggerAgentState::breakpointHintPrefixHash, crc32);
223 v8::base::checked_cast<int32_t>(length));
224 }
225 return hintObject;
226}
227
228void adjustBreakpointLocation(const V8DebuggerScript& script,
229 const protocol::DictionaryValue* hintObject,
230 int* lineNumber, int* columnNumber) {
231 if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
232 return;
233 if (*lineNumber == script.startLine() &&
234 *columnNumber < script.startColumn()) {
235 return;
236 }
237 if (*lineNumber == script.endLine() && script.endColumn() < *columnNumber) {
238 return;
239 }
240
241 int sourceOffset;
242 if (!script.offset(*lineNumber, *columnNumber).To(&sourceOffset)) return;
243
244 int prefixLength = 0;
246 &prefixLength);
247 String16 hint;
248 if (!hintObject->getString(DebuggerAgentState::breakpointHintText, &hint) ||
249 hint.isEmpty())
250 return;
251
252 intptr_t searchRegionOffset = std::max(
253 sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
254 size_t offset = sourceOffset - searchRegionOffset;
255 size_t searchRegionSize =
257 static_cast<intptr_t>(prefixLength + hint.length()));
258
259 String16 searchArea = script.source(searchRegionOffset, searchRegionSize);
260
261 // Let us see if the breakpoint hint text appears at the same location
262 // as before, with the same prefix text in between. If yes, then we just use
263 // that position.
264 int prefixHash;
265 if (hintObject->getInteger(DebuggerAgentState::breakpointHintPrefixHash,
266 &prefixHash) &&
267 offset + prefixLength + hint.length() <= searchArea.length() &&
268 searchArea.substring(offset + prefixLength, hint.length()) == hint &&
269 computeCrc32(searchArea.substring(offset, prefixLength)) == prefixHash) {
270 v8::debug::Location hintPosition = script.location(
271 static_cast<int>(searchRegionOffset + offset + prefixLength));
272 *lineNumber = hintPosition.GetLineNumber();
273 *columnNumber = hintPosition.GetColumnNumber();
274 return;
275 }
276
277 size_t nextMatch = searchArea.find(hint, offset);
278 size_t prevMatch = searchArea.reverseFind(hint, offset);
279 if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
280 return;
281 }
282 size_t bestMatch;
283 if (nextMatch == String16::kNotFound ||
285 bestMatch = prevMatch;
286 } else if (prevMatch == String16::kNotFound) {
287 bestMatch = nextMatch;
288 } else {
289 bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
290 }
291 bestMatch += searchRegionOffset;
292 v8::debug::Location hintPosition =
293 script.location(static_cast<int>(bestMatch));
294 if (hintPosition.IsEmpty()) return;
295 *lineNumber = hintPosition.GetLineNumber();
296 *columnNumber = hintPosition.GetColumnNumber();
297}
298
299String16 breakLocationType(v8::debug::BreakLocationType type) {
300 switch (type) {
302 return protocol::Debugger::BreakLocation::TypeEnum::Call;
304 return protocol::Debugger::BreakLocation::TypeEnum::Return;
306 return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
308 return String16();
309 }
310 return String16();
311}
312
314 switch (type) {
316 return Scope::TypeEnum::Global;
318 return Scope::TypeEnum::Local;
320 return Scope::TypeEnum::With;
322 return Scope::TypeEnum::Closure;
324 return Scope::TypeEnum::Catch;
326 return Scope::TypeEnum::Block;
328 return Scope::TypeEnum::Script;
330 return Scope::TypeEnum::Eval;
332 return Scope::TypeEnum::Module;
334 return Scope::TypeEnum::WasmExpressionStack;
335 }
336 UNREACHABLE();
337}
338
339Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
340 InjectedScript* injectedScript,
341 std::unique_ptr<Array<Scope>>* scopes) {
342 *scopes = std::make_unique<Array<Scope>>();
343 if (!injectedScript) return Response::Success();
344 if (iterator->Done()) return Response::Success();
345
346 String16 scriptId = String16::fromInteger(iterator->GetScriptId());
347
348 for (; !iterator->Done(); iterator->Advance()) {
349 std::unique_ptr<RemoteObject> object;
350 Response result =
351 injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
352 WrapOptions({WrapMode::kIdOnly}), &object);
353 if (!result.IsSuccess()) return result;
354
355 auto scope = Scope::create()
356 .setType(scopeType(iterator->GetType()))
357 .setObject(std::move(object))
358 .build();
359
361 isolate, iterator->GetFunctionDebugName());
362 if (!name.isEmpty()) scope->setName(name);
363
364 if (iterator->HasLocationInfo()) {
366 scope->setStartLocation(protocol::Debugger::Location::create()
367 .setScriptId(scriptId)
368 .setLineNumber(start.GetLineNumber())
369 .setColumnNumber(start.GetColumnNumber())
370 .build());
371
373 scope->setEndLocation(protocol::Debugger::Location::create()
374 .setScriptId(scriptId)
375 .setLineNumber(end.GetLineNumber())
376 .setColumnNumber(end.GetColumnNumber())
377 .build());
378 }
379 (*scopes)->emplace_back(std::move(scope));
380 }
381 return Response::Success();
382}
383
384protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
385 const String16& key) {
386 protocol::DictionaryValue* value = object->getObject(key);
387 if (value) return value;
388 std::unique_ptr<protocol::DictionaryValue> newDictionary =
389 protocol::DictionaryValue::create();
390 value = newDictionary.get();
391 object->setObject(key, std::move(newDictionary));
392 return value;
393}
394
395Response isValidPosition(protocol::Debugger::ScriptPosition* position) {
396 if (position->getLineNumber() < 0)
397 return Response::ServerError("Position missing 'line' or 'line' < 0.");
398 if (position->getColumnNumber() < 0)
399 return Response::ServerError("Position missing 'column' or 'column' < 0.");
400 return Response::Success();
401}
402
403Response isValidRangeOfPositions(
404 const std::vector<std::pair<int, int>>& positions) {
405 for (size_t i = 1; i < positions.size(); ++i) {
406 if (positions[i - 1].first < positions[i].first) continue;
407 if (positions[i - 1].first == positions[i].first &&
408 positions[i - 1].second < positions[i].second)
409 continue;
410 return Response::ServerError(
411 "Input positions array is not sorted or contains duplicate values.");
412 }
413 return Response::Success();
414}
415
416bool hitBreakReasonEncodedAsOther(v8::debug::BreakReasons breakReasons) {
417 // The listed break reasons are not explicitly encoded in CDP when
418 // reporting the break. They are summarized as 'other'.
419 v8::debug::BreakReasons otherBreakReasons(
423 return breakReasons.contains_any(otherBreakReasons);
424}
425} // namespace
426
428 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
429 protocol::DictionaryValue* state)
430 : m_inspector(session->inspector()),
431 m_debugger(m_inspector->debugger()),
432 m_session(session),
433 m_enableState(kDisabled),
434 m_state(state),
435 m_frontend(frontendChannel),
436 m_isolate(m_inspector->isolate()) {}
437
439
444
445 std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts =
447 for (auto& script : compiledScripts) {
448 didParseSource(std::move(script), true);
449 }
450
451 m_breakpointsActive = m_state->booleanProperty(
455 }
456 if (isPaused()) {
457 didPause(0, v8::Local<v8::Value>(), std::vector<v8::debug::BreakpointId>(),
460 }
461}
462
463Response V8DebuggerAgentImpl::enable(std::optional<double> maxScriptsCacheSize,
464 String16* outDebuggerId) {
466 return Response::ServerError("Debugger is stopping");
467 m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
468 maxScriptsCacheSize.value_or(std::numeric_limits<double>::max()));
470 static_cast<double>(m_maxScriptCacheSize));
471 *outDebuggerId =
473 if (enabled()) return Response::Success();
474
476 return Response::ServerError("Script execution is prohibited");
477
478 enableImpl();
479 return Response::Success();
480}
481
483 if (!enabled()) return Response::Success();
484
490
494
497 m_breakpointsActive = false;
498 }
499 m_blackboxedPositions.clear();
500 m_blackboxPattern.reset();
502 m_skipList.clear();
503 m_scripts.clear();
504 m_cachedScripts.clear();
508 for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
509 m_debugger->removeBreakpoint(it.first);
510 }
513 m_wasmDisassemblies.clear();
516 m_skipAllPauses = false;
517 m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
523 return Response::Success();
524}
525
528 if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
529 return;
531 return;
532
533 enableImpl();
534
535 double maxScriptCacheSize = 0;
537 &maxScriptCacheSize);
538 m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(maxScriptCacheSize);
539
540 int pauseState = v8::debug::NoBreakOnException;
541 m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
542 setPauseOnExceptionsImpl(pauseState);
543
545 m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);
546
547 int asyncCallStackDepth = 0;
549 &asyncCallStackDepth);
550 m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);
551
552 String16 blackboxPattern;
554 &blackboxPattern)) {
555 setBlackboxPattern(blackboxPattern);
556 }
558 m_state->booleanProperty(DebuggerAgentState::skipAnonymousScripts, false);
559}
560
563 if (!enabled()) return Response::Success();
564 if (m_breakpointsActive == active) return Response::Success();
565 m_breakpointsActive = active;
567 if (!active && !m_breakReason.empty()) {
570 }
571 return Response::Success();
572}
573
576 m_skipAllPauses = skip;
577 return Response::Success();
578}
579
580namespace {
581
582class Matcher {
583 public:
584 Matcher(V8InspectorImpl* inspector, BreakpointType type,
585 const String16& selector)
586 : type_(type), selector_(selector) {
587 if (type == BreakpointType::kByUrlRegex) {
588 regex_ = std::make_unique<V8Regex>(inspector, selector, true);
589 }
590 }
591
592 bool matches(const V8DebuggerScript& script) {
593 switch (type_) {
594 case BreakpointType::kByUrl:
595 return script.sourceURL() == selector_;
596 case BreakpointType::kByScriptHash:
597 return script.hash() == selector_;
598 case BreakpointType::kByUrlRegex: {
599 return regex_->match(script.sourceURL()) != -1;
600 }
601 case BreakpointType::kByScriptId: {
602 return script.scriptId() == selector_;
603 }
604 default:
605 return false;
606 }
607 }
608
609 private:
610 std::unique_ptr<V8Regex> regex_;
611 BreakpointType type_;
612 const String16& selector_;
613};
614
615} // namespace
616
618 int lineNumber, std::optional<String16> optionalURL,
619 std::optional<String16> optionalURLRegex,
620 std::optional<String16> optionalScriptHash,
621 std::optional<int> optionalColumnNumber,
622 std::optional<String16> optionalCondition, String16* outBreakpointId,
623 std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
624 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
625
626 *locations = std::make_unique<Array<protocol::Debugger::Location>>();
627
628 int specified = (optionalURL.has_value() ? 1 : 0) +
629 (optionalURLRegex.has_value() ? 1 : 0) +
630 (optionalScriptHash.has_value() ? 1 : 0);
631 if (specified != 1) {
632 return Response::ServerError(
633 "Either url or urlRegex or scriptHash must be specified.");
634 }
635 int columnNumber = 0;
636 if (optionalColumnNumber.has_value()) {
637 columnNumber = optionalColumnNumber.value();
638 if (columnNumber < 0)
639 return Response::ServerError("Incorrect column number");
640 }
641
642 BreakpointType type = BreakpointType::kByUrl;
643 String16 selector;
644 if (optionalURLRegex.has_value()) {
645 selector = optionalURLRegex.value();
646 type = BreakpointType::kByUrlRegex;
647 } else if (optionalURL.has_value()) {
648 selector = optionalURL.value();
649 type = BreakpointType::kByUrl;
650 } else if (optionalScriptHash.has_value()) {
651 selector = optionalScriptHash.value();
652 type = BreakpointType::kByScriptHash;
653 }
654
655 // Note: This constructor can call into JavaScript.
656 Matcher matcher(m_inspector, type, selector);
657
658 String16 condition = optionalCondition.value_or(String16());
659 String16 breakpointId =
660 generateBreakpointId(type, selector, lineNumber, columnNumber);
661 protocol::DictionaryValue* breakpoints;
662 switch (type) {
663 case BreakpointType::kByUrlRegex:
664 breakpoints =
666 break;
667 case BreakpointType::kByUrl:
668 breakpoints = getOrCreateObject(
670 selector);
671 break;
672 case BreakpointType::kByScriptHash:
673 breakpoints = getOrCreateObject(
674 getOrCreateObject(m_state,
676 selector);
677 break;
678 default:
679 UNREACHABLE();
680 }
681 if (breakpoints->get(breakpointId)) {
682 return Response::ServerError(
683 "Breakpoint at specified location already exists.");
684 }
685
686 std::unique_ptr<protocol::DictionaryValue> hint;
687 for (const auto& script : m_scripts) {
688 if (!matcher.matches(*script.second)) continue;
689 // Make sure the session was not disabled by some re-entrant call
690 // in the script matcher.
691 DCHECK(enabled());
692 int adjustedLineNumber = lineNumber;
693 int adjustedColumnNumber = columnNumber;
694 if (hint) {
695 adjustBreakpointLocation(*script.second, hint.get(), &adjustedLineNumber,
696 &adjustedColumnNumber);
697 }
698 std::unique_ptr<protocol::Debugger::Location> location =
699 setBreakpointImpl(breakpointId, script.first, condition,
700 adjustedLineNumber, adjustedColumnNumber);
701 if (location && type != BreakpointType::kByUrlRegex) {
702 hint = breakpointHint(*script.second, lineNumber, columnNumber,
703 location->getLineNumber(),
704 location->getColumnNumber(adjustedColumnNumber));
705 }
706 if (location) (*locations)->emplace_back(std::move(location));
707 }
708 breakpoints->setString(breakpointId, condition);
709 if (hint) {
710 protocol::DictionaryValue* breakpointHints =
712 breakpointHints->setObject(breakpointId, std::move(hint));
713 }
714 *outBreakpointId = breakpointId;
715 return Response::Success();
716}
717
719 std::unique_ptr<protocol::Debugger::Location> location,
720 std::optional<String16> optionalCondition, String16* outBreakpointId,
721 std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
722 String16 breakpointId = generateBreakpointId(
723 BreakpointType::kByScriptId, location->getScriptId(),
724 location->getLineNumber(), location->getColumnNumber(0));
725 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
726
727 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
729 return Response::ServerError(
730 "Breakpoint at specified location already exists.");
731 }
732 *actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
733 optionalCondition.value_or(String16()),
734 location->getLineNumber(),
735 location->getColumnNumber(0));
736 if (!*actualLocation)
737 return Response::ServerError("Could not resolve breakpoint");
738 *outBreakpointId = breakpointId;
739 return Response::Success();
740}
741
743 const String16& functionObjectId, std::optional<String16> optionalCondition,
744 String16* outBreakpointId) {
745 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
746
747 InjectedScript::ObjectScope scope(m_session, functionObjectId);
748 Response response = scope.initialize();
749 if (!response.IsSuccess()) return response;
750 if (!scope.object()->IsFunction()) {
751 return Response::ServerError("Could not find function with given id");
752 }
753 v8::Local<v8::Function> function =
755 String16 breakpointId =
756 generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
757 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
759 return Response::ServerError(
760 "Breakpoint at specified location already exists.");
761 }
763 toV8String(m_isolate, optionalCondition.value_or(String16()));
764 setBreakpointImpl(breakpointId, function, condition);
765 *outBreakpointId = breakpointId;
766 return Response::Success();
767}
768
770 const String16& instrumentation, String16* outBreakpointId) {
771 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
772 String16 breakpointId = generateInstrumentationBreakpointId(instrumentation);
773 protocol::DictionaryValue* breakpoints = getOrCreateObject(
775 if (breakpoints->get(breakpointId)) {
776 return Response::ServerError(
777 "Instrumentation breakpoint is already enabled.");
778 }
779 breakpoints->setBoolean(breakpointId, true);
780 *outBreakpointId = breakpointId;
781 return Response::Success();
782}
783
784Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
785 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
786 BreakpointType type;
787 String16 selector;
788 if (!parseBreakpointId(breakpointId, &type, &selector)) {
789 return Response::Success();
790 }
791 Matcher matcher(m_inspector, type, selector);
792 protocol::DictionaryValue* breakpoints = nullptr;
793 switch (type) {
794 case BreakpointType::kByUrl: {
795 protocol::DictionaryValue* breakpointsByUrl =
797 if (breakpointsByUrl) {
798 breakpoints = breakpointsByUrl->getObject(selector);
799 }
800 } break;
801 case BreakpointType::kByScriptHash: {
802 protocol::DictionaryValue* breakpointsByScriptHash =
804 if (breakpointsByScriptHash) {
805 breakpoints = breakpointsByScriptHash->getObject(selector);
806 }
807 } break;
808 case BreakpointType::kByUrlRegex:
809 breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
810 break;
811 case BreakpointType::kInstrumentationBreakpoint:
812 breakpoints =
814 break;
815 default:
816 break;
817 }
818 if (breakpoints) breakpoints->remove(breakpointId);
819 protocol::DictionaryValue* breakpointHints =
821 if (breakpointHints) breakpointHints->remove(breakpointId);
822
823 // Get a list of scripts to remove breakpoints.
824 // TODO(duongn): we can do better here if from breakpoint id we can tell it is
825 // not Wasm breakpoint.
826 std::vector<V8DebuggerScript*> scripts;
827 for (const auto& scriptIter : m_scripts) {
828 const bool scriptSelectorMatch = matcher.matches(*scriptIter.second);
829 // Make sure the session was not disabled by some re-entrant call
830 // in the script matcher.
831 DCHECK(enabled());
832 const bool isInstrumentation =
833 type == BreakpointType::kInstrumentationBreakpoint;
834 if (!scriptSelectorMatch && !isInstrumentation) continue;
835 V8DebuggerScript* script = scriptIter.second.get();
836 if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly) {
837 scripts.push_back(script);
838 }
839 }
840 removeBreakpointImpl(breakpointId, scripts);
841
842 return Response::Success();
843}
844
846 const String16& breakpointId,
847 const std::vector<V8DebuggerScript*>& scripts) {
848 DCHECK(enabled());
849 BreakpointIdToDebuggerBreakpointIdsMap::iterator
850 debuggerBreakpointIdsIterator =
851 m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
852 if (debuggerBreakpointIdsIterator ==
854 return;
855 }
856 for (const auto& id : debuggerBreakpointIdsIterator->second) {
857#if V8_ENABLE_WEBASSEMBLY
858 for (auto& script : scripts) {
859 script->removeWasmBreakpoint(id);
860 }
861#endif // V8_ENABLE_WEBASSEMBLY
864 }
865 m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
866}
867
869 std::unique_ptr<protocol::Debugger::Location> start,
870 std::unique_ptr<protocol::Debugger::Location> end,
871 std::optional<bool> restrictToFunction,
872 std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
873 locations) {
874 String16 scriptId = start->getScriptId();
875
876 if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
877 return Response::ServerError(
878 "start.lineNumber and start.columnNumber should be >= 0");
879
880 v8::debug::Location v8Start(start->getLineNumber(),
881 start->getColumnNumber(0));
883 if (end) {
884 if (end->getScriptId() != scriptId)
885 return Response::ServerError(
886 "Locations should contain the same scriptId");
887 int line = end->getLineNumber();
888 int column = end->getColumnNumber(0);
889 if (line < 0 || column < 0)
890 return Response::ServerError(
891 "end.lineNumber and end.columnNumber should be >= 0");
892 v8End = v8::debug::Location(line, column);
893 }
894 auto it = m_scripts.find(scriptId);
895 if (it == m_scripts.end()) return Response::ServerError("Script not found");
896 std::vector<v8::debug::BreakLocation> v8Locations;
897 {
898 v8::HandleScope handleScope(m_isolate);
899 int contextId = it->second->executionContextId();
900 InspectedContext* inspected = m_inspector->getContext(contextId);
901 if (!inspected) {
902 return Response::ServerError("Cannot retrive script context");
903 }
904 v8::Context::Scope contextScope(inspected->context());
905 v8::MicrotasksScope microtasks(inspected->context(),
907 v8::TryCatch tryCatch(m_isolate);
908 it->second->getPossibleBreakpoints(
909 v8Start, v8End, restrictToFunction.value_or(false), &v8Locations);
910 }
911
912 *locations =
913 std::make_unique<protocol::Array<protocol::Debugger::BreakLocation>>();
914
915 // TODO(1106269): Return an error instead of capping the number of
916 // breakpoints.
917 const size_t numBreakpointsToSend =
918 std::min(v8Locations.size(), kMaxNumBreakpoints);
919 for (size_t i = 0; i < numBreakpointsToSend; ++i) {
920 std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
921 protocol::Debugger::BreakLocation::create()
922 .setScriptId(scriptId)
923 .setLineNumber(v8Locations[i].GetLineNumber())
924 .setColumnNumber(v8Locations[i].GetColumnNumber())
925 .build();
926 if (v8Locations[i].type() != v8::debug::kCommonBreakLocation) {
927 breakLocation->setType(breakLocationType(v8Locations[i].type()));
928 }
929 (*locations)->emplace_back(std::move(breakLocation));
930 }
931 return Response::Success();
932}
933
935 std::unique_ptr<protocol::Debugger::Location> location,
936 std::optional<String16> targetCallFrames) {
937 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
938 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
939 ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
940 if (it == m_scripts.end()) {
941 return Response::ServerError("Cannot continue to specified location");
942 }
943 V8DebuggerScript* script = it->second.get();
944 int contextId = script->executionContextId();
945 InspectedContext* inspected = m_inspector->getContext(contextId);
946 if (!inspected)
947 return Response::ServerError("Cannot continue to specified location");
948 v8::HandleScope handleScope(m_isolate);
949 v8::Context::Scope contextScope(inspected->context());
951 m_session->contextGroupId(), script, std::move(location),
952 targetCallFrames.value_or(
953 protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
954}
955
957 std::unique_ptr<protocol::Runtime::StackTraceId> inStackTraceId,
958 std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
959 bool isOk = false;
960 int64_t id = inStackTraceId->getId().toInteger64(&isOk);
961 if (!isOk) return Response::ServerError("Invalid stack trace id");
962
963 internal::V8DebuggerId debuggerId;
964 if (inStackTraceId->hasDebuggerId()) {
965 debuggerId =
966 internal::V8DebuggerId(inStackTraceId->getDebuggerId(String16()));
967 } else {
969 }
970 if (!debuggerId.isValid())
971 return Response::ServerError("Invalid stack trace id");
972
973 V8StackTraceId v8StackTraceId(id, debuggerId.pair());
974 if (v8StackTraceId.IsInvalid())
975 return Response::ServerError("Invalid stack trace id");
976 auto stack =
978 if (!stack) {
979 return Response::ServerError("Stack trace with given id is not found");
980 }
981 *outStackTrace = stack->buildInspectorObject(
983 return Response::Success();
984}
985
988 const v8::debug::Location& end) {
989 ScriptsMap::iterator it = m_scripts.find(scriptId);
990 if (it == m_scripts.end()) {
991 // Unknown scripts are blackboxed.
992 return true;
993 }
994 const String16& scriptSourceURL = it->second->sourceURL();
995 if (m_blackboxPattern && !scriptSourceURL.isEmpty()
996 && m_blackboxPattern->match(scriptSourceURL) != -1) {
997 return true;
998 }
999 if (m_skipAnonymousScripts && scriptSourceURL.isEmpty()) {
1000 return true;
1001 }
1002 if (!m_blackboxedExecutionContexts.empty()) {
1003 int contextId = it->second->executionContextId();
1004 InspectedContext* inspected = m_inspector->getContext(contextId);
1005 if (inspected && m_blackboxedExecutionContexts.count(
1006 inspected->uniqueId().toString()) > 0) {
1007 return true;
1008 }
1009 }
1010 auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
1011 if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
1012
1013 const std::vector<std::pair<int, int>>& ranges =
1014 itBlackboxedPositions->second;
1015 auto itStartRange = std::lower_bound(
1016 ranges.begin(), ranges.end(),
1017 std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
1018 positionComparator);
1019 auto itEndRange = std::lower_bound(
1020 itStartRange, ranges.end(),
1021 std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
1022 positionComparator);
1023 // Ranges array contains positions in script where blackbox state is changed.
1024 // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
1025 // blackboxed...
1026 return itStartRange == itEndRange &&
1027 std::distance(ranges.begin(), itStartRange) % 2;
1028}
1029
1030bool V8DebuggerAgentImpl::shouldBeSkipped(const String16& scriptId, int line,
1031 int column) {
1032 if (m_skipList.empty()) return false;
1033
1034 auto it = m_skipList.find(scriptId);
1035 if (it == m_skipList.end()) return false;
1036
1037 const std::vector<std::pair<int, int>>& ranges = it->second;
1038 DCHECK(!ranges.empty());
1039 const std::pair<int, int> location = std::make_pair(line, column);
1040 auto itLowerBound = std::lower_bound(ranges.begin(), ranges.end(), location,
1041 positionComparator);
1042
1043 bool shouldSkip = false;
1044 if (itLowerBound != ranges.end()) {
1045 // Skip lists are defined as pairs of locations that specify the
1046 // start and the end of ranges to skip: [ranges[0], ranges[1], ..], where
1047 // locations in [ranges[0], ranges[1]) should be skipped, i.e.
1048 // [(lineStart, columnStart), (lineEnd, columnEnd)).
1049 const bool isSameAsLowerBound = location == *itLowerBound;
1050 const bool isUnevenIndex = (itLowerBound - ranges.begin()) % 2;
1051 shouldSkip = isSameAsLowerBound ^ isUnevenIndex;
1052 }
1053
1054 return shouldSkip;
1055}
1056
1057bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
1058 return enabled() && (isOOMBreak || !m_skipAllPauses);
1059}
1060
1061std::unique_ptr<protocol::Debugger::Location>
1063 const String16& scriptId,
1064 const String16& condition,
1065 int lineNumber, int columnNumber) {
1066 v8::HandleScope handles(m_isolate);
1067 DCHECK(enabled());
1068
1069 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1070 if (scriptIterator == m_scripts.end()) return nullptr;
1071 V8DebuggerScript* script = scriptIterator->second.get();
1072
1073 v8::debug::BreakpointId debuggerBreakpointId;
1074 v8::debug::Location location(lineNumber, columnNumber);
1075 int contextId = script->executionContextId();
1076 InspectedContext* inspected = m_inspector->getContext(contextId);
1077 if (!inspected) return nullptr;
1078
1079 {
1080 v8::Context::Scope contextScope(inspected->context());
1081 if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
1082 return nullptr;
1083 }
1084 }
1085
1086 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
1087 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
1088 debuggerBreakpointId);
1089
1090 return protocol::Debugger::Location::create()
1091 .setScriptId(scriptId)
1092 .setLineNumber(location.GetLineNumber())
1093 .setColumnNumber(location.GetColumnNumber())
1094 .build();
1095}
1096
1098 v8::Local<v8::Function> function,
1100 v8::debug::BreakpointId debuggerBreakpointId;
1102 &debuggerBreakpointId)) {
1103 return;
1104 }
1105 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
1106 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
1107 debuggerBreakpointId);
1108}
1109
1111 const String16& scriptId, const String16& query,
1112 std::optional<bool> optionalCaseSensitive,
1113 std::optional<bool> optionalIsRegex,
1114 std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
1115 v8::HandleScope handles(m_isolate);
1116 ScriptsMap::iterator it = m_scripts.find(scriptId);
1117 if (it == m_scripts.end())
1118 return Response::ServerError("No script for id: " + scriptId.utf8());
1119
1120 *results = std::make_unique<protocol::Array<protocol::Debugger::SearchMatch>>(
1121 searchInTextByLinesImpl(m_session, it->second->source(0), query,
1122 optionalCaseSensitive.value_or(false),
1123 optionalIsRegex.value_or(false)));
1124 return Response::Success();
1125}
1126
1127namespace {
1128const char* buildStatus(v8::debug::LiveEditResult::Status status) {
1129 switch (status) {
1131 return protocol::Debugger::SetScriptSource::StatusEnum::Ok;
1133 return protocol::Debugger::SetScriptSource::StatusEnum::CompileError;
1135 return protocol::Debugger::SetScriptSource::StatusEnum::
1136 BlockedByActiveFunction;
1138 return protocol::Debugger::SetScriptSource::StatusEnum::
1139 BlockedByActiveGenerator;
1141 return protocol::Debugger::SetScriptSource::StatusEnum::
1142 BlockedByTopLevelEsModuleChange;
1143 }
1144}
1145} // namespace
1146
1148 const String16& scriptId, const String16& newContent,
1149 std::optional<bool> dryRun, std::optional<bool> allowTopFrameEditing,
1150 std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>*
1151 newCallFrames,
1152 std::optional<bool>* stackChanged,
1153 std::unique_ptr<protocol::Runtime::StackTrace>* asyncStackTrace,
1154 std::unique_ptr<protocol::Runtime::StackTraceId>* asyncStackTraceId,
1155 String16* status,
1156 std::unique_ptr<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
1157 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1158
1159 ScriptsMap::iterator it = m_scripts.find(scriptId);
1160 if (it == m_scripts.end()) {
1161 return Response::ServerError("No script with given id found");
1162 }
1163 int contextId = it->second->executionContextId();
1164 InspectedContext* inspected = m_inspector->getContext(contextId);
1165 if (!inspected) {
1166 return Response::InternalError();
1167 }
1168 v8::HandleScope handleScope(m_isolate);
1169 v8::Local<v8::Context> context = inspected->context();
1170 v8::Context::Scope contextScope(context);
1171 const bool allowTopFrameLiveEditing = allowTopFrameEditing.value_or(false);
1172
1174 it->second->setSource(newContent, dryRun.value_or(false),
1175 allowTopFrameLiveEditing, &result);
1176 *status = buildStatus(result.status);
1178 *optOutCompileError =
1179 protocol::Runtime::ExceptionDetails::create()
1180 .setExceptionId(m_inspector->nextExceptionId())
1181 .setText(toProtocolString(m_isolate, result.message))
1182 .setLineNumber(result.line_number != -1 ? result.line_number - 1
1183 : 0)
1184 .setColumnNumber(result.column_number != -1 ? result.column_number
1185 : 0)
1186 .build();
1187 return Response::Success();
1188 }
1189
1190 if (result.restart_top_frame_required) {
1191 CHECK(allowTopFrameLiveEditing);
1192 // Nothing could have happened to the JS stack since the live edit so
1193 // restarting the top frame is guaranteed to be successful.
1195 /* callFrameOrdinal */ 0));
1197 }
1198
1199 return Response::Success();
1200}
1201
1203 const String16& callFrameId, std::optional<String16> mode,
1204 std::unique_ptr<Array<CallFrame>>* newCallFrames,
1205 std::unique_ptr<protocol::Runtime::StackTrace>* asyncStackTrace,
1206 std::unique_ptr<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
1207 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1208 if (!mode.has_value()) {
1209 return Response::ServerError(
1210 "Restarting frame without 'mode' not supported");
1211 }
1212 if (mode.value() != protocol::Debugger::RestartFrame::ModeEnum::StepInto) {
1213 return Response::InvalidParams("'StepInto' is the only valid mode");
1214 }
1215
1216 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1217 Response response = scope.initialize();
1218 if (!response.IsSuccess()) return response;
1219 int callFrameOrdinal = static_cast<int>(scope.frameOrdinal());
1220
1222 callFrameOrdinal)) {
1223 return Response::ServerError("Restarting frame failed");
1224 }
1226 *newCallFrames = std::make_unique<Array<CallFrame>>();
1227 return Response::Success();
1228}
1229
1231 const String16& scriptId, String16* scriptSource,
1232 std::optional<protocol::Binary>* bytecode) {
1233 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1234 ScriptsMap::iterator it = m_scripts.find(scriptId);
1235 if (it == m_scripts.end()) {
1236 auto cachedScriptIt =
1237 std::find_if(m_cachedScripts.begin(), m_cachedScripts.end(),
1238 [&scriptId](const CachedScript& cachedScript) {
1239 return cachedScript.scriptId == scriptId;
1240 });
1241 if (cachedScriptIt != m_cachedScripts.end()) {
1242 *scriptSource = cachedScriptIt->source;
1244 cachedScriptIt->bytecode.begin(), cachedScriptIt->bytecode.size()));
1245 return Response::Success();
1246 }
1247 return Response::ServerError("No script for id: " + scriptId.utf8());
1248 }
1249 *scriptSource = it->second->source(0);
1250#if V8_ENABLE_WEBASSEMBLY
1252 if (it->second->wasmBytecode().To(&span)) {
1253 if (span.size() > kWasmBytecodeMaxLength) {
1254 return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
1255 }
1256 *bytecode = protocol::Binary::fromSpan(span);
1257 }
1258#endif // V8_ENABLE_WEBASSEMBLY
1259 return Response::Success();
1260}
1261
1263 DisassemblyChunk() = default;
1264 DisassemblyChunk(const DisassemblyChunk& other) = delete;
1268
1269 std::vector<String16> lines;
1270 std::vector<int> lineOffsets;
1271
1272 void Reserve(size_t size) {
1273 lines.reserve(size);
1274 lineOffsets.reserve(size);
1275 }
1276};
1277
1279 public:
1281
1282 void ReserveLineCount(size_t count) override {
1283 if (count == 0) return;
1284 size_t num_chunks = (count + kLinesPerChunk - 1) / kLinesPerChunk;
1285 chunks_.resize(num_chunks);
1286 for (size_t i = 0; i < num_chunks - 1; i++) {
1287 chunks_[i].Reserve(kLinesPerChunk);
1288 }
1289 size_t last = num_chunks - 1;
1290 size_t last_size = count % kLinesPerChunk;
1291 if (last_size == 0) last_size = kLinesPerChunk;
1292 chunks_[last].Reserve(last_size);
1293 }
1294
1295 void AddLine(const char* src, size_t length,
1296 uint32_t bytecode_offset) override {
1297 chunks_[writing_chunk_index_].lines.emplace_back(src, length);
1298 chunks_[writing_chunk_index_].lineOffsets.push_back(
1299 static_cast<int>(bytecode_offset));
1300 if (chunks_[writing_chunk_index_].lines.size() == kLinesPerChunk) {
1302 }
1304 }
1305
1307
1308 bool HasNextChunk() { return reading_chunk_index_ < chunks_.size(); }
1310 return std::move(chunks_[reading_chunk_index_++]);
1311 }
1312
1313 private:
1314 // For a large Ritz module, the average is about 50 chars per line,
1315 // so (with 2-byte String16 chars) this should give approximately 20 MB
1316 // per chunk.
1317 static constexpr size_t kLinesPerChunk = 200'000;
1318
1322 std::vector<DisassemblyChunk> chunks_;
1323};
1324
1326 const String16& in_scriptId, std::optional<String16>* out_streamId,
1327 int* out_totalNumberOfLines,
1328 std::unique_ptr<protocol::Array<int>>* out_functionBodyOffsets,
1329 std::unique_ptr<protocol::Debugger::WasmDisassemblyChunk>* out_chunk) {
1330#if V8_ENABLE_WEBASSEMBLY
1331 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1332 ScriptsMap::iterator it = m_scripts.find(in_scriptId);
1333 std::unique_ptr<DisassemblyCollectorImpl> collector =
1334 std::make_unique<DisassemblyCollectorImpl>();
1335 std::vector<int> functionBodyOffsets;
1336 if (it != m_scripts.end()) {
1337 V8DebuggerScript* script = it->second.get();
1338 if (script->getLanguage() != V8DebuggerScript::Language::WebAssembly) {
1339 return Response::InvalidParams("Script with id " + in_scriptId.utf8() +
1340 " is not WebAssembly");
1341 }
1342 script->Disassemble(collector.get(), &functionBodyOffsets);
1343 } else {
1344 auto cachedScriptIt =
1345 std::find_if(m_cachedScripts.begin(), m_cachedScripts.end(),
1346 [&in_scriptId](const CachedScript& cachedScript) {
1347 return cachedScript.scriptId == in_scriptId;
1348 });
1349 if (cachedScriptIt == m_cachedScripts.end()) {
1350 return Response::InvalidParams("No script for id: " + in_scriptId.utf8());
1351 }
1352 v8::debug::Disassemble(v8::base::VectorOf(cachedScriptIt->bytecode),
1353 collector.get(), &functionBodyOffsets);
1354 }
1355 *out_totalNumberOfLines =
1356 static_cast<int>(collector->total_number_of_lines());
1357 *out_functionBodyOffsets =
1358 std::make_unique<protocol::Array<int>>(std::move(functionBodyOffsets));
1359 // Even an empty module would disassemble to "(module)", never to zero lines.
1360 DCHECK(collector->HasNextChunk());
1361 DisassemblyChunk chunk(collector->NextChunk());
1362 *out_chunk = protocol::Debugger::WasmDisassemblyChunk::create()
1363 .setBytecodeOffsets(std::make_unique<protocol::Array<int>>(
1364 std::move(chunk.lineOffsets)))
1365 .setLines(std::make_unique<protocol::Array<String16>>(
1366 std::move(chunk.lines)))
1367 .build();
1368 if (collector->HasNextChunk()) {
1370 *out_streamId = streamId;
1371 m_wasmDisassemblies[streamId] = std::move(collector);
1372 }
1373 return Response::Success();
1374#else
1375 return Response::ServerError("WebAssembly is disabled");
1376#endif // V8_ENABLE_WEBASSEMBLY
1377}
1378
1380 const String16& in_streamId,
1381 std::unique_ptr<protocol::Debugger::WasmDisassemblyChunk>* out_chunk) {
1382#if V8_ENABLE_WEBASSEMBLY
1383 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1384 auto it = m_wasmDisassemblies.find(in_streamId);
1385 if (it == m_wasmDisassemblies.end()) {
1386 return Response::InvalidParams("No chunks available for stream " +
1387 in_streamId.utf8());
1388 }
1389 if (it->second->HasNextChunk()) {
1390 DisassemblyChunk chunk(it->second->NextChunk());
1391 *out_chunk = protocol::Debugger::WasmDisassemblyChunk::create()
1392 .setBytecodeOffsets(std::make_unique<protocol::Array<int>>(
1393 std::move(chunk.lineOffsets)))
1394 .setLines(std::make_unique<protocol::Array<String16>>(
1395 std::move(chunk.lines)))
1396 .build();
1397 } else {
1398 *out_chunk =
1399 protocol::Debugger::WasmDisassemblyChunk::create()
1400 .setBytecodeOffsets(std::make_unique<protocol::Array<int>>())
1401 .setLines(std::make_unique<protocol::Array<String16>>())
1402 .build();
1403 m_wasmDisassemblies.erase(it);
1404 }
1405 return Response::Success();
1406#else
1407 return Response::ServerError("WebAssembly is disabled");
1408#endif // V8_ENABLE_WEBASSEMBLY
1409}
1410
1412 protocol::Binary* bytecode) {
1413#if V8_ENABLE_WEBASSEMBLY
1414 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1415 ScriptsMap::iterator it = m_scripts.find(scriptId);
1416 if (it == m_scripts.end())
1417 return Response::ServerError("No script for id: " + scriptId.utf8());
1419 if (!it->second->wasmBytecode().To(&span))
1420 return Response::ServerError("Script with id " + scriptId.utf8() +
1421 " is not WebAssembly");
1422 if (span.size() > kWasmBytecodeMaxLength) {
1423 return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
1424 }
1425 *bytecode = protocol::Binary::fromSpan(span);
1426 return Response::Success();
1427#else
1428 return Response::ServerError("WebAssembly is disabled");
1429#endif // V8_ENABLE_WEBASSEMBLY
1430}
1431
1433 const String16& breakReason,
1434 std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
1435 m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
1436}
1437
1439 if (m_breakReason.empty()) return;
1440 m_breakReason.pop_back();
1441}
1442
1444 std::vector<BreakReason> emptyBreakReason;
1445 m_breakReason.swap(emptyBreakReason);
1446}
1447
1449 const String16& breakReason,
1450 std::unique_ptr<protocol::DictionaryValue> data) {
1451 if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1452 if (m_breakReason.empty()) {
1454 }
1455 pushBreakDetails(breakReason, std::move(data));
1456}
1457
1459 if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1460 if (m_breakReason.size() == 1) {
1462 }
1464}
1465
1467 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1468
1470 // If we are inside an instrumentation pause, remember the pause request
1471 // so that we can enter the requested pause once we are done
1472 // with the instrumentation.
1474 } else if (isPaused()) {
1475 // Ignore the pause request if we are already paused.
1476 return Response::Success();
1477 } else if (m_debugger->canBreakProgram()) {
1479 } else {
1480 pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
1482 }
1483
1484 return Response::Success();
1485}
1486
1487Response V8DebuggerAgentImpl::resume(std::optional<bool> terminateOnResume) {
1488 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1490
1493 terminateOnResume.value_or(false));
1494 return Response::Success();
1495}
1496
1498 std::unique_ptr<protocol::Array<protocol::Debugger::LocationRange>>
1499 inSkipList) {
1500 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1501
1502 if (inSkipList) {
1503 const Response res = processSkipList(*inSkipList);
1504 if (res.IsError()) return res;
1505 } else {
1506 m_skipList.clear();
1507 }
1508
1511 return Response::Success();
1512}
1513
1515 std::optional<bool> inBreakOnAsyncCall,
1516 std::unique_ptr<protocol::Array<protocol::Debugger::LocationRange>>
1517 inSkipList) {
1518 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1519
1520 if (inSkipList) {
1521 const Response res = processSkipList(*inSkipList);
1522 if (res.IsError()) return res;
1523 } else {
1524 m_skipList.clear();
1525 }
1526
1529 inBreakOnAsyncCall.value_or(false));
1530 return Response::Success();
1531}
1532
1534 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1537 return Response::Success();
1538}
1539
1541 std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1542 // Deprecated, just return OK.
1543 return Response::Success();
1544}
1545
1547 const String16& stringPauseState) {
1548 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1550 if (stringPauseState == "none") {
1551 pauseState = v8::debug::NoBreakOnException;
1552 } else if (stringPauseState == "all") {
1553 pauseState = v8::debug::BreakOnAnyException;
1554 } else if (stringPauseState == "caught") {
1556 } else if (stringPauseState == "uncaught") {
1558 } else {
1559 return Response::ServerError("Unknown pause on exceptions mode: " +
1560 stringPauseState.utf8());
1561 }
1562 setPauseOnExceptionsImpl(pauseState);
1563 return Response::Success();
1564}
1565
1567 // TODO(dgozman): this changes the global state and forces all context groups
1568 // to pause. We should make this flag be per-context-group.
1570 static_cast<v8::debug::ExceptionBreakState>(pauseState));
1571 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
1572}
1573
1575 const String16& callFrameId, const String16& expression,
1576 std::optional<String16> objectGroup,
1577 std::optional<bool> includeCommandLineAPI, std::optional<bool> silent,
1578 std::optional<bool> returnByValue, std::optional<bool> generatePreview,
1579 std::optional<bool> throwOnSideEffect, std::optional<double> timeout,
1580 std::unique_ptr<RemoteObject>* result,
1581 std::unique_ptr<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
1582 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1583 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1584 Response response = scope.initialize();
1585 if (!response.IsSuccess()) return response;
1586 if (includeCommandLineAPI.value_or(false)) scope.installCommandLineAPI();
1587 if (silent.value_or(false)) scope.ignoreExceptionsAndMuteConsole();
1588
1589 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1590 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1591 if (it->Done()) {
1592 return Response::ServerError("Could not find call frame with given id");
1593 }
1594
1595 v8::MaybeLocal<v8::Value> maybeResultValue;
1596 {
1597 V8InspectorImpl::EvaluateScope evaluateScope(scope);
1598 if (timeout.has_value()) {
1599 response = evaluateScope.setTimeout(timeout.value() / 1000.0);
1600 if (!response.IsSuccess()) return response;
1601 }
1602 maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
1603 throwOnSideEffect.value_or(false));
1604 }
1605 // Re-initialize after running client's code, as it could have destroyed
1606 // context or session.
1607 response = scope.initialize();
1608 if (!response.IsSuccess()) return response;
1609 WrapOptions wrapOptions = generatePreview.value_or(false)
1612 if (returnByValue.value_or(false))
1613 wrapOptions = WrapOptions({WrapMode::kJson});
1614 return scope.injectedScript()->wrapEvaluateResult(
1615 maybeResultValue, scope.tryCatch(), objectGroup.value_or(""), wrapOptions,
1616 throwOnSideEffect.value_or(false), result, exceptionDetails);
1617}
1618
1620 int scopeNumber, const String16& variableName,
1621 std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
1622 const String16& callFrameId) {
1623 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1624 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1625 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1626 Response response = scope.initialize();
1627 if (!response.IsSuccess()) return response;
1628 v8::Local<v8::Value> newValue;
1629 response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
1630 &newValue);
1631 if (!response.IsSuccess()) return response;
1632
1633 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1634 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1635 if (it->Done()) {
1636 return Response::ServerError("Could not find call frame with given id");
1637 }
1638 auto scopeIterator = it->GetScopeIterator();
1639 while (!scopeIterator->Done() && scopeNumber > 0) {
1640 --scopeNumber;
1641 scopeIterator->Advance();
1642 }
1643 if (scopeNumber != 0) {
1644 return Response::ServerError("Could not find scope with given number");
1645 }
1646
1647 if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
1648 newValue) ||
1649 scope.tryCatch().HasCaught()) {
1650 return Response::InternalError();
1651 }
1652 return Response::Success();
1653}
1654
1656 std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
1657 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1658 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1659 v8::HandleScope handleScope(m_isolate);
1661 if (iterator->Done()) {
1662 return Response::ServerError("Could not find top call frame");
1663 }
1664 if (iterator->GetReturnValue().IsEmpty()) {
1665 return Response::ServerError(
1666 "Could not update return value at non-return position");
1667 }
1668 InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
1669 Response response = scope.initialize();
1670 if (!response.IsSuccess()) return response;
1671 v8::Local<v8::Value> newValue;
1672 response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
1673 &newValue);
1674 if (!response.IsSuccess()) return response;
1676 return Response::Success();
1677}
1678
1680 if (!enabled() && !m_session->runtimeAgent()->enabled()) {
1681 return Response::ServerError(kDebuggerNotEnabled);
1682 }
1684 m_debugger->setAsyncCallStackDepth(this, depth);
1685 return Response::Success();
1686}
1687
1689 std::unique_ptr<protocol::Array<String16>> patterns,
1690 std::optional<bool> skipAnonymous) {
1691 m_skipAnonymousScripts = skipAnonymous.value_or(false);
1694 if (patterns->empty()) {
1695 m_blackboxPattern = nullptr;
1698 return Response::Success();
1699 }
1700
1701 String16Builder patternBuilder;
1702 patternBuilder.append('(');
1703 for (size_t i = 0; i < patterns->size() - 1; ++i) {
1704 patternBuilder.append((*patterns)[i]);
1705 patternBuilder.append("|");
1706 }
1707 patternBuilder.append(patterns->back());
1708 patternBuilder.append(')');
1709 String16 pattern = patternBuilder.toString();
1710 Response response = setBlackboxPattern(pattern);
1711 if (!response.IsSuccess()) return response;
1714 return Response::Success();
1715}
1716
1718 std::unique_ptr<protocol::Array<String16>> uniqueIds) {
1720 for (const String16& uniqueId : *uniqueIds) {
1721 m_blackboxedExecutionContexts.insert(uniqueId);
1722 }
1723 return Response::Success();
1724}
1725
1727 std::unique_ptr<V8Regex> regex(new V8Regex(
1728 m_inspector, pattern, true , false ));
1729 if (!regex->isValid())
1730 return Response::ServerError("Pattern parser error: " +
1731 regex->errorMessage().utf8());
1732 m_blackboxPattern = std::move(regex);
1733 return Response::Success();
1734}
1735
1737 for (const auto& it : m_scripts) {
1738 it.second->resetBlackboxedStateCache();
1739 }
1740}
1741
1743 const String16& scriptId,
1744 std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
1745 inPositions) {
1746 auto it = m_scripts.find(scriptId);
1747 if (it == m_scripts.end())
1748 return Response::ServerError("No script with passed id.");
1749
1750 if (inPositions->empty()) {
1751 m_blackboxedPositions.erase(scriptId);
1752 it->second->resetBlackboxedStateCache();
1753 return Response::Success();
1754 }
1755
1756 std::vector<std::pair<int, int>> positions;
1757 positions.reserve(inPositions->size());
1758 for (const std::unique_ptr<protocol::Debugger::ScriptPosition>& position :
1759 *inPositions) {
1760 Response res = isValidPosition(position.get());
1761 if (res.IsError()) return res;
1762
1763 positions.push_back(
1764 std::make_pair(position->getLineNumber(), position->getColumnNumber()));
1765 }
1766 Response res = isValidRangeOfPositions(positions);
1767 if (res.IsError()) return res;
1768
1769 m_blackboxedPositions[scriptId] = positions;
1770 it->second->resetBlackboxedStateCache();
1771 return Response::Success();
1772}
1773
1775 std::unique_ptr<Array<CallFrame>>* result) {
1776 if (!isPaused()) {
1777 *result = std::make_unique<Array<CallFrame>>();
1778 return Response::Success();
1779 }
1780 v8::HandleScope handles(m_isolate);
1781 *result = std::make_unique<Array<CallFrame>>();
1783 int frameOrdinal = 0;
1784 for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
1785 int contextId = iterator->GetContextId();
1786 InjectedScript* injectedScript = nullptr;
1787 if (contextId) m_session->findInjectedScript(contextId, injectedScript);
1789 m_inspector->isolateId(), contextId, frameOrdinal);
1790
1791 v8::debug::Location loc = iterator->GetSourceLocation();
1792
1793 std::unique_ptr<Array<Scope>> scopes;
1794 auto scopeIterator = iterator->GetScopeIterator();
1795 Response res =
1796 buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
1797 if (!res.IsSuccess()) return res;
1798
1799 std::unique_ptr<RemoteObject> protocolReceiver;
1800 if (injectedScript) {
1802 if (iterator->GetReceiver().ToLocal(&receiver)) {
1803 res = injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
1805 &protocolReceiver);
1806 if (!res.IsSuccess()) return res;
1807 }
1808 }
1809 if (!protocolReceiver) {
1810 protocolReceiver = RemoteObject::create()
1811 .setType(RemoteObject::TypeEnum::Undefined)
1812 .build();
1813 }
1814
1815 v8::Local<v8::debug::Script> script = iterator->GetScript();
1816 DCHECK(!script.IsEmpty());
1817 std::unique_ptr<protocol::Debugger::Location> location =
1818 protocol::Debugger::Location::create()
1819 .setScriptId(String16::fromInteger(script->Id()))
1820 .setLineNumber(loc.GetLineNumber())
1821 .setColumnNumber(loc.GetColumnNumber())
1822 .build();
1823
1824 auto frame = CallFrame::create()
1825 .setCallFrameId(callFrameId)
1826 .setFunctionName(toProtocolString(
1827 m_isolate, iterator->GetFunctionDebugName()))
1828 .setLocation(std::move(location))
1829 .setUrl(String16())
1830 .setScopeChain(std::move(scopes))
1831 .setThis(std::move(protocolReceiver))
1832 .setCanBeRestarted(iterator->CanBeRestarted())
1833 .build();
1834
1835 v8::debug::Location func_loc = iterator->GetFunctionLocation();
1836 if (!func_loc.IsEmpty()) {
1837 frame->setFunctionLocation(
1838 protocol::Debugger::Location::create()
1839 .setScriptId(String16::fromInteger(script->Id()))
1840 .setLineNumber(func_loc.GetLineNumber())
1841 .setColumnNumber(func_loc.GetColumnNumber())
1842 .build());
1843 }
1844
1845 v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
1846 if (!returnValue.IsEmpty() && injectedScript) {
1847 std::unique_ptr<RemoteObject> value;
1848 res =
1849 injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
1851 if (!res.IsSuccess()) return res;
1852 frame->setReturnValue(std::move(value));
1853 }
1854 (*result)->emplace_back(std::move(frame));
1855 }
1856 return Response::Success();
1857}
1858
1859std::unique_ptr<protocol::Runtime::StackTrace>
1861 std::shared_ptr<AsyncStackTrace> asyncParent =
1863 if (!asyncParent) return nullptr;
1864 return asyncParent->buildInspectorObject(
1866}
1867
1868std::unique_ptr<protocol::Runtime::StackTraceId>
1871 if (externalParent.IsInvalid()) return nullptr;
1872 return protocol::Runtime::StackTraceId::create()
1873 .setId(stackTraceIdToString(externalParent.id))
1874 .setDebuggerId(
1876 .build();
1877}
1878
1882
1884 switch (script.getLanguage()) {
1886 return protocol::Debugger::ScriptLanguageEnum::WebAssembly;
1888 return protocol::Debugger::ScriptLanguageEnum::JavaScript;
1889 }
1890}
1891
1892#if V8_ENABLE_WEBASSEMBLY
1893static const char* getDebugSymbolTypeName(
1894 v8::debug::WasmScript::DebugSymbols::Type type) {
1895 switch (type) {
1896 case v8::debug::WasmScript::DebugSymbols::Type::SourceMap:
1897 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1898 SourceMap;
1899 case v8::debug::WasmScript::DebugSymbols::Type::EmbeddedDWARF:
1900 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1901 EmbeddedDWARF;
1902 case v8::debug::WasmScript::DebugSymbols::Type::ExternalDWARF:
1903 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1904 ExternalDWARF;
1905 }
1906}
1907
1908static void getDebugSymbols(
1909 const V8DebuggerScript& script,
1910 std::unique_ptr<Array<protocol::Debugger::DebugSymbols>>* debug_symbols) {
1911 std::vector<v8::debug::WasmScript::DebugSymbols> v8_script_debug_symbols =
1912 script.getDebugSymbols();
1913
1914 *debug_symbols = std::make_unique<Array<protocol::Debugger::DebugSymbols>>();
1915 for (size_t i = 0; i < v8_script_debug_symbols.size(); ++i) {
1916 v8::debug::WasmScript::DebugSymbols& symbol = v8_script_debug_symbols[i];
1917 std::unique_ptr<protocol::Debugger::DebugSymbols> protocolDebugSymbol =
1918 v8_inspector::protocol::Debugger::DebugSymbols::create()
1919 .setType(getDebugSymbolTypeName(symbol.type))
1920 .build();
1921 if (symbol.external_url.size() > 0) {
1922 protocolDebugSymbol->setExternalURL(
1923 String16(symbol.external_url.data(), symbol.external_url.size()));
1924 }
1925 (*debug_symbols)->emplace_back(std::move(protocolDebugSymbol));
1926 }
1927}
1928#endif // V8_ENABLE_WEBASSEMBLY
1929
1930namespace {
1931
1932class DeferredMakeWeakScope {
1933 public:
1934 explicit DeferredMakeWeakScope(V8DebuggerScript& script) : script_(script) {}
1935
1936 ~DeferredMakeWeakScope() { script_.MakeWeak(); }
1937
1938 private:
1939 V8DebuggerScript& script_;
1940};
1941
1942} // namespace
1943
1945 std::unique_ptr<V8DebuggerScript> script, bool success) {
1946 v8::HandleScope handles(m_isolate);
1947 if (!success) {
1948 String16 scriptSource = script->source(0);
1949 script->setSourceURL(findSourceURL(scriptSource, false));
1950 script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
1951 }
1952
1953 int contextId = script->executionContextId();
1954 int contextGroupId = m_inspector->contextGroupId(contextId);
1955 InspectedContext* inspected =
1956 m_inspector->getContext(contextGroupId, contextId);
1957 std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1958 if (inspected) {
1959 // Script reused between different groups/sessions can have a stale
1960 // execution context id.
1961 const String16& aux = inspected->auxData();
1962 std::vector<uint8_t> cbor;
1963 v8_crdtp::json::ConvertJSONToCBOR(
1964 v8_crdtp::span<uint16_t>(aux.characters16(), aux.length()), &cbor);
1965 executionContextAuxData = protocol::DictionaryValue::cast(
1966 protocol::Value::parseBinary(cbor.data(), cbor.size()));
1967 }
1968 bool isLiveEdit = script->isLiveEdit();
1969 bool hasSourceURLComment = script->hasSourceURLComment();
1970 bool isModule = script->isModule();
1971 String16 scriptId = script->scriptId();
1972 String16 scriptURL = script->sourceURL();
1973 String16 embedderName = script->embedderName();
1974 String16 scriptLanguage = getScriptLanguage(*script);
1975 std::optional<int> codeOffset;
1976 std::unique_ptr<Array<protocol::Debugger::DebugSymbols>> debugSymbols;
1977#if V8_ENABLE_WEBASSEMBLY
1978 if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly) {
1979 codeOffset = script->codeOffset();
1980 getDebugSymbols(*script, &debugSymbols);
1981 }
1982#endif // V8_ENABLE_WEBASSEMBLY
1983
1984 m_scripts[scriptId] = std::move(script);
1985
1986 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1987 DCHECK(scriptIterator != m_scripts.end());
1988 V8DebuggerScript* scriptRef = scriptIterator->second.get();
1989
1990 // Release the strong reference once we exit 'didParseSource', to get notified
1991 // when debugger is the only one that holds the script. Has to be done after
1992 // script was added to m_scripts.
1993 DeferredMakeWeakScope weak_scope(*scriptRef);
1994
1995 // V8 could create functions for parsed scripts before reporting and asks
1996 // inspector about blackboxed state, we should reset state each time when we
1997 // make any change that change isFunctionBlackboxed output - adding parsed
1998 // script is changing.
1999 scriptRef->resetBlackboxedStateCache();
2000
2001 std::optional<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
2002 const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
2003 const bool* hasSourceURLParam =
2004 hasSourceURLComment ? &hasSourceURLComment : nullptr;
2005 const bool* isModuleParam = isModule ? &isModule : nullptr;
2006 std::unique_ptr<V8StackTraceImpl> stack =
2008 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
2009 stack && !stack->isEmpty()
2010 ? stack->buildInspectorObjectImpl(m_debugger, 0)
2011 : nullptr;
2012
2013 if (!success) {
2014 m_frontend.scriptFailedToParse(
2015 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
2016 scriptRef->endLine(), scriptRef->endColumn(), contextId,
2017 scriptRef->hash(), scriptRef->buildId(),
2018 std::move(executionContextAuxData), std::move(sourceMapURLParam),
2019 hasSourceURLParam, isModuleParam, scriptRef->length(),
2020 std::move(stackTrace), std::move(codeOffset), std::move(scriptLanguage),
2021 embedderName);
2022 return;
2023 }
2024
2025 std::vector<protocol::DictionaryValue*> potentialBreakpoints;
2026 if (!scriptURL.isEmpty()) {
2027 protocol::DictionaryValue* breakpointsByUrl =
2029 if (breakpointsByUrl) {
2030 potentialBreakpoints.push_back(breakpointsByUrl->getObject(scriptURL));
2031 }
2032 potentialBreakpoints.push_back(
2034 }
2035 protocol::DictionaryValue* breakpointsByScriptHash =
2037 if (breakpointsByScriptHash) {
2038 potentialBreakpoints.push_back(
2039 breakpointsByScriptHash->getObject(scriptRef->hash()));
2040 }
2041 protocol::DictionaryValue* breakpointHints =
2043 std::map<String16, std::unique_ptr<protocol::Debugger::Location>>
2044 resolvedBreakpoints;
2045 for (auto breakpoints : potentialBreakpoints) {
2046 if (!breakpoints) continue;
2047 for (size_t i = 0; i < breakpoints->size(); ++i) {
2048 auto breakpointWithCondition = breakpoints->at(i);
2049 String16 breakpointId = breakpointWithCondition.first;
2050
2051 BreakpointType type;
2052 String16 selector;
2053 int lineNumber = 0;
2054 int columnNumber = 0;
2055 parseBreakpointId(breakpointId, &type, &selector, &lineNumber,
2056 &columnNumber);
2057 Matcher matcher(m_inspector, type, selector);
2058
2059 if (!matcher.matches(*scriptRef)) continue;
2060 // Make sure the session was not disabled by some re-entrant call
2061 // in the script matcher.
2062 DCHECK(enabled());
2064 breakpointWithCondition.second->asString(&condition);
2065 protocol::DictionaryValue* hint =
2066 breakpointHints ? breakpointHints->getObject(breakpointId) : nullptr;
2067 if (hint) {
2068 adjustBreakpointLocation(*scriptRef, hint, &lineNumber, &columnNumber);
2069 }
2070 std::unique_ptr<protocol::Debugger::Location> location =
2071 setBreakpointImpl(breakpointId, scriptId, condition, lineNumber,
2072 columnNumber);
2073 if (location)
2074 resolvedBreakpoints.emplace(breakpointId, std::move(location));
2075 }
2076 }
2077
2078 auto resolvedBreakpointObjects =
2079 !resolvedBreakpoints.empty()
2080 ? std::make_unique<std::vector<
2081 std::unique_ptr<protocol::Debugger::ResolvedBreakpoint>>>()
2082 : nullptr;
2083 for (const auto& pair : resolvedBreakpoints) {
2084 resolvedBreakpointObjects->emplace_back(
2085 protocol::Debugger::ResolvedBreakpoint::create()
2086 .setBreakpointId(pair.first)
2087 .setLocation(pair.second->Clone())
2088 .build());
2089 }
2090
2091 m_frontend.scriptParsed(
2092 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
2093 scriptRef->endLine(), scriptRef->endColumn(), contextId,
2094 scriptRef->hash(), scriptRef->buildId(),
2095 std::move(executionContextAuxData), isLiveEditParam,
2096 std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
2097 scriptRef->length(), std::move(stackTrace), std::move(codeOffset),
2098 std::move(scriptLanguage), std::move(debugSymbols), embedderName,
2099 std::move(resolvedBreakpointObjects));
2100
2101 for (auto& pair : resolvedBreakpoints) {
2102 m_frontend.breakpointResolved(pair.first, std::move(pair.second));
2103 }
2104
2106}
2107
2109 V8DebuggerScript* scriptRef) {
2110 protocol::DictionaryValue* breakpoints =
2112 if (!breakpoints) return;
2113 bool isBlackboxed = isFunctionBlackboxed(
2114 scriptRef->scriptId(), v8::debug::Location(0, 0),
2115 v8::debug::Location(scriptRef->endLine(), scriptRef->endColumn()));
2116 if (isBlackboxed) return;
2117
2118 String16 sourceMapURL = scriptRef->sourceMappingURL();
2119 String16 breakpointId = generateInstrumentationBreakpointId(
2120 InstrumentationEnum::BeforeScriptExecution);
2121 if (!breakpoints->get(breakpointId)) {
2122 if (sourceMapURL.isEmpty()) return;
2123 breakpointId = generateInstrumentationBreakpointId(
2124 InstrumentationEnum::BeforeScriptWithSourceMapExecution);
2125 if (!breakpoints->get(breakpointId)) return;
2126 }
2127 v8::debug::BreakpointId debuggerBreakpointId;
2128 if (!scriptRef->setInstrumentationBreakpoint(&debuggerBreakpointId)) return;
2129
2130 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
2131 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
2132 debuggerBreakpointId);
2133}
2134
2136 v8::debug::BreakpointId instrumentationId) {
2137 String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
2138 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
2139
2140 std::unique_ptr<Array<CallFrame>> protocolCallFrames;
2141 Response response = currentCallFrames(&protocolCallFrames);
2142 if (!response.IsSuccess())
2143 protocolCallFrames = std::make_unique<Array<CallFrame>>();
2144
2145 if (m_debuggerBreakpointIdToBreakpointId.find(instrumentationId) !=
2147 DCHECK_GT(protocolCallFrames->size(), 0);
2148 if (!protocolCallFrames->empty()) {
2150 breakReason = protocol::Debugger::Paused::ReasonEnum::Instrumentation;
2151 const String16 scriptId =
2152 protocolCallFrames->at(0)->getLocation()->getScriptId();
2153 DCHECK_NE(m_scripts.find(scriptId), m_scripts.end());
2154 const auto& script = m_scripts[scriptId];
2155
2156 breakAuxData = protocol::DictionaryValue::create();
2157 breakAuxData->setString("scriptId", script->scriptId());
2158 breakAuxData->setString("url", script->sourceURL());
2159 if (!script->sourceMappingURL().isEmpty()) {
2160 breakAuxData->setString("sourceMapURL", (script->sourceMappingURL()));
2161 }
2162 }
2163 }
2164
2165 m_frontend.paused(std::move(protocolCallFrames), breakReason,
2166 std::move(breakAuxData),
2167 std::make_unique<Array<String16>>(),
2169}
2170
2172 int contextId, v8::Local<v8::Value> exception,
2173 const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
2174 v8::debug::ExceptionType exceptionType, bool isUncaught,
2175 v8::debug::BreakReasons breakReasons) {
2176 v8::HandleScope handles(m_isolate);
2177
2178 std::vector<BreakReason> hitReasons;
2179
2180 if (breakReasons.contains(v8::debug::BreakReason::kOOM)) {
2181 hitReasons.push_back(
2182 std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
2183 } else if (breakReasons.contains(v8::debug::BreakReason::kAssert)) {
2184 hitReasons.push_back(std::make_pair(
2185 protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
2186 } else if (breakReasons.contains(v8::debug::BreakReason::kException)) {
2187 InjectedScript* injectedScript = nullptr;
2188 m_session->findInjectedScript(contextId, injectedScript);
2189 if (injectedScript) {
2190 String16 breakReason =
2191 exceptionType == v8::debug::kPromiseRejection
2192 ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
2193 : protocol::Debugger::Paused::ReasonEnum::Exception;
2194 std::unique_ptr<protocol::Runtime::RemoteObject> obj;
2195 injectedScript->wrapObject(exception, kBacktraceObjectGroup,
2197 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
2198 if (obj) {
2199 std::vector<uint8_t> serialized;
2200 obj->AppendSerialized(&serialized);
2201 breakAuxData = protocol::DictionaryValue::cast(
2202 protocol::Value::parseBinary(serialized.data(), serialized.size()));
2203 breakAuxData->setBoolean("uncaught", isUncaught);
2204 }
2205 hitReasons.push_back(
2206 std::make_pair(breakReason, std::move(breakAuxData)));
2207 }
2208 }
2209
2210 if (breakReasons.contains(v8::debug::BreakReason::kStep) ||
2212 hitReasons.push_back(
2213 std::make_pair(protocol::Debugger::Paused::ReasonEnum::Step, nullptr));
2214 }
2215
2216 auto hitBreakpointIds = std::make_unique<Array<String16>>();
2217 bool hitRegularBreakpoint = false;
2218 for (const auto& id : hitBreakpoints) {
2219 auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
2220 if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
2221 continue;
2222 }
2223 const String16& breakpointId = breakpointIterator->second;
2224 hitBreakpointIds->emplace_back(breakpointId);
2225 BreakpointType type;
2226 parseBreakpointId(breakpointId, &type);
2227 if (type == BreakpointType::kDebugCommand) {
2228 hitReasons.push_back(std::make_pair(
2229 protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
2230 } else {
2231 hitRegularBreakpoint = true;
2232 }
2233 }
2234
2235 for (size_t i = 0; i < m_breakReason.size(); ++i) {
2236 hitReasons.push_back(std::move(m_breakReason[i]));
2237 }
2239
2240 // Make sure that we only include (other: nullptr) once.
2241 const BreakReason otherHitReason =
2242 std::make_pair(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
2243 const bool otherBreakReasons =
2244 hitRegularBreakpoint || hitBreakReasonEncodedAsOther(breakReasons);
2245 if (otherBreakReasons && std::find(hitReasons.begin(), hitReasons.end(),
2246 otherHitReason) == hitReasons.end()) {
2247 hitReasons.push_back(
2248 std::make_pair(protocol::Debugger::Paused::ReasonEnum::Other, nullptr));
2249 }
2250
2251 // We should always know why we pause: either the pause relates to this agent
2252 // (`hitReason` is non empty), or it relates to another agent (hit a
2253 // breakpoint there, or a triggered pause was scheduled by other agent).
2254 DCHECK(hitReasons.size() > 0 || !hitBreakpoints.empty() ||
2256 String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
2257 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
2258 if (hitReasons.size() == 1) {
2259 breakReason = hitReasons[0].first;
2260 breakAuxData = std::move(hitReasons[0].second);
2261 } else if (hitReasons.size() > 1) {
2262 breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
2263 std::unique_ptr<protocol::ListValue> reasons =
2264 protocol::ListValue::create();
2265 for (size_t i = 0; i < hitReasons.size(); ++i) {
2266 std::unique_ptr<protocol::DictionaryValue> reason =
2267 protocol::DictionaryValue::create();
2268 reason->setString("reason", hitReasons[i].first);
2269 if (hitReasons[i].second)
2270 reason->setObject("auxData", std::move(hitReasons[i].second));
2271 reasons->pushValue(std::move(reason));
2272 }
2273 breakAuxData = protocol::DictionaryValue::create();
2274 breakAuxData->setArray("reasons", std::move(reasons));
2275 }
2276
2277 std::unique_ptr<Array<CallFrame>> protocolCallFrames;
2278 Response response = currentCallFrames(&protocolCallFrames);
2279 if (!response.IsSuccess())
2280 protocolCallFrames = std::make_unique<Array<CallFrame>>();
2281
2283 m_frontend.paused(std::move(protocolCallFrames), breakReason,
2284 std::move(breakAuxData), std::move(hitBreakpointIds),
2286}
2287
2289 m_frontend.resumed();
2290 m_frontend.flush();
2291}
2292
2294 const String16& breakReason,
2295 std::unique_ptr<protocol::DictionaryValue> data) {
2296 if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return;
2297 std::vector<BreakReason> currentScheduledReason;
2298 currentScheduledReason.swap(m_breakReason);
2299 pushBreakDetails(breakReason, std::move(data));
2300
2301 int contextGroupId = m_session->contextGroupId();
2302 int sessionId = m_session->sessionId();
2303 V8InspectorImpl* inspector = m_inspector;
2304 m_debugger->breakProgram(contextGroupId);
2305 // Check that session and |this| are still around.
2306 if (!inspector->sessionById(contextGroupId, sessionId)) return;
2307 if (!enabled()) return;
2308
2310 m_breakReason.swap(currentScheduledReason);
2311 if (!m_breakReason.empty()) {
2313 }
2314}
2315
2318 BreakpointSource source) {
2319 String16 breakpointId = generateBreakpointId(
2320 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
2321 : BreakpointType::kMonitorCommand,
2322 function);
2323 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
2325 return;
2326 }
2327 setBreakpointImpl(breakpointId, function, condition);
2328}
2329
2331 BreakpointSource source) {
2332 String16 breakpointId = generateBreakpointId(
2333 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
2334 : BreakpointType::kMonitorCommand,
2335 function);
2336 std::vector<V8DebuggerScript*> scripts;
2337 removeBreakpointImpl(breakpointId, scripts);
2338}
2339
2341 if (!enabled()) return;
2342 m_blackboxedPositions.clear();
2344 m_skipList.clear();
2345 m_scripts.clear();
2346 m_cachedScripts.clear();
2350}
2351
2353 DCHECK_NE(m_scripts.find(script->scriptId()), m_scripts.end());
2354 std::vector<uint8_t> bytecode;
2355#if V8_ENABLE_WEBASSEMBLY
2357 if (script->wasmBytecode().To(&span)) {
2358 bytecode.reserve(span.size());
2359 bytecode.insert(bytecode.begin(), span.data(), span.data() + span.size());
2360 }
2361#endif
2362 {
2363 CachedScript cachedScript{script->scriptId(), script->source(0),
2364 std::move(bytecode)};
2365 m_cachedScriptSize += cachedScript.size();
2366 m_cachedScripts.push_back(std::move(cachedScript));
2367 m_scripts.erase(script->scriptId());
2368 }
2369
2371 const CachedScript& cachedScript = m_cachedScripts.front();
2372 DCHECK_GE(m_cachedScriptSize, cachedScript.size());
2373 m_cachedScriptSize -= cachedScript.size();
2374 m_cachedScripts.pop_front();
2375 }
2376}
2377
2379 protocol::Array<protocol::Debugger::LocationRange>& skipList) {
2380 std::unordered_map<String16, std::vector<std::pair<int, int>>> skipListInit;
2381 for (std::unique_ptr<protocol::Debugger::LocationRange>& range : skipList) {
2382 protocol::Debugger::ScriptPosition* start = range->getStart();
2383 protocol::Debugger::ScriptPosition* end = range->getEnd();
2384 String16 scriptId = range->getScriptId();
2385
2386 auto it = m_scripts.find(scriptId);
2387 if (it == m_scripts.end())
2388 return Response::ServerError("No script with passed id.");
2389
2390 Response res = isValidPosition(start);
2391 if (res.IsError()) return res;
2392
2393 res = isValidPosition(end);
2394 if (res.IsError()) return res;
2395
2396 skipListInit[scriptId].emplace_back(start->getLineNumber(),
2397 start->getColumnNumber());
2398 skipListInit[scriptId].emplace_back(end->getLineNumber(),
2399 end->getColumnNumber());
2400 }
2401
2402 // Verify that the skipList is sorted, and that all ranges
2403 // are properly defined (start comes before end).
2404 for (const auto& skipListPair : skipListInit) {
2405 Response res = isValidRangeOfPositions(skipListPair.second);
2406 if (res.IsError()) return res;
2407 }
2408
2409 m_skipList = std::move(skipListInit);
2410 return Response::Success();
2411}
2412
2417} // namespace v8_inspector
interpreter::Bytecode bytecode
Definition builtins.cc:43
static V8_INLINE Local< T > Cast(Local< S > that)
static constexpr int kMaxLength
bool IsFunction() const
Definition api.cc:3509
constexpr bool contains_any(EnumSet set) const
Definition enum-set.h:41
constexpr bool contains(E element) const
Definition enum-set.h:35
virtual int GetScriptId()=0
virtual v8::Local< v8::Object > GetObject()=0
virtual v8::Local< v8::Value > GetFunctionDebugName()=0
virtual bool Done()=0
virtual void Advance()=0
virtual debug::Location GetStartLocation()=0
virtual debug::Location GetEndLocation()=0
virtual ScopeType GetType()=0
virtual bool HasLocationInfo()=0
static std::unique_ptr< StackTraceIterator > Create(Isolate *isolate, int index=0)
void AddLine(const char *src, size_t length, uint32_t bytecode_offset) override
v8::Local< v8::Value > object() const
InjectedScript * injectedScript() const
const v8::TryCatch & tryCatch() const
Response wrapObject(v8::Local< v8::Value >, const String16 &groupName, const WrapOptions &wrapOptions, std::unique_ptr< protocol::Runtime::RemoteObject > *result)
Response resolveCallArgument(protocol::Runtime::CallArgument *, v8::Local< v8::Value > *result)
Response wrapEvaluateResult(v8::MaybeLocal< v8::Value > maybeResultValue, const v8::TryCatch &, const String16 &objectGroup, const WrapOptions &wrapOptions, bool throwOnSideEffect, std::unique_ptr< protocol::Runtime::RemoteObject > *result, std::unique_ptr< protocol::Runtime::ExceptionDetails > *)
v8::Local< v8::Context > context() const
internal::V8DebuggerId uniqueId() const
static String16 serialize(uint64_t isolateId, int injectedScriptId, int frameOrdinal)
void append(const String16 &)
Definition string-16.cc:156
String16 substring(size_t pos, size_t len=UINT_MAX) const
Definition string-16.h:61
size_t length() const
Definition string-16.h:58
static String16 fromInteger(int)
Definition string-16.cc:71
int toInteger(bool *ok=nullptr) const
Definition string-16.cc:118
bool isEmpty() const
Definition string-16.h:59
V8_EXPORT std::string utf8() const
Definition string-16.cc:254
size_t find(const String16 &str, size_t start=0) const
Definition string-16.h:64
size_t reverseFind(const String16 &str, size_t start=UINT_MAX) const
Definition string-16.h:67
const uint16_t * characters16() const
Definition string-16.h:55
std::pair< size_t, size_t > getTrimmedOffsetAndLength() const
Definition string-16.cc:127
static const size_t kNotFound
Definition string-16.h:26
Response setBlackboxPatterns(std::unique_ptr< protocol::Array< String16 > > patterns, std::optional< bool > skipAnonymous) override
Response setVariableValue(int scopeNumber, const String16 &variableName, std::unique_ptr< protocol::Runtime::CallArgument > newValue, const String16 &callFrame) override
Response setSkipAllPauses(bool skip) override
Response searchInContent(const String16 &scriptId, const String16 &query, std::optional< bool > optionalCaseSensitive, std::optional< bool > optionalIsRegex, std::unique_ptr< protocol::Array< protocol::Debugger::SearchMatch > > *) override
Response resume(std::optional< bool > terminateOnResume) override
void removeBreakpointFor(v8::Local< v8::Function > function, BreakpointSource source)
Response setBreakpointOnFunctionCall(const String16 &functionObjectId, std::optional< String16 > optionalCondition, String16 *outBreakpointId) override
std::unique_ptr< V8Regex > m_blackboxPattern
void schedulePauseOnNextStatement(const String16 &breakReason, std::unique_ptr< protocol::DictionaryValue > data)
std::map< String16, std::unique_ptr< DisassemblyCollectorImpl > > m_wasmDisassemblies
Response enable(std::optional< double > maxScriptsCacheSize, String16 *outDebuggerId) override
Response setPauseOnExceptions(const String16 &pauseState) override
Response setBreakpointByUrl(int lineNumber, std::optional< String16 > optionalURL, std::optional< String16 > optionalURLRegex, std::optional< String16 > optionalScriptHash, std::optional< int > optionalColumnNumber, std::optional< String16 > optionalCondition, String16 *, std::unique_ptr< protocol::Array< protocol::Debugger::Location > > *locations) override
Response removeBreakpoint(const String16 &breakpointId) override
Response getScriptSource(const String16 &scriptId, String16 *scriptSource, std::optional< protocol::Binary > *bytecode) override
void ScriptCollected(const V8DebuggerScript *script)
std::vector< BreakReason > m_breakReason
std::unique_ptr< protocol::Runtime::StackTraceId > currentExternalStackTrace()
std::unordered_map< String16, std::vector< std::pair< int, int > > > m_blackboxedPositions
void removeBreakpointImpl(const String16 &breakpointId, const std::vector< V8DebuggerScript * > &scripts)
void setScriptInstrumentationBreakpointIfNeeded(V8DebuggerScript *script)
std::deque< CachedScript > m_cachedScripts
Response setBreakpointsActive(bool active) override
Response continueToLocation(std::unique_ptr< protocol::Debugger::Location >, std::optional< String16 > targetCallFrames) override
Response setInstrumentationBreakpoint(const String16 &instrumentation, String16 *outBreakpointId) override
Response getStackTrace(std::unique_ptr< protocol::Runtime::StackTraceId > inStackTraceId, std::unique_ptr< protocol::Runtime::StackTrace > *outStackTrace) override
Response disassembleWasmModule(const String16 &in_scriptId, std::optional< String16 > *out_streamId, int *out_totalNumberOfLines, std::unique_ptr< protocol::Array< int > > *out_functionBodyOffsets, std::unique_ptr< protocol::Debugger::WasmDisassemblyChunk > *out_chunk) override
std::unique_ptr< protocol::Debugger::Location > setBreakpointImpl(const String16 &breakpointId, const String16 &scriptId, const String16 &condition, int lineNumber, int columnNumber)
Response setScriptSource(const String16 &inScriptId, const String16 &inScriptSource, std::optional< bool > dryRun, std::optional< bool > allowTopFrameEditing, std::unique_ptr< protocol::Array< protocol::Debugger::CallFrame > > *optOutCallFrames, std::optional< bool > *optOutStackChanged, std::unique_ptr< protocol::Runtime::StackTrace > *optOutAsyncStackTrace, std::unique_ptr< protocol::Runtime::StackTraceId > *optOutAsyncStackTraceId, String16 *outStatus, std::unique_ptr< protocol::Runtime::ExceptionDetails > *optOutCompileError) override
Response stepOver(std::unique_ptr< protocol::Array< protocol::Debugger::LocationRange > > inSkipList) override
BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds
Response pauseOnAsyncCall(std::unique_ptr< protocol::Runtime::StackTraceId > inParentStackTraceId) override
Response setBreakpoint(std::unique_ptr< protocol::Debugger::Location >, std::optional< String16 > optionalCondition, String16 *, std::unique_ptr< protocol::Debugger::Location > *actualLocation) override
Response setBlackboxExecutionContexts(std::unique_ptr< protocol::Array< String16 > > uniqueIds) override
bool isFunctionBlackboxed(const String16 &scriptId, const v8::debug::Location &start, const v8::debug::Location &end)
Response getPossibleBreakpoints(std::unique_ptr< protocol::Debugger::Location > start, std::unique_ptr< protocol::Debugger::Location > end, std::optional< bool > restrictToFunction, std::unique_ptr< protocol::Array< protocol::Debugger::BreakLocation > > *locations) override
void pushBreakDetails(const String16 &breakReason, std::unique_ptr< protocol::DictionaryValue > breakAuxData)
void didPauseOnInstrumentation(v8::debug::BreakpointId instrumentationId)
Response setReturnValue(std::unique_ptr< protocol::Runtime::CallArgument > newValue) override
Response setBlackboxedRanges(const String16 &scriptId, std::unique_ptr< protocol::Array< protocol::Debugger::ScriptPosition > > positions) override
void setBreakpointFor(v8::Local< v8::Function > function, v8::Local< v8::String > condition, BreakpointSource source)
Response stepInto(std::optional< bool > inBreakOnAsyncCall, std::unique_ptr< protocol::Array< protocol::Debugger::LocationRange > > inSkipList) override
Response restartFrame(const String16 &callFrameId, std::optional< String16 > mode, std::unique_ptr< protocol::Array< protocol::Debugger::CallFrame > > *newCallFrames, std::unique_ptr< protocol::Runtime::StackTrace > *asyncStackTrace, std::unique_ptr< protocol::Runtime::StackTraceId > *asyncStackTraceId) override
V8DebuggerAgentImpl(V8InspectorSessionImpl *, protocol::FrontendChannel *, protocol::DictionaryValue *state)
Response evaluateOnCallFrame(const String16 &callFrameId, const String16 &expression, std::optional< String16 > objectGroup, std::optional< bool > includeCommandLineAPI, std::optional< bool > silent, std::optional< bool > returnByValue, std::optional< bool > generatePreview, std::optional< bool > throwOnSideEffect, std::optional< double > timeout, std::unique_ptr< protocol::Runtime::RemoteObject > *result, std::unique_ptr< protocol::Runtime::ExceptionDetails > *) override
std::unordered_set< String16 > m_blackboxedExecutionContexts
Response processSkipList(protocol::Array< protocol::Debugger::LocationRange > &skipList)
void didPause(int contextId, v8::Local< v8::Value > exception, const std::vector< v8::debug::BreakpointId > &hitBreakpoints, v8::debug::ExceptionType exceptionType, bool isUncaught, v8::debug::BreakReasons breakReasons)
std::pair< String16, std::unique_ptr< protocol::DictionaryValue > > BreakReason
protocol::Debugger::Frontend m_frontend
Response getWasmBytecode(const String16 &scriptId, protocol::Binary *bytecode) override
void breakProgram(const String16 &breakReason, std::unique_ptr< protocol::DictionaryValue > data)
Response setBlackboxPattern(const String16 &pattern)
std::unordered_map< String16, std::vector< std::pair< int, int > > > m_skipList
std::unique_ptr< protocol::Runtime::StackTrace > currentAsyncStackTrace()
Response nextWasmDisassemblyChunk(const String16 &in_streamId, std::unique_ptr< protocol::Debugger::WasmDisassemblyChunk > *out_chunk) override
void didParseSource(std::unique_ptr< V8DebuggerScript >, bool success)
Response currentCallFrames(std::unique_ptr< protocol::Array< protocol::Debugger::CallFrame > > *)
Response setAsyncCallStackDepth(int depth) override
bool shouldBeSkipped(const String16 &scriptId, int line, int column)
DebuggerBreakpointIdToBreakpointIdMap m_debuggerBreakpointIdToBreakpointId
virtual const String16 & hash() const =0
virtual const String16 & sourceMappingURL() const =0
const String16 & scriptId() const
virtual int startColumn() const =0
virtual int endColumn() const =0
virtual int length() const =0
virtual int startLine() const =0
virtual String16 buildId() const =0
virtual int endLine() const =0
virtual bool setInstrumentationBreakpoint(int *id) const =0
virtual void resetBlackboxedStateCache()=0
std::shared_ptr< AsyncStackTrace > stackTraceFor(int contextGroupId, const V8StackTraceId &id)
bool isPausedInContextGroup(int contextGroupId) const
void removeBreakpoint(v8::debug::BreakpointId id)
void setPauseOnNextCall(bool, int targetContextGroupId)
internal::V8DebuggerId debuggerIdFor(int contextGroupId)
void stepOutOfFunction(int targetContextGroupId)
std::vector< std::unique_ptr< V8DebuggerScript > > getCompiledScripts(int contextGroupId, V8DebuggerAgentImpl *agent)
void interruptAndBreak(int targetContextGroupId)
void continueProgram(int targetContextGroupId, bool terminateOnResume=false)
bool isInInstrumentationPause() const
std::shared_ptr< AsyncStackTrace > currentAsyncParent()
void setAsyncCallStackDepth(V8DebuggerAgentImpl *, int)
void breakProgram(int targetContextGroupId)
V8StackTraceId currentExternalParent()
void stepIntoStatement(int targetContextGroupId, bool breakOnAsyncCall)
v8::Isolate * isolate() const
Definition v8-debugger.h:60
void stepOverStatement(int targetContextGroupId)
void setPauseOnExceptionsState(v8::debug::ExceptionBreakState)
bool restartFrame(int targetContextGroupId, int callFrameOrdinal)
Response continueToLocation(int targetContextGroupId, V8DebuggerScript *script, std::unique_ptr< protocol::Debugger::Location >, const String16 &targetCallFramess)
virtual bool canExecuteScripts(int contextGroupId)
protocol::Response setTimeout(double timeout)
InspectedContext * getContext(int groupId, int contextId) const
int contextGroupId(v8::Local< v8::Context >) const
V8InspectorSessionImpl * sessionById(int contextGroupId, int sessionId)
Response findInjectedScript(int contextId, InjectedScript *&)
void releaseObjectGroup(const String16 &objectGroup)
static std::unique_ptr< V8StackTraceImpl > capture(V8Debugger *, int maxStackSize)
std::pair< int64_t, int64_t > pair() const
static Binary fromSpan(v8_crdtp::span< uint8_t > span)
Definition string-util.h:55
const ObjectRef type_
int start
int end
Handle< Script > script_
InstructionSelectorT * selector_
int32_t offset
TNode< Object > receiver
std::string pattern
double second
ZoneVector< RpoNumber > & result
int position
Definition liveedit.cc:290
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
@ kDebuggerStatementBreakLocation
void SetReturnValue(v8::Isolate *v8_isolate, v8::Local< v8::Value > value)
bool SetFunctionBreakpoint(v8::Local< v8::Function > function, v8::Local< v8::String > condition, BreakpointId *id)
int GetDebuggingId(v8::Local< v8::Function > function)
void NotifyDebuggerPausedEventSent(v8::Isolate *v8_isolate)
static const intptr_t kBreakpointHintMaxSearchOffset
static String16 getScriptLanguage(const V8DebuggerScript &script)
static const size_t kMaxNumBreakpoints
static const char kDebuggerNotEnabled[]
int32_t computeCrc32(const String16 &text)
Definition crc32.cc:72
String16 toProtocolString(v8::Isolate *isolate, v8::Local< v8::String > value)
static const char kDebuggerNotPaused[]
String16 stackTraceIdToString(uintptr_t id)
v8::Local< v8::String > toV8String(v8::Isolate *isolate, const String16 &string)
static const size_t kBreakpointHintMaxLength
std::vector< std::unique_ptr< protocol::Debugger::SearchMatch > > searchInTextByLinesImpl(V8InspectorSession *session, const String16 &text, const String16 &query, const bool caseSensitive, const bool isRegex)
String16 findSourceURL(const String16 &content, bool multiline)
String16 toProtocolStringWithTypeCheck(v8::Isolate *isolate, v8::Local< v8::Value > value)
String16 findSourceMapURL(const String16 &content, bool multiline)
static const char kBacktraceObjectGroup[]
#define V8_NOEXCEPT
#define UNREACHABLE()
Definition logging.h:67
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_GT(v1, v2)
Definition logging.h:487
DisassemblyChunk(DisassemblyChunk &&other) V8_NOEXCEPT=default
DisassemblyChunk & operator=(const DisassemblyChunk &other)=delete
DisassemblyChunk & operator=(DisassemblyChunk &&other) V8_NOEXCEPT=default
DisassemblyChunk(const DisassemblyChunk &other)=delete
std::pair< int64_t, int64_t > debugger_id
v8::Isolate * m_isolate
V8InspectorImpl * m_inspector
std::unique_ptr< V8Regex > regex_
V8InspectorSessionImpl * m_session
protocol::HeapProfiler::Frontend * m_frontend
std::unique_ptr< ValueMirror > value
std::unique_ptr< ValueMirror > key
wasm::ValueType type