Bug Summary

File:out/../deps/v8/src/inspector/v8-debugger-agent-impl.cc
Warning:line 1836, column 14
The left operand of '==' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name v8-debugger-agent-impl.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/maurizio/node-v18.6.0/out -resource-dir /usr/local/lib/clang/16.0.0 -D _GLIBCXX_USE_CXX11_ABI=1 -D NODE_OPENSSL_CONF_NAME=nodejs_conf -D NODE_OPENSSL_HAS_QUIC -D V8_GYP_BUILD -D V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -D __STDC_FORMAT_MACROS -D OPENSSL_NO_PINSHARED -D OPENSSL_THREADS -D V8_TARGET_ARCH_X64 -D V8_HAVE_TARGET_OS -D V8_TARGET_OS_LINUX -D V8_EMBEDDER_STRING="-node.8" -D ENABLE_DISASSEMBLER -D V8_PROMISE_INTERNAL_FIELD_COUNT=1 -D V8_SHORT_BUILTIN_CALLS -D OBJECT_PRINT -D V8_INTL_SUPPORT -D V8_ATOMIC_OBJECT_FIELD_WRITES -D V8_ENABLE_LAZY_SOURCE_POSITIONS -D V8_USE_SIPHASH -D V8_SHARED_RO_HEAP -D V8_WIN64_UNWINDING_INFO -D V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -D V8_SNAPSHOT_COMPRESSION -D V8_ENABLE_WEBASSEMBLY -D V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS -D V8_ALLOCATION_FOLDING -D V8_ALLOCATION_SITE_TRACKING -D V8_SCRIPTORMODULE_LEGACY_LIFETIME -D V8_ADVANCED_BIGINT_ALGORITHMS -D ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC -D UCONFIG_NO_SERVICE=1 -D U_ENABLE_DYLOAD=0 -D U_STATIC_IMPLEMENTATION=1 -D U_HAVE_STD_STRING=1 -D UCONFIG_NO_BREAK_ITERATION=0 -I ../deps/v8 -I ../deps/v8/include -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/inspector-generated-output-root -I ../deps/v8/third_party/inspector_protocol -I /home/maurizio/node-v18.6.0/out/Release/obj/gen -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/generate-bytecode-output-root -I ../deps/icu-small/source/i18n -I ../deps/icu-small/source/common -I ../deps/v8/third_party/zlib -I ../deps/v8/third_party/zlib/google -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /usr/local/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wno-return-type -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/home/maurizio/node-v18.6.0/out -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-08-22-142216-507842-1 -x c++ ../deps/v8/src/inspector/v8-debugger-agent-impl.cc
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
5#include "src/inspector/v8-debugger-agent-impl.h"
6
7#include <algorithm>
8
9#include "../../third_party/inspector_protocol/crdtp/json.h"
10#include "include/v8-context.h"
11#include "include/v8-function.h"
12#include "include/v8-inspector.h"
13#include "include/v8-microtask-queue.h"
14#include "src/base/safe_conversions.h"
15#include "src/debug/debug-interface.h"
16#include "src/inspector/injected-script.h"
17#include "src/inspector/inspected-context.h"
18#include "src/inspector/protocol/Debugger.h"
19#include "src/inspector/protocol/Protocol.h"
20#include "src/inspector/remote-object-id.h"
21#include "src/inspector/search-util.h"
22#include "src/inspector/string-util.h"
23#include "src/inspector/v8-debugger-script.h"
24#include "src/inspector/v8-debugger.h"
25#include "src/inspector/v8-inspector-impl.h"
26#include "src/inspector/v8-inspector-session-impl.h"
27#include "src/inspector/v8-regex.h"
28#include "src/inspector/v8-runtime-agent-impl.h"
29#include "src/inspector/v8-stack-trace-impl.h"
30#include "src/inspector/v8-value-utils.h"
31
32namespace v8_inspector {
33
34using protocol::Array;
35using protocol::Maybe;
36using protocol::Debugger::BreakpointId;
37using protocol::Debugger::CallFrame;
38using protocol::Debugger::Scope;
39using protocol::Runtime::ExceptionDetails;
40using protocol::Runtime::RemoteObject;
41using protocol::Runtime::ScriptId;
42
43namespace InstrumentationEnum =
44 protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum;
45
46namespace DebuggerAgentState {
47static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
48static const char asyncCallStackDepth[] = "asyncCallStackDepth";
49static const char blackboxPattern[] = "blackboxPattern";
50static const char debuggerEnabled[] = "debuggerEnabled";
51static const char skipAllPauses[] = "skipAllPauses";
52
53static const char breakpointsByRegex[] = "breakpointsByRegex";
54static const char breakpointsByUrl[] = "breakpointsByUrl";
55static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
56static const char breakpointHints[] = "breakpointHints";
57static const char instrumentationBreakpoints[] = "instrumentationBreakpoints";
58
59} // namespace DebuggerAgentState
60
61static const char kBacktraceObjectGroup[] = "backtrace";
62static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
63static const char kDebuggerNotPaused[] =
64 "Can only perform operation while paused.";
65
66static const size_t kBreakpointHintMaxLength = 128;
67static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;
68// Limit the number of breakpoints returned, as we otherwise may exceed
69// the maximum length of a message in mojo (see https://crbug.com/1105172).
70static const size_t kMaxNumBreakpoints = 1000;
71
72#if V8_ENABLE_WEBASSEMBLY1
73// TODO(1099680): getScriptSource and getWasmBytecode return Wasm wire bytes
74// as protocol::Binary, which is encoded as JSON string in the communication
75// to the DevTools front-end and hence leads to either crashing the renderer
76// that is being debugged or the renderer that's running the front-end if we
77// allow arbitrarily big Wasm byte sequences here. Ideally we would find a
78// different way to transfer the wire bytes (middle- to long-term), but as a
79// short-term solution, we should at least not crash.
80static constexpr size_t kWasmBytecodeMaxLength =
81 (v8::String::kMaxLength / 4) * 3;
82static constexpr const char kWasmBytecodeExceedsTransferLimit[] =
83 "WebAssembly bytecode exceeds the transfer limit";
84#endif // V8_ENABLE_WEBASSEMBLY
85
86namespace {
87
88enum class BreakpointType {
89 kByUrl = 1,
90 kByUrlRegex,
91 kByScriptHash,
92 kByScriptId,
93 kDebugCommand,
94 kMonitorCommand,
95 kBreakpointAtEntry,
96 kInstrumentationBreakpoint
97};
98
99String16 generateBreakpointId(BreakpointType type,
100 const String16& scriptSelector, int lineNumber,
101 int columnNumber) {
102 String16Builder builder;
103 builder.appendNumber(static_cast<int>(type));
104 builder.append(':');
105 builder.appendNumber(lineNumber);
106 builder.append(':');
107 builder.appendNumber(columnNumber);
108 builder.append(':');
109 builder.append(scriptSelector);
110 return builder.toString();
111}
112
113String16 generateBreakpointId(BreakpointType type,
114 v8::Local<v8::Function> function) {
115 String16Builder builder;
116 builder.appendNumber(static_cast<int>(type));
117 builder.append(':');
118 builder.appendNumber(v8::debug::GetDebuggingId(function));
119 return builder.toString();
120}
121
122String16 generateInstrumentationBreakpointId(const String16& instrumentation) {
123 String16Builder builder;
124 builder.appendNumber(
125 static_cast<int>(BreakpointType::kInstrumentationBreakpoint));
126 builder.append(':');
127 builder.append(instrumentation);
128 return builder.toString();
129}
130
131bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
132 String16* scriptSelector = nullptr,
133 int* lineNumber = nullptr, int* columnNumber = nullptr) {
134 size_t typeLineSeparator = breakpointId.find(':');
135 if (typeLineSeparator == String16::kNotFound) return false;
15
Assuming 'typeLineSeparator' is equal to 'kNotFound'
16
Taking true branch
17
Returning without writing to '*type'
136
137 int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
138 if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
139 rawType > static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
140 return false;
141 }
142 if (type) *type = static_cast<BreakpointType>(rawType);
143 if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
144 rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
145 rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry) ||
146 rawType == static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
147 // The script and source position are not encoded in this case.
148 return true;
149 }
150
151 size_t lineColumnSeparator = breakpointId.find(':', typeLineSeparator + 1);
152 if (lineColumnSeparator == String16::kNotFound) return false;
153 size_t columnSelectorSeparator =
154 breakpointId.find(':', lineColumnSeparator + 1);
155 if (columnSelectorSeparator == String16::kNotFound) return false;
156 if (scriptSelector) {
157 *scriptSelector = breakpointId.substring(columnSelectorSeparator + 1);
158 }
159 if (lineNumber) {
160 *lineNumber = breakpointId
161 .substring(typeLineSeparator + 1,
162 lineColumnSeparator - typeLineSeparator - 1)
163 .toInteger();
164 }
165 if (columnNumber) {
166 *columnNumber =
167 breakpointId
168 .substring(lineColumnSeparator + 1,
169 columnSelectorSeparator - lineColumnSeparator - 1)
170 .toInteger();
171 }
172 return true;
173}
174
175bool positionComparator(const std::pair<int, int>& a,
176 const std::pair<int, int>& b) {
177 if (a.first != b.first) return a.first < b.first;
178 return a.second < b.second;
179}
180
181String16 breakpointHint(const V8DebuggerScript& script, int lineNumber,
182 int columnNumber) {
183 int offset = script.offset(lineNumber, columnNumber);
184 if (offset == V8DebuggerScript::kNoOffset) return String16();
185 String16 hint =
186 script.source(offset, kBreakpointHintMaxLength).stripWhiteSpace();
187 for (size_t i = 0; i < hint.length(); ++i) {
188 if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') {
189 return hint.substring(0, i);
190 }
191 }
192 return hint;
193}
194
195void adjustBreakpointLocation(const V8DebuggerScript& script,
196 const String16& hint, int* lineNumber,
197 int* columnNumber) {
198 if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
199 return;
200 if (*lineNumber == script.startLine() &&
201 *columnNumber < script.startColumn()) {
202 return;
203 }
204 if (*lineNumber == script.endLine() && script.endColumn() < *columnNumber) {
205 return;
206 }
207
208 if (hint.isEmpty()) return;
209 intptr_t sourceOffset = script.offset(*lineNumber, *columnNumber);
210 if (sourceOffset == V8DebuggerScript::kNoOffset) return;
211
212 intptr_t searchRegionOffset = std::max(
213 sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
214 size_t offset = sourceOffset - searchRegionOffset;
215 String16 searchArea = script.source(searchRegionOffset,
216 offset + kBreakpointHintMaxSearchOffset);
217
218 size_t nextMatch = searchArea.find(hint, offset);
219 size_t prevMatch = searchArea.reverseFind(hint, offset);
220 if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
221 return;
222 }
223 size_t bestMatch;
224 if (nextMatch == String16::kNotFound) {
225 bestMatch = prevMatch;
226 } else if (prevMatch == String16::kNotFound) {
227 bestMatch = nextMatch;
228 } else {
229 bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
230 }
231 bestMatch += searchRegionOffset;
232 v8::debug::Location hintPosition =
233 script.location(static_cast<int>(bestMatch));
234 if (hintPosition.IsEmpty()) return;
235 *lineNumber = hintPosition.GetLineNumber();
236 *columnNumber = hintPosition.GetColumnNumber();
237}
238
239String16 breakLocationType(v8::debug::BreakLocationType type) {
240 switch (type) {
241 case v8::debug::kCallBreakLocation:
242 return protocol::Debugger::BreakLocation::TypeEnum::Call;
243 case v8::debug::kReturnBreakLocation:
244 return protocol::Debugger::BreakLocation::TypeEnum::Return;
245 case v8::debug::kDebuggerStatementBreakLocation:
246 return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
247 case v8::debug::kCommonBreakLocation:
248 return String16();
249 }
250 return String16();
251}
252
253String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
254 switch (type) {
255 case v8::debug::ScopeIterator::ScopeTypeGlobal:
256 return Scope::TypeEnum::Global;
257 case v8::debug::ScopeIterator::ScopeTypeLocal:
258 return Scope::TypeEnum::Local;
259 case v8::debug::ScopeIterator::ScopeTypeWith:
260 return Scope::TypeEnum::With;
261 case v8::debug::ScopeIterator::ScopeTypeClosure:
262 return Scope::TypeEnum::Closure;
263 case v8::debug::ScopeIterator::ScopeTypeCatch:
264 return Scope::TypeEnum::Catch;
265 case v8::debug::ScopeIterator::ScopeTypeBlock:
266 return Scope::TypeEnum::Block;
267 case v8::debug::ScopeIterator::ScopeTypeScript:
268 return Scope::TypeEnum::Script;
269 case v8::debug::ScopeIterator::ScopeTypeEval:
270 return Scope::TypeEnum::Eval;
271 case v8::debug::ScopeIterator::ScopeTypeModule:
272 return Scope::TypeEnum::Module;
273 case v8::debug::ScopeIterator::ScopeTypeWasmExpressionStack:
274 return Scope::TypeEnum::WasmExpressionStack;
275 }
276 UNREACHABLE()V8_Fatal("unreachable code");
277}
278
279Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
280 InjectedScript* injectedScript,
281 std::unique_ptr<Array<Scope>>* scopes) {
282 *scopes = std::make_unique<Array<Scope>>();
283 if (!injectedScript) return Response::Success();
284 if (iterator->Done()) return Response::Success();
285
286 String16 scriptId = String16::fromInteger(iterator->GetScriptId());
287
288 for (; !iterator->Done(); iterator->Advance()) {
289 std::unique_ptr<RemoteObject> object;
290 Response result =
291 injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
292 WrapMode::kNoPreview, &object);
293 if (!result.IsSuccess()) return result;
294
295 auto scope = Scope::create()
296 .setType(scopeType(iterator->GetType()))
297 .setObject(std::move(object))
298 .build();
299
300 String16 name = toProtocolStringWithTypeCheck(
301 isolate, iterator->GetFunctionDebugName());
302 if (!name.isEmpty()) scope->setName(name);
303
304 if (iterator->HasLocationInfo()) {
305 v8::debug::Location start = iterator->GetStartLocation();
306 scope->setStartLocation(protocol::Debugger::Location::create()
307 .setScriptId(scriptId)
308 .setLineNumber(start.GetLineNumber())
309 .setColumnNumber(start.GetColumnNumber())
310 .build());
311
312 v8::debug::Location end = iterator->GetEndLocation();
313 scope->setEndLocation(protocol::Debugger::Location::create()
314 .setScriptId(scriptId)
315 .setLineNumber(end.GetLineNumber())
316 .setColumnNumber(end.GetColumnNumber())
317 .build());
318 }
319 (*scopes)->emplace_back(std::move(scope));
320 }
321 return Response::Success();
322}
323
324protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
325 const String16& key) {
326 protocol::DictionaryValue* value = object->getObject(key);
327 if (value) return value;
328 std::unique_ptr<protocol::DictionaryValue> newDictionary =
329 protocol::DictionaryValue::create();
330 value = newDictionary.get();
331 object->setObject(key, std::move(newDictionary));
332 return value;
333}
334
335Response isValidPosition(protocol::Debugger::ScriptPosition* position) {
336 if (position->getLineNumber() < 0)
337 return Response::ServerError("Position missing 'line' or 'line' < 0.");
338 if (position->getColumnNumber() < 0)
339 return Response::ServerError("Position missing 'column' or 'column' < 0.");
340 return Response::Success();
341}
342
343Response isValidRangeOfPositions(std::vector<std::pair<int, int>>& positions) {
344 for (size_t i = 1; i < positions.size(); ++i) {
345 if (positions[i - 1].first < positions[i].first) continue;
346 if (positions[i - 1].first == positions[i].first &&
347 positions[i - 1].second < positions[i].second)
348 continue;
349 return Response::ServerError(
350 "Input positions array is not sorted or contains duplicate values.");
351 }
352 return Response::Success();
353}
354
355bool hitBreakReasonEncodedAsOther(v8::debug::BreakReasons breakReasons) {
356 // The listed break reasons are not explicitly encoded in CDP when
357 // reporting the break. They are summarized as 'other'.
358 v8::debug::BreakReasons otherBreakReasons(
359 {v8::debug::BreakReason::kStep,
360 v8::debug::BreakReason::kDebuggerStatement,
361 v8::debug::BreakReason::kScheduled, v8::debug::BreakReason::kAsyncStep,
362 v8::debug::BreakReason::kAlreadyPaused});
363 return breakReasons.contains_any(otherBreakReasons);
364}
365} // namespace
366
367V8DebuggerAgentImpl::V8DebuggerAgentImpl(
368 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
369 protocol::DictionaryValue* state)
370 : m_inspector(session->inspector()),
371 m_debugger(m_inspector->debugger()),
372 m_session(session),
373 m_enabled(false),
374 m_state(state),
375 m_frontend(frontendChannel),
376 m_isolate(m_inspector->isolate()) {}
377
378V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default;
379
380void V8DebuggerAgentImpl::enableImpl() {
381 m_enabled = true;
382 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
383 m_debugger->enable();
384
385 std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts =
386 m_debugger->getCompiledScripts(m_session->contextGroupId(), this);
387 for (auto& script : compiledScripts) {
388 didParseSource(std::move(script), true);
389 }
390
391 m_breakpointsActive = true;
392 m_debugger->setBreakpointsActive(true);
393
394 if (isPaused()) {
6
Assuming the condition is true
7
Taking true branch
395 didPause(0, v8::Local<v8::Value>(), std::vector<v8::debug::BreakpointId>(),
8
Calling 'V8DebuggerAgentImpl::didPause'
396 v8::debug::kException, false,
397 v8::debug::BreakReasons({v8::debug::BreakReason::kAlreadyPaused}));
398 }
399}
400
401Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
402 String16* outDebuggerId) {
403 m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
404 maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
405 *outDebuggerId =
406 m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
407 if (enabled()) return Response::Success();
1
Assuming the condition is false
2
Taking false branch
408
409 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
3
Assuming the condition is false
4
Taking false branch
410 return Response::ServerError("Script execution is prohibited");
411
412 enableImpl();
5
Calling 'V8DebuggerAgentImpl::enableImpl'
413 return Response::Success();
414}
415
416Response V8DebuggerAgentImpl::disable() {
417 if (!enabled()) return Response::Success();
418
419 m_state->remove(DebuggerAgentState::breakpointsByRegex);
420 m_state->remove(DebuggerAgentState::breakpointsByUrl);
421 m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
422 m_state->remove(DebuggerAgentState::breakpointHints);
423 m_state->remove(DebuggerAgentState::instrumentationBreakpoints);
424
425 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
426 v8::debug::NoBreakOnException);
427 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
428
429 if (m_breakpointsActive) {
430 m_debugger->setBreakpointsActive(false);
431 m_breakpointsActive = false;
432 }
433 m_blackboxedPositions.clear();
434 m_blackboxPattern.reset();
435 resetBlackboxedStateCache();
436 m_skipList.clear();
437 m_scripts.clear();
438 m_cachedScripts.clear();
439 m_cachedScriptSize = 0;
440 for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
441 v8::debug::RemoveBreakpoint(m_isolate, it.first);
442 }
443 m_breakpointIdToDebuggerBreakpointIds.clear();
444 m_debuggerBreakpointIdToBreakpointId.clear();
445 m_debugger->setAsyncCallStackDepth(this, 0);
446 clearBreakDetails();
447 m_skipAllPauses = false;
448 m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
449 m_state->remove(DebuggerAgentState::blackboxPattern);
450 m_enabled = false;
451 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
452 m_debugger->disable();
453 return Response::Success();
454}
455
456void V8DebuggerAgentImpl::restore() {
457 DCHECK(!m_enabled)((void) 0);
458 if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
459 return;
460 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
461 return;
462
463 enableImpl();
464
465 int pauseState = v8::debug::NoBreakOnException;
466 m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
467 setPauseOnExceptionsImpl(pauseState);
468
469 m_skipAllPauses =
470 m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);
471
472 int asyncCallStackDepth = 0;
473 m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
474 &asyncCallStackDepth);
475 m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);
476
477 String16 blackboxPattern;
478 if (m_state->getString(DebuggerAgentState::blackboxPattern,
479 &blackboxPattern)) {
480 setBlackboxPattern(blackboxPattern);
481 }
482}
483
484Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
485 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
486 if (m_breakpointsActive == active) return Response::Success();
487 m_breakpointsActive = active;
488 m_debugger->setBreakpointsActive(active);
489 if (!active && !m_breakReason.empty()) {
490 clearBreakDetails();
491 m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
492 }
493 return Response::Success();
494}
495
496Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
497 m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
498 m_skipAllPauses = skip;
499 return Response::Success();
500}
501
502static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
503 BreakpointType type, const String16& selector) {
504 switch (type) {
505 case BreakpointType::kByUrl:
506 return script.sourceURL() == selector;
507 case BreakpointType::kByScriptHash:
508 return script.hash() == selector;
509 case BreakpointType::kByUrlRegex: {
510 V8Regex regex(inspector, selector, true);
511 return regex.match(script.sourceURL()) != -1;
512 }
513 case BreakpointType::kByScriptId: {
514 return script.scriptId() == selector;
515 }
516 default:
517 return false;
518 }
519}
520
521Response V8DebuggerAgentImpl::setBreakpointByUrl(
522 int lineNumber, Maybe<String16> optionalURL,
523 Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash,
524 Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition,
525 String16* outBreakpointId,
526 std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
527 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
528
529 *locations = std::make_unique<Array<protocol::Debugger::Location>>();
530
531 int specified = (optionalURL.isJust() ? 1 : 0) +
532 (optionalURLRegex.isJust() ? 1 : 0) +
533 (optionalScriptHash.isJust() ? 1 : 0);
534 if (specified != 1) {
535 return Response::ServerError(
536 "Either url or urlRegex or scriptHash must be specified.");
537 }
538 int columnNumber = 0;
539 if (optionalColumnNumber.isJust()) {
540 columnNumber = optionalColumnNumber.fromJust();
541 if (columnNumber < 0)
542 return Response::ServerError("Incorrect column number");
543 }
544
545 BreakpointType type = BreakpointType::kByUrl;
546 String16 selector;
547 if (optionalURLRegex.isJust()) {
548 selector = optionalURLRegex.fromJust();
549 type = BreakpointType::kByUrlRegex;
550 } else if (optionalURL.isJust()) {
551 selector = optionalURL.fromJust();
552 type = BreakpointType::kByUrl;
553 } else if (optionalScriptHash.isJust()) {
554 selector = optionalScriptHash.fromJust();
555 type = BreakpointType::kByScriptHash;
556 }
557
558 String16 condition = optionalCondition.fromMaybe(String16());
559 String16 breakpointId =
560 generateBreakpointId(type, selector, lineNumber, columnNumber);
561 protocol::DictionaryValue* breakpoints;
562 switch (type) {
563 case BreakpointType::kByUrlRegex:
564 breakpoints =
565 getOrCreateObject(m_state, DebuggerAgentState::breakpointsByRegex);
566 break;
567 case BreakpointType::kByUrl:
568 breakpoints = getOrCreateObject(
569 getOrCreateObject(m_state, DebuggerAgentState::breakpointsByUrl),
570 selector);
571 break;
572 case BreakpointType::kByScriptHash:
573 breakpoints = getOrCreateObject(
574 getOrCreateObject(m_state,
575 DebuggerAgentState::breakpointsByScriptHash),
576 selector);
577 break;
578 default:
579 UNREACHABLE()V8_Fatal("unreachable code");
580 }
581 if (breakpoints->get(breakpointId)) {
582 return Response::ServerError(
583 "Breakpoint at specified location already exists.");
584 }
585
586 String16 hint;
587 for (const auto& script : m_scripts) {
588 if (!matches(m_inspector, *script.second, type, selector)) continue;
589 if (!hint.isEmpty()) {
590 adjustBreakpointLocation(*script.second, hint, &lineNumber,
591 &columnNumber);
592 }
593 std::unique_ptr<protocol::Debugger::Location> location = setBreakpointImpl(
594 breakpointId, script.first, condition, lineNumber, columnNumber);
595 if (location && type != BreakpointType::kByUrlRegex) {
596 hint = breakpointHint(*script.second, location->getLineNumber(),
597 location->getColumnNumber(columnNumber));
598 }
599 if (location) (*locations)->emplace_back(std::move(location));
600 }
601 breakpoints->setString(breakpointId, condition);
602 if (!hint.isEmpty()) {
603 protocol::DictionaryValue* breakpointHints =
604 getOrCreateObject(m_state, DebuggerAgentState::breakpointHints);
605 breakpointHints->setString(breakpointId, hint);
606 }
607 *outBreakpointId = breakpointId;
608 return Response::Success();
609}
610
611Response V8DebuggerAgentImpl::setBreakpoint(
612 std::unique_ptr<protocol::Debugger::Location> location,
613 Maybe<String16> optionalCondition, String16* outBreakpointId,
614 std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
615 String16 breakpointId = generateBreakpointId(
616 BreakpointType::kByScriptId, location->getScriptId(),
617 location->getLineNumber(), location->getColumnNumber(0));
618 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
619
620 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
621 m_breakpointIdToDebuggerBreakpointIds.end()) {
622 return Response::ServerError(
623 "Breakpoint at specified location already exists.");
624 }
625 *actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
626 optionalCondition.fromMaybe(String16()),
627 location->getLineNumber(),
628 location->getColumnNumber(0));
629 if (!*actualLocation)
630 return Response::ServerError("Could not resolve breakpoint");
631 *outBreakpointId = breakpointId;
632 return Response::Success();
633}
634
635Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
636 const String16& functionObjectId, Maybe<String16> optionalCondition,
637 String16* outBreakpointId) {
638 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
639
640 InjectedScript::ObjectScope scope(m_session, functionObjectId);
641 Response response = scope.initialize();
642 if (!response.IsSuccess()) return response;
643 if (!scope.object()->IsFunction()) {
644 return Response::ServerError("Could not find function with given id");
645 }
646 v8::Local<v8::Function> function =
647 v8::Local<v8::Function>::Cast(scope.object());
648 String16 breakpointId =
649 generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
650 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
651 m_breakpointIdToDebuggerBreakpointIds.end()) {
652 return Response::ServerError(
653 "Breakpoint at specified location already exists.");
654 }
655 v8::Local<v8::String> condition =
656 toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
657 setBreakpointImpl(breakpointId, function, condition);
658 *outBreakpointId = breakpointId;
659 return Response::Success();
660}
661
662Response V8DebuggerAgentImpl::setInstrumentationBreakpoint(
663 const String16& instrumentation, String16* outBreakpointId) {
664 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
665 String16 breakpointId = generateInstrumentationBreakpointId(instrumentation);
666 protocol::DictionaryValue* breakpoints = getOrCreateObject(
667 m_state, DebuggerAgentState::instrumentationBreakpoints);
668 if (breakpoints->get(breakpointId)) {
669 return Response::ServerError(
670 "Instrumentation breakpoint is already enabled.");
671 }
672 breakpoints->setBoolean(breakpointId, true);
673 *outBreakpointId = breakpointId;
674 return Response::Success();
675}
676
677Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
678 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
679 BreakpointType type;
680 String16 selector;
681 if (!parseBreakpointId(breakpointId, &type, &selector)) {
682 return Response::Success();
683 }
684 protocol::DictionaryValue* breakpoints = nullptr;
685 switch (type) {
686 case BreakpointType::kByUrl: {
687 protocol::DictionaryValue* breakpointsByUrl =
688 m_state->getObject(DebuggerAgentState::breakpointsByUrl);
689 if (breakpointsByUrl) {
690 breakpoints = breakpointsByUrl->getObject(selector);
691 }
692 } break;
693 case BreakpointType::kByScriptHash: {
694 protocol::DictionaryValue* breakpointsByScriptHash =
695 m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
696 if (breakpointsByScriptHash) {
697 breakpoints = breakpointsByScriptHash->getObject(selector);
698 }
699 } break;
700 case BreakpointType::kByUrlRegex:
701 breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
702 break;
703 case BreakpointType::kInstrumentationBreakpoint:
704 breakpoints =
705 m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
706 break;
707 default:
708 break;
709 }
710 if (breakpoints) breakpoints->remove(breakpointId);
711 protocol::DictionaryValue* breakpointHints =
712 m_state->getObject(DebuggerAgentState::breakpointHints);
713 if (breakpointHints) breakpointHints->remove(breakpointId);
714
715 // Get a list of scripts to remove breakpoints.
716 // TODO(duongn): we can do better here if from breakpoint id we can tell it is
717 // not Wasm breakpoint.
718 std::vector<V8DebuggerScript*> scripts;
719 for (const auto& scriptIter : m_scripts) {
720 const bool scriptSelectorMatch =
721 matches(m_inspector, *scriptIter.second, type, selector);
722 const bool isInstrumentation =
723 type == BreakpointType::kInstrumentationBreakpoint;
724 if (!scriptSelectorMatch && !isInstrumentation) continue;
725 V8DebuggerScript* script = scriptIter.second.get();
726 if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly) {
727 scripts.push_back(script);
728 }
729 }
730 removeBreakpointImpl(breakpointId, scripts);
731
732 return Response::Success();
733}
734
735void V8DebuggerAgentImpl::removeBreakpointImpl(
736 const String16& breakpointId,
737 const std::vector<V8DebuggerScript*>& scripts) {
738 DCHECK(enabled())((void) 0);
739 BreakpointIdToDebuggerBreakpointIdsMap::iterator
740 debuggerBreakpointIdsIterator =
741 m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
742 if (debuggerBreakpointIdsIterator ==
743 m_breakpointIdToDebuggerBreakpointIds.end()) {
744 return;
745 }
746 for (const auto& id : debuggerBreakpointIdsIterator->second) {
747#if V8_ENABLE_WEBASSEMBLY1
748 for (auto& script : scripts) {
749 script->removeWasmBreakpoint(id);
750 }
751#endif // V8_ENABLE_WEBASSEMBLY
752 v8::debug::RemoveBreakpoint(m_isolate, id);
753 m_debuggerBreakpointIdToBreakpointId.erase(id);
754 }
755 m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
756}
757
758Response V8DebuggerAgentImpl::getPossibleBreakpoints(
759 std::unique_ptr<protocol::Debugger::Location> start,
760 Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
761 std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
762 locations) {
763 String16 scriptId = start->getScriptId();
764
765 if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
766 return Response::ServerError(
767 "start.lineNumber and start.columnNumber should be >= 0");
768
769 v8::debug::Location v8Start(start->getLineNumber(),
770 start->getColumnNumber(0));
771 v8::debug::Location v8End;
772 if (end.isJust()) {
773 if (end.fromJust()->getScriptId() != scriptId)
774 return Response::ServerError(
775 "Locations should contain the same scriptId");
776 int line = end.fromJust()->getLineNumber();
777 int column = end.fromJust()->getColumnNumber(0);
778 if (line < 0 || column < 0)
779 return Response::ServerError(
780 "end.lineNumber and end.columnNumber should be >= 0");
781 v8End = v8::debug::Location(line, column);
782 }
783 auto it = m_scripts.find(scriptId);
784 if (it == m_scripts.end()) return Response::ServerError("Script not found");
785 std::vector<v8::debug::BreakLocation> v8Locations;
786 {
787 v8::HandleScope handleScope(m_isolate);
788 int contextId = it->second->executionContextId();
789 InspectedContext* inspected = m_inspector->getContext(contextId);
790 if (!inspected) {
791 return Response::ServerError("Cannot retrive script context");
792 }
793 v8::Context::Scope contextScope(inspected->context());
794 v8::MicrotasksScope microtasks(m_isolate,
795 v8::MicrotasksScope::kDoNotRunMicrotasks);
796 v8::TryCatch tryCatch(m_isolate);
797 it->second->getPossibleBreakpoints(
798 v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations);
799 }
800
801 *locations =
802 std::make_unique<protocol::Array<protocol::Debugger::BreakLocation>>();
803
804 // TODO(1106269): Return an error instead of capping the number of
805 // breakpoints.
806 const size_t numBreakpointsToSend =
807 std::min(v8Locations.size(), kMaxNumBreakpoints);
808 for (size_t i = 0; i < numBreakpointsToSend; ++i) {
809 std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
810 protocol::Debugger::BreakLocation::create()
811 .setScriptId(scriptId)
812 .setLineNumber(v8Locations[i].GetLineNumber())
813 .setColumnNumber(v8Locations[i].GetColumnNumber())
814 .build();
815 if (v8Locations[i].type() != v8::debug::kCommonBreakLocation) {
816 breakLocation->setType(breakLocationType(v8Locations[i].type()));
817 }
818 (*locations)->emplace_back(std::move(breakLocation));
819 }
820 return Response::Success();
821}
822
823Response V8DebuggerAgentImpl::continueToLocation(
824 std::unique_ptr<protocol::Debugger::Location> location,
825 Maybe<String16> targetCallFrames) {
826 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
827 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
828 ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
829 if (it == m_scripts.end()) {
830 return Response::ServerError("Cannot continue to specified location");
831 }
832 V8DebuggerScript* script = it->second.get();
833 int contextId = script->executionContextId();
834 InspectedContext* inspected = m_inspector->getContext(contextId);
835 if (!inspected)
836 return Response::ServerError("Cannot continue to specified location");
837 v8::HandleScope handleScope(m_isolate);
838 v8::Context::Scope contextScope(inspected->context());
839 return m_debugger->continueToLocation(
840 m_session->contextGroupId(), script, std::move(location),
841 targetCallFrames.fromMaybe(
842 protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
843}
844
845Response V8DebuggerAgentImpl::getStackTrace(
846 std::unique_ptr<protocol::Runtime::StackTraceId> inStackTraceId,
847 std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
848 bool isOk = false;
849 int64_t id = inStackTraceId->getId().toInteger64(&isOk);
850 if (!isOk) return Response::ServerError("Invalid stack trace id");
851
852 internal::V8DebuggerId debuggerId;
853 if (inStackTraceId->hasDebuggerId()) {
854 debuggerId =
855 internal::V8DebuggerId(inStackTraceId->getDebuggerId(String16()));
856 } else {
857 debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
858 }
859 if (!debuggerId.isValid())
860 return Response::ServerError("Invalid stack trace id");
861
862 V8StackTraceId v8StackTraceId(id, debuggerId.pair());
863 if (v8StackTraceId.IsInvalid())
864 return Response::ServerError("Invalid stack trace id");
865 auto stack =
866 m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
867 if (!stack) {
868 return Response::ServerError("Stack trace with given id is not found");
869 }
870 *outStackTrace = stack->buildInspectorObject(
871 m_debugger, m_debugger->maxAsyncCallChainDepth());
872 return Response::Success();
873}
874
875bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
876 const v8::debug::Location& start,
877 const v8::debug::Location& end) {
878 ScriptsMap::iterator it = m_scripts.find(scriptId);
879 if (it == m_scripts.end()) {
880 // Unknown scripts are blackboxed.
881 return true;
882 }
883 if (m_blackboxPattern) {
884 const String16& scriptSourceURL = it->second->sourceURL();
885 if (!scriptSourceURL.isEmpty() &&
886 m_blackboxPattern->match(scriptSourceURL) != -1)
887 return true;
888 }
889 auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
890 if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
891
892 const std::vector<std::pair<int, int>>& ranges =
893 itBlackboxedPositions->second;
894 auto itStartRange = std::lower_bound(
895 ranges.begin(), ranges.end(),
896 std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
897 positionComparator);
898 auto itEndRange = std::lower_bound(
899 itStartRange, ranges.end(),
900 std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
901 positionComparator);
902 // Ranges array contains positions in script where blackbox state is changed.
903 // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
904 // blackboxed...
905 return itStartRange == itEndRange &&
906 std::distance(ranges.begin(), itStartRange) % 2;
907}
908
909bool V8DebuggerAgentImpl::shouldBeSkipped(const String16& scriptId, int line,
910 int column) {
911 if (m_skipList.empty()) return false;
912
913 auto it = m_skipList.find(scriptId);
914 if (it == m_skipList.end()) return false;
915
916 const std::vector<std::pair<int, int>>& ranges = it->second;
917 DCHECK(!ranges.empty())((void) 0);
918 const std::pair<int, int> location = std::make_pair(line, column);
919 auto itLowerBound = std::lower_bound(ranges.begin(), ranges.end(), location,
920 positionComparator);
921
922 bool shouldSkip = false;
923 if (itLowerBound != ranges.end()) {
924 // Skip lists are defined as pairs of locations that specify the
925 // start and the end of ranges to skip: [ranges[0], ranges[1], ..], where
926 // locations in [ranges[0], ranges[1]) should be skipped, i.e.
927 // [(lineStart, columnStart), (lineEnd, columnEnd)).
928 const bool isSameAsLowerBound = location == *itLowerBound;
929 const bool isUnevenIndex = (itLowerBound - ranges.begin()) % 2;
930 shouldSkip = isSameAsLowerBound ^ isUnevenIndex;
931 }
932
933 return shouldSkip;
934}
935
936bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
937 return enabled() && (isOOMBreak || !m_skipAllPauses);
938}
939
940std::unique_ptr<protocol::Debugger::Location>
941V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
942 const String16& scriptId,
943 const String16& condition,
944 int lineNumber, int columnNumber) {
945 v8::HandleScope handles(m_isolate);
946 DCHECK(enabled())((void) 0);
947
948 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
949 if (scriptIterator == m_scripts.end()) return nullptr;
950 V8DebuggerScript* script = scriptIterator->second.get();
951 if (lineNumber < script->startLine() || script->endLine() < lineNumber) {
952 return nullptr;
953 }
954 if (lineNumber == script->startLine() &&
955 columnNumber < script->startColumn()) {
956 return nullptr;
957 }
958 if (lineNumber == script->endLine() && script->endColumn() < columnNumber) {
959 return nullptr;
960 }
961
962 v8::debug::BreakpointId debuggerBreakpointId;
963 v8::debug::Location location(lineNumber, columnNumber);
964 int contextId = script->executionContextId();
965 InspectedContext* inspected = m_inspector->getContext(contextId);
966 if (!inspected) return nullptr;
967
968 {
969 v8::Context::Scope contextScope(inspected->context());
970 if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
971 return nullptr;
972 }
973 }
974
975 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
976 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
977 debuggerBreakpointId);
978
979 return protocol::Debugger::Location::create()
980 .setScriptId(scriptId)
981 .setLineNumber(location.GetLineNumber())
982 .setColumnNumber(location.GetColumnNumber())
983 .build();
984}
985
986void V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
987 v8::Local<v8::Function> function,
988 v8::Local<v8::String> condition) {
989 v8::debug::BreakpointId debuggerBreakpointId;
990 if (!v8::debug::SetFunctionBreakpoint(function, condition,
991 &debuggerBreakpointId)) {
992 return;
993 }
994 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
995 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
996 debuggerBreakpointId);
997}
998
999Response V8DebuggerAgentImpl::searchInContent(
1000 const String16& scriptId, const String16& query,
1001 Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
1002 std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
1003 v8::HandleScope handles(m_isolate);
1004 ScriptsMap::iterator it = m_scripts.find(scriptId);
1005 if (it == m_scripts.end())
1006 return Response::ServerError("No script for id: " + scriptId.utf8());
1007
1008 *results = std::make_unique<protocol::Array<protocol::Debugger::SearchMatch>>(
1009 searchInTextByLinesImpl(m_session, it->second->source(0), query,
1010 optionalCaseSensitive.fromMaybe(false),
1011 optionalIsRegex.fromMaybe(false)));
1012 return Response::Success();
1013}
1014
1015Response V8DebuggerAgentImpl::setScriptSource(
1016 const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
1017 Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
1018 Maybe<bool>* stackChanged,
1019 Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
1020 Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
1021 Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
1022 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1023
1024 ScriptsMap::iterator it = m_scripts.find(scriptId);
1025 if (it == m_scripts.end()) {
1026 return Response::ServerError("No script with given id found");
1027 }
1028 int contextId = it->second->executionContextId();
1029 InspectedContext* inspected = m_inspector->getContext(contextId);
1030 if (!inspected) {
1031 return Response::InternalError();
1032 }
1033 v8::HandleScope handleScope(m_isolate);
1034 v8::Local<v8::Context> context = inspected->context();
1035 v8::Context::Scope contextScope(context);
1036
1037 v8::debug::LiveEditResult result;
1038 it->second->setSource(newContent, dryRun.fromMaybe(false), &result);
1039 if (result.status != v8::debug::LiveEditResult::OK) {
1040 *optOutCompileError =
1041 protocol::Runtime::ExceptionDetails::create()
1042 .setExceptionId(m_inspector->nextExceptionId())
1043 .setText(toProtocolString(m_isolate, result.message))
1044 .setLineNumber(result.line_number != -1 ? result.line_number - 1
1045 : 0)
1046 .setColumnNumber(result.column_number != -1 ? result.column_number
1047 : 0)
1048 .build();
1049 return Response::Success();
1050 } else {
1051 *stackChanged = result.stack_changed;
1052 }
1053 std::unique_ptr<Array<CallFrame>> callFrames;
1054 Response response = currentCallFrames(&callFrames);
1055 if (!response.IsSuccess()) return response;
1056 *newCallFrames = std::move(callFrames);
1057 *asyncStackTrace = currentAsyncStackTrace();
1058 *asyncStackTraceId = currentExternalStackTrace();
1059 return Response::Success();
1060}
1061
1062Response V8DebuggerAgentImpl::restartFrame(
1063 const String16& callFrameId,
1064 std::unique_ptr<Array<CallFrame>>* newCallFrames,
1065 Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
1066 Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
1067 return Response::ServerError("Frame restarting not supported");
1068}
1069
1070Response V8DebuggerAgentImpl::getScriptSource(
1071 const String16& scriptId, String16* scriptSource,
1072 Maybe<protocol::Binary>* bytecode) {
1073 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1074 ScriptsMap::iterator it = m_scripts.find(scriptId);
1075 if (it == m_scripts.end()) {
1076 auto cachedScriptIt =
1077 std::find_if(m_cachedScripts.begin(), m_cachedScripts.end(),
1078 [&scriptId](const CachedScript& cachedScript) {
1079 return cachedScript.scriptId == scriptId;
1080 });
1081 if (cachedScriptIt != m_cachedScripts.end()) {
1082 *scriptSource = cachedScriptIt->source;
1083 *bytecode = protocol::Binary::fromSpan(cachedScriptIt->bytecode.data(),
1084 cachedScriptIt->bytecode.size());
1085 return Response::Success();
1086 }
1087 return Response::ServerError("No script for id: " + scriptId.utf8());
1088 }
1089 *scriptSource = it->second->source(0);
1090#if V8_ENABLE_WEBASSEMBLY1
1091 v8::MemorySpan<const uint8_t> span;
1092 if (it->second->wasmBytecode().To(&span)) {
1093 if (span.size() > kWasmBytecodeMaxLength) {
1094 return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
1095 }
1096 *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
1097 }
1098#endif // V8_ENABLE_WEBASSEMBLY
1099 return Response::Success();
1100}
1101
1102Response V8DebuggerAgentImpl::getWasmBytecode(const String16& scriptId,
1103 protocol::Binary* bytecode) {
1104#if V8_ENABLE_WEBASSEMBLY1
1105 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1106 ScriptsMap::iterator it = m_scripts.find(scriptId);
1107 if (it == m_scripts.end())
1108 return Response::ServerError("No script for id: " + scriptId.utf8());
1109 v8::MemorySpan<const uint8_t> span;
1110 if (!it->second->wasmBytecode().To(&span))
1111 return Response::ServerError("Script with id " + scriptId.utf8() +
1112 " is not WebAssembly");
1113 if (span.size() > kWasmBytecodeMaxLength) {
1114 return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
1115 }
1116 *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
1117 return Response::Success();
1118#else
1119 return Response::ServerError("WebAssembly is disabled");
1120#endif // V8_ENABLE_WEBASSEMBLY
1121}
1122
1123void V8DebuggerAgentImpl::pushBreakDetails(
1124 const String16& breakReason,
1125 std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
1126 m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
1127}
1128
1129void V8DebuggerAgentImpl::popBreakDetails() {
1130 if (m_breakReason.empty()) return;
1131 m_breakReason.pop_back();
1132}
1133
1134void V8DebuggerAgentImpl::clearBreakDetails() {
1135 std::vector<BreakReason> emptyBreakReason;
1136 m_breakReason.swap(emptyBreakReason);
1137}
1138
1139void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
1140 const String16& breakReason,
1141 std::unique_ptr<protocol::DictionaryValue> data) {
1142 if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1143 if (m_breakReason.empty()) {
1144 m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1145 }
1146 pushBreakDetails(breakReason, std::move(data));
1147}
1148
1149void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
1150 if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1151 if (m_breakReason.size() == 1) {
1152 m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
1153 }
1154 popBreakDetails();
1155}
1156
1157Response V8DebuggerAgentImpl::pause() {
1158 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1159 if (isPaused()) return Response::Success();
1160
1161 if (m_debugger->canBreakProgram()) {
1162 m_debugger->interruptAndBreak(m_session->contextGroupId());
1163 } else {
1164 pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
1165 m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1166 }
1167
1168 return Response::Success();
1169}
1170
1171Response V8DebuggerAgentImpl::resume(Maybe<bool> terminateOnResume) {
1172 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1173 m_session->releaseObjectGroup(kBacktraceObjectGroup);
1174 m_debugger->continueProgram(m_session->contextGroupId(),
1175 terminateOnResume.fromMaybe(false));
1176 return Response::Success();
1177}
1178
1179Response V8DebuggerAgentImpl::stepOver(
1180 Maybe<protocol::Array<protocol::Debugger::LocationRange>> inSkipList) {
1181 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1182
1183 if (inSkipList.isJust()) {
1184 const Response res = processSkipList(inSkipList.fromJust());
1185 if (res.IsError()) return res;
1186 } else {
1187 m_skipList.clear();
1188 }
1189
1190 m_session->releaseObjectGroup(kBacktraceObjectGroup);
1191 m_debugger->stepOverStatement(m_session->contextGroupId());
1192 return Response::Success();
1193}
1194
1195Response V8DebuggerAgentImpl::stepInto(
1196 Maybe<bool> inBreakOnAsyncCall,
1197 Maybe<protocol::Array<protocol::Debugger::LocationRange>> inSkipList) {
1198 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1199
1200 if (inSkipList.isJust()) {
1201 const Response res = processSkipList(inSkipList.fromJust());
1202 if (res.IsError()) return res;
1203 } else {
1204 m_skipList.clear();
1205 }
1206
1207 m_session->releaseObjectGroup(kBacktraceObjectGroup);
1208 m_debugger->stepIntoStatement(m_session->contextGroupId(),
1209 inBreakOnAsyncCall.fromMaybe(false));
1210 return Response::Success();
1211}
1212
1213Response V8DebuggerAgentImpl::stepOut() {
1214 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1215 m_session->releaseObjectGroup(kBacktraceObjectGroup);
1216 m_debugger->stepOutOfFunction(m_session->contextGroupId());
1217 return Response::Success();
1218}
1219
1220Response V8DebuggerAgentImpl::pauseOnAsyncCall(
1221 std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1222 // Deprecated, just return OK.
1223 return Response::Success();
1224}
1225
1226Response V8DebuggerAgentImpl::setPauseOnExceptions(
1227 const String16& stringPauseState) {
1228 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1229 v8::debug::ExceptionBreakState pauseState;
1230 if (stringPauseState == "none") {
1231 pauseState = v8::debug::NoBreakOnException;
1232 } else if (stringPauseState == "all") {
1233 pauseState = v8::debug::BreakOnAnyException;
1234 } else if (stringPauseState == "uncaught") {
1235 pauseState = v8::debug::BreakOnUncaughtException;
1236 } else {
1237 return Response::ServerError("Unknown pause on exceptions mode: " +
1238 stringPauseState.utf8());
1239 }
1240 setPauseOnExceptionsImpl(pauseState);
1241 return Response::Success();
1242}
1243
1244void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
1245 // TODO(dgozman): this changes the global state and forces all context groups
1246 // to pause. We should make this flag be per-context-group.
1247 m_debugger->setPauseOnExceptionsState(
1248 static_cast<v8::debug::ExceptionBreakState>(pauseState));
1249 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
1250}
1251
1252Response V8DebuggerAgentImpl::evaluateOnCallFrame(
1253 const String16& callFrameId, const String16& expression,
1254 Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
1255 Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
1256 Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
1257 std::unique_ptr<RemoteObject>* result,
1258 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
1259 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1260 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1261 Response response = scope.initialize();
1262 if (!response.IsSuccess()) return response;
1263 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
1264 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
1265
1266 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1267 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1268 if (it->Done()) {
1269 return Response::ServerError("Could not find call frame with given id");
1270 }
1271
1272 v8::MaybeLocal<v8::Value> maybeResultValue;
1273 {
1274 V8InspectorImpl::EvaluateScope evaluateScope(scope);
1275 if (timeout.isJust()) {
1276 response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
1277 if (!response.IsSuccess()) return response;
1278 }
1279 maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
1280 throwOnSideEffect.fromMaybe(false));
1281 }
1282 // Re-initialize after running client's code, as it could have destroyed
1283 // context or session.
1284 response = scope.initialize();
1285 if (!response.IsSuccess()) return response;
1286 WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
1287 : WrapMode::kNoPreview;
1288 if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
1289 return scope.injectedScript()->wrapEvaluateResult(
1290 maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), mode,
1291 result, exceptionDetails);
1292}
1293
1294Response V8DebuggerAgentImpl::setVariableValue(
1295 int scopeNumber, const String16& variableName,
1296 std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
1297 const String16& callFrameId) {
1298 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1299 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1300 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1301 Response response = scope.initialize();
1302 if (!response.IsSuccess()) return response;
1303 v8::Local<v8::Value> newValue;
1304 response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
1305 &newValue);
1306 if (!response.IsSuccess()) return response;
1307
1308 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1309 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1310 if (it->Done()) {
1311 return Response::ServerError("Could not find call frame with given id");
1312 }
1313 auto scopeIterator = it->GetScopeIterator();
1314 while (!scopeIterator->Done() && scopeNumber > 0) {
1315 --scopeNumber;
1316 scopeIterator->Advance();
1317 }
1318 if (scopeNumber != 0) {
1319 return Response::ServerError("Could not find scope with given number");
1320 }
1321
1322 if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
1323 newValue) ||
1324 scope.tryCatch().HasCaught()) {
1325 return Response::InternalError();
1326 }
1327 return Response::Success();
1328}
1329
1330Response V8DebuggerAgentImpl::setReturnValue(
1331 std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
1332 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1333 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1334 v8::HandleScope handleScope(m_isolate);
1335 auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1336 if (iterator->Done()) {
1337 return Response::ServerError("Could not find top call frame");
1338 }
1339 if (iterator->GetReturnValue().IsEmpty()) {
1340 return Response::ServerError(
1341 "Could not update return value at non-return position");
1342 }
1343 InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
1344 Response response = scope.initialize();
1345 if (!response.IsSuccess()) return response;
1346 v8::Local<v8::Value> newValue;
1347 response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
1348 &newValue);
1349 if (!response.IsSuccess()) return response;
1350 v8::debug::SetReturnValue(m_isolate, newValue);
1351 return Response::Success();
1352}
1353
1354Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
1355 if (!enabled() && !m_session->runtimeAgent()->enabled()) {
1356 return Response::ServerError(kDebuggerNotEnabled);
1357 }
1358 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
1359 m_debugger->setAsyncCallStackDepth(this, depth);
1360 return Response::Success();
1361}
1362
1363Response V8DebuggerAgentImpl::setBlackboxPatterns(
1364 std::unique_ptr<protocol::Array<String16>> patterns) {
1365 if (patterns->empty()) {
1366 m_blackboxPattern = nullptr;
1367 resetBlackboxedStateCache();
1368 m_state->remove(DebuggerAgentState::blackboxPattern);
1369 return Response::Success();
1370 }
1371
1372 String16Builder patternBuilder;
1373 patternBuilder.append('(');
1374 for (size_t i = 0; i < patterns->size() - 1; ++i) {
1375 patternBuilder.append((*patterns)[i]);
1376 patternBuilder.append("|");
1377 }
1378 patternBuilder.append(patterns->back());
1379 patternBuilder.append(')');
1380 String16 pattern = patternBuilder.toString();
1381 Response response = setBlackboxPattern(pattern);
1382 if (!response.IsSuccess()) return response;
1383 resetBlackboxedStateCache();
1384 m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
1385 return Response::Success();
1386}
1387
1388Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
1389 std::unique_ptr<V8Regex> regex(new V8Regex(
1390 m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
1391 if (!regex->isValid())
1392 return Response::ServerError("Pattern parser error: " +
1393 regex->errorMessage().utf8());
1394 m_blackboxPattern = std::move(regex);
1395 return Response::Success();
1396}
1397
1398void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
1399 for (const auto& it : m_scripts) {
1400 it.second->resetBlackboxedStateCache();
1401 }
1402}
1403
1404Response V8DebuggerAgentImpl::setBlackboxedRanges(
1405 const String16& scriptId,
1406 std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
1407 inPositions) {
1408 auto it = m_scripts.find(scriptId);
1409 if (it == m_scripts.end())
1410 return Response::ServerError("No script with passed id.");
1411
1412 if (inPositions->empty()) {
1413 m_blackboxedPositions.erase(scriptId);
1414 it->second->resetBlackboxedStateCache();
1415 return Response::Success();
1416 }
1417
1418 std::vector<std::pair<int, int>> positions;
1419 positions.reserve(inPositions->size());
1420 for (const std::unique_ptr<protocol::Debugger::ScriptPosition>& position :
1421 *inPositions) {
1422 Response res = isValidPosition(position.get());
1423 if (res.IsError()) return res;
1424
1425 positions.push_back(
1426 std::make_pair(position->getLineNumber(), position->getColumnNumber()));
1427 }
1428 Response res = isValidRangeOfPositions(positions);
1429 if (res.IsError()) return res;
1430
1431 m_blackboxedPositions[scriptId] = positions;
1432 it->second->resetBlackboxedStateCache();
1433 return Response::Success();
1434}
1435
1436Response V8DebuggerAgentImpl::currentCallFrames(
1437 std::unique_ptr<Array<CallFrame>>* result) {
1438 if (!isPaused()) {
1439 *result = std::make_unique<Array<CallFrame>>();
1440 return Response::Success();
1441 }
1442 v8::HandleScope handles(m_isolate);
1443 *result = std::make_unique<Array<CallFrame>>();
1444 auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1445 int frameOrdinal = 0;
1446 for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
1447 int contextId = iterator->GetContextId();
1448 InjectedScript* injectedScript = nullptr;
1449 if (contextId) m_session->findInjectedScript(contextId, injectedScript);
1450 String16 callFrameId = RemoteCallFrameId::serialize(
1451 m_inspector->isolateId(), contextId, frameOrdinal);
1452
1453 v8::debug::Location loc = iterator->GetSourceLocation();
1454
1455 std::unique_ptr<Array<Scope>> scopes;
1456 auto scopeIterator = iterator->GetScopeIterator();
1457 Response res =
1458 buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
1459 if (!res.IsSuccess()) return res;
1460
1461 std::unique_ptr<RemoteObject> protocolReceiver;
1462 if (injectedScript) {
1463 v8::Local<v8::Value> receiver;
1464 if (iterator->GetReceiver().ToLocal(&receiver)) {
1465 res =
1466 injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
1467 WrapMode::kNoPreview, &protocolReceiver);
1468 if (!res.IsSuccess()) return res;
1469 }
1470 }
1471 if (!protocolReceiver) {
1472 protocolReceiver = RemoteObject::create()
1473 .setType(RemoteObject::TypeEnum::Undefined)
1474 .build();
1475 }
1476
1477 v8::Local<v8::debug::Script> script = iterator->GetScript();
1478 DCHECK(!script.IsEmpty())((void) 0);
1479 std::unique_ptr<protocol::Debugger::Location> location =
1480 protocol::Debugger::Location::create()
1481 .setScriptId(String16::fromInteger(script->Id()))
1482 .setLineNumber(loc.GetLineNumber())
1483 .setColumnNumber(loc.GetColumnNumber())
1484 .build();
1485
1486 auto frame = CallFrame::create()
1487 .setCallFrameId(callFrameId)
1488 .setFunctionName(toProtocolString(
1489 m_isolate, iterator->GetFunctionDebugName()))
1490 .setLocation(std::move(location))
1491 .setUrl(String16())
1492 .setScopeChain(std::move(scopes))
1493 .setThis(std::move(protocolReceiver))
1494 .build();
1495
1496 v8::Local<v8::Function> func = iterator->GetFunction();
1497 if (!func.IsEmpty()) {
1498 frame->setFunctionLocation(
1499 protocol::Debugger::Location::create()
1500 .setScriptId(String16::fromInteger(func->ScriptId()))
1501 .setLineNumber(func->GetScriptLineNumber())
1502 .setColumnNumber(func->GetScriptColumnNumber())
1503 .build());
1504 }
1505
1506 v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
1507 if (!returnValue.IsEmpty() && injectedScript) {
1508 std::unique_ptr<RemoteObject> value;
1509 res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
1510 WrapMode::kNoPreview, &value);
1511 if (!res.IsSuccess()) return res;
1512 frame->setReturnValue(std::move(value));
1513 }
1514 (*result)->emplace_back(std::move(frame));
1515 }
1516 return Response::Success();
1517}
1518
1519std::unique_ptr<protocol::Runtime::StackTrace>
1520V8DebuggerAgentImpl::currentAsyncStackTrace() {
1521 std::shared_ptr<AsyncStackTrace> asyncParent =
1522 m_debugger->currentAsyncParent();
1523 if (!asyncParent) return nullptr;
1524 return asyncParent->buildInspectorObject(
1525 m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
1526}
1527
1528std::unique_ptr<protocol::Runtime::StackTraceId>
1529V8DebuggerAgentImpl::currentExternalStackTrace() {
1530 V8StackTraceId externalParent = m_debugger->currentExternalParent();
1531 if (externalParent.IsInvalid()) return nullptr;
1532 return protocol::Runtime::StackTraceId::create()
1533 .setId(stackTraceIdToString(externalParent.id))
1534 .setDebuggerId(
1535 internal::V8DebuggerId(externalParent.debugger_id).toString())
1536 .build();
1537}
1538
1539bool V8DebuggerAgentImpl::isPaused() const {
1540 return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
1541}
1542
1543static String16 getScriptLanguage(const V8DebuggerScript& script) {
1544 switch (script.getLanguage()) {
1545 case V8DebuggerScript::Language::WebAssembly:
1546 return protocol::Debugger::ScriptLanguageEnum::WebAssembly;
1547 case V8DebuggerScript::Language::JavaScript:
1548 return protocol::Debugger::ScriptLanguageEnum::JavaScript;
1549 }
1550}
1551
1552#if V8_ENABLE_WEBASSEMBLY1
1553static const char* getDebugSymbolTypeName(
1554 v8::debug::WasmScript::DebugSymbolsType type) {
1555 switch (type) {
1556 case v8::debug::WasmScript::DebugSymbolsType::None:
1557 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::None;
1558 case v8::debug::WasmScript::DebugSymbolsType::SourceMap:
1559 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1560 SourceMap;
1561 case v8::debug::WasmScript::DebugSymbolsType::EmbeddedDWARF:
1562 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1563 EmbeddedDWARF;
1564 case v8::debug::WasmScript::DebugSymbolsType::ExternalDWARF:
1565 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1566 ExternalDWARF;
1567 }
1568}
1569
1570static std::unique_ptr<protocol::Debugger::DebugSymbols> getDebugSymbols(
1571 const V8DebuggerScript& script) {
1572 v8::debug::WasmScript::DebugSymbolsType type;
1573 if (!script.getDebugSymbolsType().To(&type)) return {};
1574
1575 std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
1576 v8_inspector::protocol::Debugger::DebugSymbols::create()
1577 .setType(getDebugSymbolTypeName(type))
1578 .build();
1579 String16 externalUrl;
1580 if (script.getExternalDebugSymbolsURL().To(&externalUrl)) {
1581 debugSymbols->setExternalURL(externalUrl);
1582 }
1583 return debugSymbols;
1584}
1585#endif // V8_ENABLE_WEBASSEMBLY
1586
1587void V8DebuggerAgentImpl::didParseSource(
1588 std::unique_ptr<V8DebuggerScript> script, bool success) {
1589 v8::HandleScope handles(m_isolate);
1590 if (!success) {
1591 String16 scriptSource = script->source(0);
1592 script->setSourceURL(findSourceURL(scriptSource, false));
1593 script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
1594 }
1595
1596 int contextId = script->executionContextId();
1597 int contextGroupId = m_inspector->contextGroupId(contextId);
1598 InspectedContext* inspected =
1599 m_inspector->getContext(contextGroupId, contextId);
1600 std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1601 if (inspected) {
1602 // Script reused between different groups/sessions can have a stale
1603 // execution context id.
1604 const String16& aux = inspected->auxData();
1605 std::vector<uint8_t> cbor;
1606 v8_crdtp::json::ConvertJSONToCBOR(
1607 v8_crdtp::span<uint16_t>(aux.characters16(), aux.length()), &cbor);
1608 executionContextAuxData = protocol::DictionaryValue::cast(
1609 protocol::Value::parseBinary(cbor.data(), cbor.size()));
1610 }
1611 bool isLiveEdit = script->isLiveEdit();
1612 bool hasSourceURLComment = script->hasSourceURLComment();
1613 bool isModule = script->isModule();
1614 String16 scriptId = script->scriptId();
1615 String16 scriptURL = script->sourceURL();
1616 String16 embedderName = script->embedderName();
1617 String16 scriptLanguage = getScriptLanguage(*script);
1618 Maybe<int> codeOffset;
1619 std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols;
1620#if V8_ENABLE_WEBASSEMBLY1
1621 if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly)
1622 codeOffset = script->codeOffset();
1623 debugSymbols = getDebugSymbols(*script);
1624#endif // V8_ENABLE_WEBASSEMBLY
1625
1626 m_scripts[scriptId] = std::move(script);
1627 // Release the strong reference to get notified when debugger is the only
1628 // one that holds the script. Has to be done after script added to m_scripts.
1629 m_scripts[scriptId]->MakeWeak();
1630
1631 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1632 DCHECK(scriptIterator != m_scripts.end())((void) 0);
1633 V8DebuggerScript* scriptRef = scriptIterator->second.get();
1634 // V8 could create functions for parsed scripts before reporting and asks
1635 // inspector about blackboxed state, we should reset state each time when we
1636 // make any change that change isFunctionBlackboxed output - adding parsed
1637 // script is changing.
1638 scriptRef->resetBlackboxedStateCache();
1639
1640 Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
1641 Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
1642 std::move(executionContextAuxData));
1643 const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1644 const bool* hasSourceURLParam =
1645 hasSourceURLComment ? &hasSourceURLComment : nullptr;
1646 const bool* isModuleParam = isModule ? &isModule : nullptr;
1647 std::unique_ptr<V8StackTraceImpl> stack =
1648 V8StackTraceImpl::capture(m_inspector->debugger(), 1);
1649 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
1650 stack && !stack->isEmpty()
1651 ? stack->buildInspectorObjectImpl(m_debugger, 0)
1652 : nullptr;
1653
1654 if (!success) {
1655 m_frontend.scriptFailedToParse(
1656 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1657 scriptRef->endLine(), scriptRef->endColumn(), contextId,
1658 scriptRef->hash(), std::move(executionContextAuxDataParam),
1659 std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
1660 scriptRef->length(), std::move(stackTrace), std::move(codeOffset),
1661 std::move(scriptLanguage), embedderName);
1662 return;
1663 }
1664
1665 m_frontend.scriptParsed(
1666 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1667 scriptRef->endLine(), scriptRef->endColumn(), contextId,
1668 scriptRef->hash(), std::move(executionContextAuxDataParam),
1669 isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
1670 isModuleParam, scriptRef->length(), std::move(stackTrace),
1671 std::move(codeOffset), std::move(scriptLanguage), std::move(debugSymbols),
1672 embedderName);
1673
1674 std::vector<protocol::DictionaryValue*> potentialBreakpoints;
1675 if (!scriptURL.isEmpty()) {
1676 protocol::DictionaryValue* breakpointsByUrl =
1677 m_state->getObject(DebuggerAgentState::breakpointsByUrl);
1678 if (breakpointsByUrl) {
1679 potentialBreakpoints.push_back(breakpointsByUrl->getObject(scriptURL));
1680 }
1681 potentialBreakpoints.push_back(
1682 m_state->getObject(DebuggerAgentState::breakpointsByRegex));
1683 }
1684 protocol::DictionaryValue* breakpointsByScriptHash =
1685 m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
1686 if (breakpointsByScriptHash) {
1687 potentialBreakpoints.push_back(
1688 breakpointsByScriptHash->getObject(scriptRef->hash()));
1689 }
1690 protocol::DictionaryValue* breakpointHints =
1691 m_state->getObject(DebuggerAgentState::breakpointHints);
1692 for (auto breakpoints : potentialBreakpoints) {
1693 if (!breakpoints) continue;
1694 for (size_t i = 0; i < breakpoints->size(); ++i) {
1695 auto breakpointWithCondition = breakpoints->at(i);
1696 String16 breakpointId = breakpointWithCondition.first;
1697
1698 BreakpointType type;
1699 String16 selector;
1700 int lineNumber = 0;
1701 int columnNumber = 0;
1702 parseBreakpointId(breakpointId, &type, &selector, &lineNumber,
1703 &columnNumber);
1704
1705 if (!matches(m_inspector, *scriptRef, type, selector)) continue;
1706 String16 condition;
1707 breakpointWithCondition.second->asString(&condition);
1708 String16 hint;
1709 bool hasHint =
1710 breakpointHints && breakpointHints->getString(breakpointId, &hint);
1711 if (hasHint) {
1712 adjustBreakpointLocation(*scriptRef, hint, &lineNumber, &columnNumber);
1713 }
1714 std::unique_ptr<protocol::Debugger::Location> location =
1715 setBreakpointImpl(breakpointId, scriptId, condition, lineNumber,
1716 columnNumber);
1717 if (location)
1718 m_frontend.breakpointResolved(breakpointId, std::move(location));
1719 }
1720 }
1721 setScriptInstrumentationBreakpointIfNeeded(scriptRef);
1722}
1723
1724void V8DebuggerAgentImpl::setScriptInstrumentationBreakpointIfNeeded(
1725 V8DebuggerScript* scriptRef) {
1726 protocol::DictionaryValue* breakpoints =
1727 m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
1728 if (!breakpoints) return;
1729 bool isBlackboxed = isFunctionBlackboxed(
1730 scriptRef->scriptId(), v8::debug::Location(0, 0),
1731 v8::debug::Location(scriptRef->endLine(), scriptRef->endColumn()));
1732 if (isBlackboxed) return;
1733
1734 String16 sourceMapURL = scriptRef->sourceMappingURL();
1735 String16 breakpointId = generateInstrumentationBreakpointId(
1736 InstrumentationEnum::BeforeScriptExecution);
1737 if (!breakpoints->get(breakpointId)) {
1738 if (sourceMapURL.isEmpty()) return;
1739 breakpointId = generateInstrumentationBreakpointId(
1740 InstrumentationEnum::BeforeScriptWithSourceMapExecution);
1741 if (!breakpoints->get(breakpointId)) return;
1742 }
1743 v8::debug::BreakpointId debuggerBreakpointId;
1744 if (!scriptRef->setInstrumentationBreakpoint(&debuggerBreakpointId)) return;
1745
1746 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
1747 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
1748 debuggerBreakpointId);
1749}
1750
1751void V8DebuggerAgentImpl::didPauseOnInstrumentation(
1752 v8::debug::BreakpointId instrumentationId) {
1753 String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1754 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1755
1756 std::unique_ptr<Array<CallFrame>> protocolCallFrames;
1757 Response response = currentCallFrames(&protocolCallFrames);
1758 if (!response.IsSuccess())
1759 protocolCallFrames = std::make_unique<Array<CallFrame>>();
1760
1761 if (m_debuggerBreakpointIdToBreakpointId.find(instrumentationId) !=
1762 m_debuggerBreakpointIdToBreakpointId.end()) {
1763 DCHECK_GT(protocolCallFrames->size(), 0)((void) 0);
1764 if (protocolCallFrames->size() > 0) {
1765 breakReason = protocol::Debugger::Paused::ReasonEnum::Instrumentation;
1766 const String16 scriptId =
1767 protocolCallFrames->at(0)->getLocation()->getScriptId();
1768 DCHECK_NE(m_scripts.find(scriptId), m_scripts.end())((void) 0);
1769 const auto& script = m_scripts[scriptId];
1770
1771 breakAuxData = protocol::DictionaryValue::create();
1772 breakAuxData->setString("scriptId", script->scriptId());
1773 breakAuxData->setString("url", script->sourceURL());
1774 if (!script->sourceMappingURL().isEmpty()) {
1775 breakAuxData->setString("sourceMapURL", (script->sourceMappingURL()));
1776 }
1777 }
1778 }
1779
1780 m_frontend.paused(std::move(protocolCallFrames), breakReason,
1781 std::move(breakAuxData),
1782 std::make_unique<Array<String16>>(),
1783 currentAsyncStackTrace(), currentExternalStackTrace());
1784}
1785
1786void V8DebuggerAgentImpl::didPause(
1787 int contextId, v8::Local<v8::Value> exception,
1788 const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
1789 v8::debug::ExceptionType exceptionType, bool isUncaught,
1790 v8::debug::BreakReasons breakReasons) {
1791 v8::HandleScope handles(m_isolate);
1792
1793 std::vector<BreakReason> hitReasons;
1794
1795 if (breakReasons.contains(v8::debug::BreakReason::kOOM)) {
9
Taking false branch
1796 hitReasons.push_back(
1797 std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
1798 } else if (breakReasons.contains(v8::debug::BreakReason::kAssert)) {
10
Taking false branch
1799 hitReasons.push_back(std::make_pair(
1800 protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
1801 } else if (breakReasons.contains(v8::debug::BreakReason::kException)) {
11
Taking false branch
1802 InjectedScript* injectedScript = nullptr;
1803 m_session->findInjectedScript(contextId, injectedScript);
1804 if (injectedScript) {
1805 String16 breakReason =
1806 exceptionType == v8::debug::kPromiseRejection
1807 ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1808 : protocol::Debugger::Paused::ReasonEnum::Exception;
1809 std::unique_ptr<protocol::Runtime::RemoteObject> obj;
1810 injectedScript->wrapObject(exception, kBacktraceObjectGroup,
1811 WrapMode::kNoPreview, &obj);
1812 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1813 if (obj) {
1814 std::vector<uint8_t> serialized;
1815 obj->AppendSerialized(&serialized);
1816 breakAuxData = protocol::DictionaryValue::cast(
1817 protocol::Value::parseBinary(serialized.data(), serialized.size()));
1818 breakAuxData->setBoolean("uncaught", isUncaught);
1819 }
1820 hitReasons.push_back(
1821 std::make_pair(breakReason, std::move(breakAuxData)));
1822 }
1823 }
1824
1825 auto hitBreakpointIds = std::make_unique<Array<String16>>();
1826 bool hitRegularBreakpoint = false;
1827 for (const auto& id : hitBreakpoints) {
1828 auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
1829 if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
12
Taking false branch
1830 continue;
1831 }
1832 const String16& breakpointId = breakpointIterator->second;
1833 hitBreakpointIds->emplace_back(breakpointId);
1834 BreakpointType type;
13
'type' declared without an initial value
1835 parseBreakpointId(breakpointId, &type);
14
Calling 'parseBreakpointId'
18
Returning from 'parseBreakpointId'
1836 if (type == BreakpointType::kDebugCommand) {
19
The left operand of '==' is a garbage value
1837 hitReasons.push_back(std::make_pair(
1838 protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
1839 } else {
1840 hitRegularBreakpoint = true;
1841 }
1842 }
1843
1844 for (size_t i = 0; i < m_breakReason.size(); ++i) {
1845 hitReasons.push_back(std::move(m_breakReason[i]));
1846 }
1847 clearBreakDetails();
1848
1849 // Make sure that we only include (other: nullptr) once.
1850 const BreakReason otherHitReason =
1851 std::make_pair(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
1852 const bool otherBreakReasons =
1853 hitRegularBreakpoint || hitBreakReasonEncodedAsOther(breakReasons);
1854 if (otherBreakReasons && std::find(hitReasons.begin(), hitReasons.end(),
1855 otherHitReason) == hitReasons.end()) {
1856 hitReasons.push_back(
1857 std::make_pair(protocol::Debugger::Paused::ReasonEnum::Other, nullptr));
1858 }
1859
1860 // We should always know why we pause: either the pause relates to this agent
1861 // (`hitReason` is non empty), or it relates to another agent (hit a
1862 // breakpoint there, or a triggered pause was scheduled by other agent).
1863 DCHECK(hitReasons.size() > 0 || !hitBreakpoints.empty() ||((void) 0)
1864 breakReasons.contains(v8::debug::BreakReason::kAgent))((void) 0);
1865 String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1866 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1867 if (hitReasons.size() == 1) {
1868 breakReason = hitReasons[0].first;
1869 breakAuxData = std::move(hitReasons[0].second);
1870 } else if (hitReasons.size() > 1) {
1871 breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
1872 std::unique_ptr<protocol::ListValue> reasons =
1873 protocol::ListValue::create();
1874 for (size_t i = 0; i < hitReasons.size(); ++i) {
1875 std::unique_ptr<protocol::DictionaryValue> reason =
1876 protocol::DictionaryValue::create();
1877 reason->setString("reason", hitReasons[i].first);
1878 if (hitReasons[i].second)
1879 reason->setObject("auxData", std::move(hitReasons[i].second));
1880 reasons->pushValue(std::move(reason));
1881 }
1882 breakAuxData = protocol::DictionaryValue::create();
1883 breakAuxData->setArray("reasons", std::move(reasons));
1884 }
1885
1886 std::unique_ptr<Array<CallFrame>> protocolCallFrames;
1887 Response response = currentCallFrames(&protocolCallFrames);
1888 if (!response.IsSuccess())
1889 protocolCallFrames = std::make_unique<Array<CallFrame>>();
1890
1891 m_frontend.paused(std::move(protocolCallFrames), breakReason,
1892 std::move(breakAuxData), std::move(hitBreakpointIds),
1893 currentAsyncStackTrace(), currentExternalStackTrace());
1894}
1895
1896void V8DebuggerAgentImpl::didContinue() {
1897 m_frontend.resumed();
1898 m_frontend.flush();
1899}
1900
1901void V8DebuggerAgentImpl::breakProgram(
1902 const String16& breakReason,
1903 std::unique_ptr<protocol::DictionaryValue> data) {
1904 if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return;
1905 std::vector<BreakReason> currentScheduledReason;
1906 currentScheduledReason.swap(m_breakReason);
1907 pushBreakDetails(breakReason, std::move(data));
1908
1909 int contextGroupId = m_session->contextGroupId();
1910 int sessionId = m_session->sessionId();
1911 V8InspectorImpl* inspector = m_inspector;
1912 m_debugger->breakProgram(contextGroupId);
1913 // Check that session and |this| are still around.
1914 if (!inspector->sessionById(contextGroupId, sessionId)) return;
1915 if (!enabled()) return;
1916
1917 popBreakDetails();
1918 m_breakReason.swap(currentScheduledReason);
1919 if (!m_breakReason.empty()) {
1920 m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1921 }
1922}
1923
1924void V8DebuggerAgentImpl::setBreakpointFor(v8::Local<v8::Function> function,
1925 v8::Local<v8::String> condition,
1926 BreakpointSource source) {
1927 String16 breakpointId = generateBreakpointId(
1928 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1929 : BreakpointType::kMonitorCommand,
1930 function);
1931 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
1932 m_breakpointIdToDebuggerBreakpointIds.end()) {
1933 return;
1934 }
1935 setBreakpointImpl(breakpointId, function, condition);
1936}
1937
1938void V8DebuggerAgentImpl::removeBreakpointFor(v8::Local<v8::Function> function,
1939 BreakpointSource source) {
1940 String16 breakpointId = generateBreakpointId(
1941 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1942 : BreakpointType::kMonitorCommand,
1943 function);
1944 std::vector<V8DebuggerScript*> scripts;
1945 removeBreakpointImpl(breakpointId, scripts);
1946}
1947
1948void V8DebuggerAgentImpl::reset() {
1949 if (!enabled()) return;
1950 m_blackboxedPositions.clear();
1951 resetBlackboxedStateCache();
1952 m_skipList.clear();
1953 m_scripts.clear();
1954 m_cachedScripts.clear();
1955 m_cachedScriptSize = 0;
1956}
1957
1958void V8DebuggerAgentImpl::ScriptCollected(const V8DebuggerScript* script) {
1959 DCHECK_NE(m_scripts.find(script->scriptId()), m_scripts.end())((void) 0);
1960 std::vector<uint8_t> bytecode;
1961#if V8_ENABLE_WEBASSEMBLY1
1962 v8::MemorySpan<const uint8_t> span;
1963 if (script->wasmBytecode().To(&span)) {
1964 bytecode.reserve(span.size());
1965 bytecode.insert(bytecode.begin(), span.data(), span.data() + span.size());
1966 }
1967#endif
1968 CachedScript cachedScript{script->scriptId(), script->source(0),
1969 std::move(bytecode)};
1970 m_cachedScriptSize += cachedScript.size();
1971 m_cachedScripts.push_back(std::move(cachedScript));
1972 m_scripts.erase(script->scriptId());
1973
1974 while (m_cachedScriptSize > m_maxScriptCacheSize) {
1975 const CachedScript& cachedScript = m_cachedScripts.front();
1976 DCHECK_GE(m_cachedScriptSize, cachedScript.size())((void) 0);
1977 m_cachedScriptSize -= cachedScript.size();
1978 m_cachedScripts.pop_front();
1979 }
1980}
1981
1982Response V8DebuggerAgentImpl::processSkipList(
1983 protocol::Array<protocol::Debugger::LocationRange>* skipList) {
1984 std::unordered_map<String16, std::vector<std::pair<int, int>>> skipListInit;
1985 for (std::unique_ptr<protocol::Debugger::LocationRange>& range : *skipList) {
1986 protocol::Debugger::ScriptPosition* start = range->getStart();
1987 protocol::Debugger::ScriptPosition* end = range->getEnd();
1988 String16 scriptId = range->getScriptId();
1989
1990 auto it = m_scripts.find(scriptId);
1991 if (it == m_scripts.end())
1992 return Response::ServerError("No script with passed id.");
1993
1994 Response res = isValidPosition(start);
1995 if (res.IsError()) return res;
1996
1997 res = isValidPosition(end);
1998 if (res.IsError()) return res;
1999
2000 skipListInit[scriptId].emplace_back(start->getLineNumber(),
2001 start->getColumnNumber());
2002 skipListInit[scriptId].emplace_back(end->getLineNumber(),
2003 end->getColumnNumber());
2004 }
2005
2006 // Verify that the skipList is sorted, and that all ranges
2007 // are properly defined (start comes before end).
2008 for (auto skipListPair : skipListInit) {
2009 Response res = isValidRangeOfPositions(skipListPair.second);
2010 if (res.IsError()) return res;
2011 }
2012
2013 m_skipList = std::move(skipListInit);
2014 return Response::Success();
2015}
2016} // namespace v8_inspector