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 | |
2 | |
3 | |
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 | |
32 | namespace v8_inspector { |
33 | |
34 | using protocol::Array; |
35 | using protocol::Maybe; |
36 | using protocol::Debugger::BreakpointId; |
37 | using protocol::Debugger::CallFrame; |
38 | using protocol::Debugger::Scope; |
39 | using protocol::Runtime::ExceptionDetails; |
40 | using protocol::Runtime::RemoteObject; |
41 | using protocol::Runtime::ScriptId; |
42 | |
43 | namespace InstrumentationEnum = |
44 | protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum; |
45 | |
46 | namespace DebuggerAgentState { |
47 | static const char pauseOnExceptionsState[] = "pauseOnExceptionsState"; |
48 | static const char asyncCallStackDepth[] = "asyncCallStackDepth"; |
49 | static const char blackboxPattern[] = "blackboxPattern"; |
50 | static const char debuggerEnabled[] = "debuggerEnabled"; |
51 | static const char skipAllPauses[] = "skipAllPauses"; |
52 | |
53 | static const char breakpointsByRegex[] = "breakpointsByRegex"; |
54 | static const char breakpointsByUrl[] = "breakpointsByUrl"; |
55 | static const char breakpointsByScriptHash[] = "breakpointsByScriptHash"; |
56 | static const char breakpointHints[] = "breakpointHints"; |
57 | static const char instrumentationBreakpoints[] = "instrumentationBreakpoints"; |
58 | |
59 | } |
60 | |
61 | static const char kBacktraceObjectGroup[] = "backtrace"; |
62 | static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled"; |
63 | static const char kDebuggerNotPaused[] = |
64 | "Can only perform operation while paused."; |
65 | |
66 | static const size_t kBreakpointHintMaxLength = 128; |
67 | static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10; |
68 | |
69 | |
70 | static const size_t kMaxNumBreakpoints = 1000; |
71 | |
72 | #if V8_ENABLE_WEBASSEMBLY |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | static constexpr size_t kWasmBytecodeMaxLength = |
81 | (v8::String::kMaxLength / 4) * 3; |
82 | static constexpr const char kWasmBytecodeExceedsTransferLimit[] = |
83 | "WebAssembly bytecode exceeds the transfer limit"; |
84 | #endif // V8_ENABLE_WEBASSEMBLY |
85 | |
86 | namespace { |
87 | |
88 | enum class BreakpointType { |
89 | kByUrl = 1, |
90 | kByUrlRegex, |
91 | kByScriptHash, |
92 | kByScriptId, |
93 | kDebugCommand, |
94 | kMonitorCommand, |
95 | kBreakpointAtEntry, |
96 | kInstrumentationBreakpoint |
97 | }; |
98 | |
99 | String16 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 | |
113 | String16 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 | |
122 | String16 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 | |
131 | bool 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' | |
|
| |
| 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 | |
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 | |
175 | bool 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 | |
181 | String16 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 | |
195 | void 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 | |
239 | String16 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 | |
253 | String16 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(); |
277 | } |
278 | |
279 | Response 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 | |
324 | protocol::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 | |
335 | Response 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 | |
343 | Response 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 | |
355 | bool hitBreakReasonEncodedAsOther(v8::debug::BreakReasons breakReasons) { |
356 | |
357 | |
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 | } |
366 | |
367 | V8DebuggerAgentImpl::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 | |
378 | V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default; |
379 | |
380 | void 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 | |
|
| |
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 | |
401 | Response 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 | |
|
| |
408 | |
409 | if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId())) |
| 3 | | Assuming the condition is false | |
|
| |
410 | return Response::ServerError("Script execution is prohibited"); |
411 | |
412 | enableImpl(); |
| 5 | | Calling 'V8DebuggerAgentImpl::enableImpl' | |
|
413 | return Response::Success(); |
414 | } |
415 | |
416 | Response 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 | |
456 | void V8DebuggerAgentImpl::restore() { |
457 | DCHECK(!m_enabled); |
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 | |
484 | Response 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 | |
496 | Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) { |
497 | m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip); |
498 | m_skipAllPauses = skip; |
499 | return Response::Success(); |
500 | } |
501 | |
502 | static 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 | |
521 | Response 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(); |
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 | |
611 | Response 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 | |
635 | Response 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 | |
662 | Response 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 | |
677 | Response 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 | |
716 | |
717 | |
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 | |
735 | void V8DebuggerAgentImpl::removeBreakpointImpl( |
736 | const String16& breakpointId, |
737 | const std::vector<V8DebuggerScript*>& scripts) { |
738 | DCHECK(enabled()); |
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_WEBASSEMBLY |
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 | |
758 | Response 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 | |
805 | |
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 | |
823 | Response 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 | |
845 | Response 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 | |
875 | bool 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 | |
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 | |
903 | |
904 | |
905 | return itStartRange == itEndRange && |
906 | std::distance(ranges.begin(), itStartRange) % 2; |
907 | } |
908 | |
909 | bool 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()); |
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 | |
925 | |
926 | |
927 | |
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 | |
936 | bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const { |
937 | return enabled() && (isOOMBreak || !m_skipAllPauses); |
938 | } |
939 | |
940 | std::unique_ptr<protocol::Debugger::Location> |
941 | V8DebuggerAgentImpl::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()); |
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 | |
986 | void 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 | |
999 | Response 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 | |
1015 | Response 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 | |
1062 | Response 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 | |
1070 | Response 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_WEBASSEMBLY |
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 | |
1102 | Response V8DebuggerAgentImpl::getWasmBytecode(const String16& scriptId, |
1103 | protocol::Binary* bytecode) { |
1104 | #if V8_ENABLE_WEBASSEMBLY |
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 | |
1123 | void 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 | |
1129 | void V8DebuggerAgentImpl::popBreakDetails() { |
1130 | if (m_breakReason.empty()) return; |
1131 | m_breakReason.pop_back(); |
1132 | } |
1133 | |
1134 | void V8DebuggerAgentImpl::clearBreakDetails() { |
1135 | std::vector<BreakReason> emptyBreakReason; |
1136 | m_breakReason.swap(emptyBreakReason); |
1137 | } |
1138 | |
1139 | void 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 | |
1149 | void 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 | |
1157 | Response 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 | |
1171 | Response 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 | |
1179 | Response 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 | |
1195 | Response 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 | |
1213 | Response 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 | |
1220 | Response V8DebuggerAgentImpl::pauseOnAsyncCall( |
1221 | std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) { |
1222 | |
1223 | return Response::Success(); |
1224 | } |
1225 | |
1226 | Response 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 | |
1244 | void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) { |
1245 | |
1246 | |
1247 | m_debugger->setPauseOnExceptionsState( |
1248 | static_cast<v8::debug::ExceptionBreakState>(pauseState)); |
1249 | m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState); |
1250 | } |
1251 | |
1252 | Response 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 | |
1283 | |
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 | |
1294 | Response 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 | |
1330 | Response 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 | |
1354 | Response 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 | |
1363 | Response 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 | |
1388 | Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) { |
1389 | std::unique_ptr<V8Regex> regex(new V8Regex( |
1390 | m_inspector, pattern, true , false )); |
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 | |
1398 | void V8DebuggerAgentImpl::resetBlackboxedStateCache() { |
1399 | for (const auto& it : m_scripts) { |
1400 | it.second->resetBlackboxedStateCache(); |
1401 | } |
1402 | } |
1403 | |
1404 | Response 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 | |
1436 | Response 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()); |
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 | |
1519 | std::unique_ptr<protocol::Runtime::StackTrace> |
1520 | V8DebuggerAgentImpl::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 | |
1528 | std::unique_ptr<protocol::Runtime::StackTraceId> |
1529 | V8DebuggerAgentImpl::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 | |
1539 | bool V8DebuggerAgentImpl::isPaused() const { |
1540 | return m_debugger->isPausedInContextGroup(m_session->contextGroupId()); |
1541 | } |
1542 | |
1543 | static 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_WEBASSEMBLY |
1553 | static 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 | |
1570 | static 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 | |
1587 | void 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 | |
1603 | |
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_WEBASSEMBLY |
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 | |
1628 | |
1629 | m_scripts[scriptId]->MakeWeak(); |
1630 | |
1631 | ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId); |
1632 | DCHECK(scriptIterator != m_scripts.end()); |
1633 | V8DebuggerScript* scriptRef = scriptIterator->second.get(); |
1634 | |
1635 | |
1636 | |
1637 | |
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 | |
1724 | void 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 | |
1751 | void 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); |
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()); |
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 | |
1786 | void 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)) { |
| |
1796 | hitReasons.push_back( |
1797 | std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr)); |
1798 | } else if (breakReasons.contains(v8::debug::BreakReason::kAssert)) { |
| |
1799 | hitReasons.push_back(std::make_pair( |
1800 | protocol::Debugger::Paused::ReasonEnum::Assert, nullptr)); |
1801 | } else if (breakReasons.contains(v8::debug::BreakReason::kException)) { |
| |
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()) { |
| |
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 | |
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 | |
1861 | |
1862 | |
1863 | DCHECK(hitReasons.size() > 0 || !hitBreakpoints.empty() || |
1864 | breakReasons.contains(v8::debug::BreakReason::kAgent)); |
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 | |
1896 | void V8DebuggerAgentImpl::didContinue() { |
1897 | m_frontend.resumed(); |
1898 | m_frontend.flush(); |
1899 | } |
1900 | |
1901 | void 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 | |
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 | |
1924 | void 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 | |
1938 | void 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 | |
1948 | void 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 | |
1958 | void V8DebuggerAgentImpl::ScriptCollected(const V8DebuggerScript* script) { |
1959 | DCHECK_NE(m_scripts.find(script->scriptId()), m_scripts.end()); |
1960 | std::vector<uint8_t> bytecode; |
1961 | #if V8_ENABLE_WEBASSEMBLY |
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()); |
1977 | m_cachedScriptSize -= cachedScript.size(); |
1978 | m_cachedScripts.pop_front(); |
1979 | } |
1980 | } |
1981 | |
1982 | Response 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 | |
2007 | |
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 | } |