Line data Source code
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 "src/debug/debug-interface.h"
10 : #include "src/inspector/injected-script.h"
11 : #include "src/inspector/inspected-context.h"
12 : #include "src/inspector/protocol/Protocol.h"
13 : #include "src/inspector/remote-object-id.h"
14 : #include "src/inspector/search-util.h"
15 : #include "src/inspector/string-util.h"
16 : #include "src/inspector/v8-debugger-script.h"
17 : #include "src/inspector/v8-debugger.h"
18 : #include "src/inspector/v8-inspector-impl.h"
19 : #include "src/inspector/v8-inspector-session-impl.h"
20 : #include "src/inspector/v8-regex.h"
21 : #include "src/inspector/v8-runtime-agent-impl.h"
22 : #include "src/inspector/v8-stack-trace-impl.h"
23 : #include "src/inspector/v8-value-utils.h"
24 :
25 : #include "include/v8-inspector.h"
26 :
27 : namespace v8_inspector {
28 :
29 : using protocol::Array;
30 : using protocol::Maybe;
31 : using protocol::Debugger::BreakpointId;
32 : using protocol::Debugger::CallFrame;
33 : using protocol::Runtime::ExceptionDetails;
34 : using protocol::Runtime::ScriptId;
35 : using protocol::Runtime::RemoteObject;
36 : using protocol::Debugger::Scope;
37 :
38 : namespace DebuggerAgentState {
39 : static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
40 : static const char asyncCallStackDepth[] = "asyncCallStackDepth";
41 : static const char blackboxPattern[] = "blackboxPattern";
42 : static const char debuggerEnabled[] = "debuggerEnabled";
43 : static const char skipAllPauses[] = "skipAllPauses";
44 :
45 : static const char breakpointsByRegex[] = "breakpointsByRegex";
46 : static const char breakpointsByUrl[] = "breakpointsByUrl";
47 : static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
48 : static const char breakpointHints[] = "breakpointHints";
49 :
50 : } // namespace DebuggerAgentState
51 :
52 : static const char kBacktraceObjectGroup[] = "backtrace";
53 : static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
54 : static const char kDebuggerNotPaused[] =
55 : "Can only perform operation while paused.";
56 :
57 : static const size_t kBreakpointHintMaxLength = 128;
58 : static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;
59 :
60 : static const int kMaxScriptFailedToParseScripts = 1000;
61 :
62 : namespace {
63 :
64 293808 : void TranslateLocation(protocol::Debugger::Location* location,
65 : WasmTranslation* wasmTranslation) {
66 : String16 scriptId = location->getScriptId();
67 146904 : int lineNumber = location->getLineNumber();
68 146904 : int columnNumber = location->getColumnNumber(-1);
69 146904 : if (wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
70 : &scriptId, &lineNumber, &columnNumber)) {
71 : location->setScriptId(std::move(scriptId));
72 463 : location->setLineNumber(lineNumber);
73 463 : location->setColumnNumber(columnNumber);
74 : }
75 146904 : }
76 :
77 : enum class BreakpointType {
78 : kByUrl = 1,
79 : kByUrlRegex,
80 : kByScriptHash,
81 : kByScriptId,
82 : kDebugCommand,
83 : kMonitorCommand,
84 : kBreakpointAtEntry
85 : };
86 :
87 2179 : String16 generateBreakpointId(BreakpointType type,
88 : const String16& scriptSelector, int lineNumber,
89 : int columnNumber) {
90 2179 : String16Builder builder;
91 2179 : builder.appendNumber(static_cast<int>(type));
92 2179 : builder.append(':');
93 2179 : builder.appendNumber(lineNumber);
94 2179 : builder.append(':');
95 2179 : builder.appendNumber(columnNumber);
96 2179 : builder.append(':');
97 2179 : builder.append(scriptSelector);
98 4358 : return builder.toString();
99 : }
100 :
101 170 : String16 generateBreakpointId(BreakpointType type,
102 : v8::Local<v8::Function> function) {
103 170 : String16Builder builder;
104 170 : builder.appendNumber(static_cast<int>(type));
105 170 : builder.append(':');
106 170 : builder.appendNumber(v8::debug::GetDebuggingId(function));
107 340 : return builder.toString();
108 : }
109 :
110 3404 : bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
111 : String16* scriptSelector = nullptr,
112 : int* lineNumber = nullptr, int* columnNumber = nullptr) {
113 : size_t typeLineSeparator = breakpointId.find(':');
114 3404 : if (typeLineSeparator == String16::kNotFound) return false;
115 :
116 6798 : int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
117 3399 : if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
118 : rawType > static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
119 : return false;
120 : }
121 3394 : if (type) *type = static_cast<BreakpointType>(rawType);
122 3394 : if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
123 3394 : rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
124 : rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry)) {
125 : // The script and source position is not encoded in this case.
126 : return true;
127 : }
128 :
129 3314 : size_t lineColumnSeparator = breakpointId.find(':', typeLineSeparator + 1);
130 3314 : if (lineColumnSeparator == String16::kNotFound) return false;
131 : size_t columnSelectorSeparator =
132 3314 : breakpointId.find(':', lineColumnSeparator + 1);
133 3314 : if (columnSelectorSeparator == String16::kNotFound) return false;
134 3314 : if (scriptSelector) {
135 1728 : *scriptSelector = breakpointId.substring(columnSelectorSeparator + 1);
136 : }
137 3314 : if (lineNumber) {
138 : *lineNumber = breakpointId
139 : .substring(typeLineSeparator + 1,
140 110 : lineColumnSeparator - typeLineSeparator - 1)
141 220 : .toInteger();
142 : }
143 3314 : if (columnNumber) {
144 : *columnNumber =
145 : breakpointId
146 : .substring(lineColumnSeparator + 1,
147 110 : columnSelectorSeparator - lineColumnSeparator - 1)
148 220 : .toInteger();
149 : }
150 : return true;
151 : }
152 :
153 225 : bool positionComparator(const std::pair<int, int>& a,
154 : const std::pair<int, int>& b) {
155 225 : if (a.first != b.first) return a.first < b.first;
156 45 : return a.second < b.second;
157 : }
158 :
159 215 : String16 breakpointHint(const V8DebuggerScript& script, int lineNumber,
160 : int columnNumber) {
161 215 : int offset = script.offset(lineNumber, columnNumber);
162 215 : if (offset == V8DebuggerScript::kNoOffset) return String16();
163 : String16 hint =
164 430 : script.source(offset, kBreakpointHintMaxLength).stripWhiteSpace();
165 5300 : for (size_t i = 0; i < hint.length(); ++i) {
166 2625 : if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') {
167 190 : return hint.substring(0, i);
168 : }
169 : }
170 25 : return hint;
171 : }
172 :
173 160 : void adjustBreakpointLocation(const V8DebuggerScript& script,
174 : const String16& hint, int* lineNumber,
175 : int* columnNumber) {
176 160 : if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
177 100 : return;
178 95 : if (hint.isEmpty()) return;
179 95 : intptr_t sourceOffset = script.offset(*lineNumber, *columnNumber);
180 95 : if (sourceOffset == V8DebuggerScript::kNoOffset) return;
181 :
182 : intptr_t searchRegionOffset = std::max(
183 190 : sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
184 95 : size_t offset = sourceOffset - searchRegionOffset;
185 : String16 searchArea = script.source(searchRegionOffset,
186 95 : offset + kBreakpointHintMaxSearchOffset);
187 :
188 : size_t nextMatch = searchArea.find(hint, offset);
189 : size_t prevMatch = searchArea.reverseFind(hint, offset);
190 95 : if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
191 : return;
192 : }
193 : size_t bestMatch;
194 60 : if (nextMatch == String16::kNotFound) {
195 : bestMatch = prevMatch;
196 60 : } else if (prevMatch == String16::kNotFound) {
197 : bestMatch = nextMatch;
198 : } else {
199 45 : bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
200 : }
201 60 : bestMatch += searchRegionOffset;
202 : v8::debug::Location hintPosition =
203 60 : script.location(static_cast<int>(bestMatch));
204 60 : if (hintPosition.IsEmpty()) return;
205 60 : *lineNumber = hintPosition.GetLineNumber();
206 60 : *columnNumber = hintPosition.GetColumnNumber();
207 : }
208 :
209 2480 : String16 breakLocationType(v8::debug::BreakLocationType type) {
210 2480 : switch (type) {
211 : case v8::debug::kCallBreakLocation:
212 1135 : return protocol::Debugger::BreakLocation::TypeEnum::Call;
213 : case v8::debug::kReturnBreakLocation:
214 1335 : return protocol::Debugger::BreakLocation::TypeEnum::Return;
215 : case v8::debug::kDebuggerStatementBreakLocation:
216 10 : return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
217 : case v8::debug::kCommonBreakLocation:
218 0 : return String16();
219 : }
220 0 : return String16();
221 : }
222 :
223 : } // namespace
224 :
225 371828 : String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
226 371828 : switch (type) {
227 : case v8::debug::ScopeIterator::ScopeTypeGlobal:
228 142575 : return Scope::TypeEnum::Global;
229 : case v8::debug::ScopeIterator::ScopeTypeLocal:
230 83802 : return Scope::TypeEnum::Local;
231 : case v8::debug::ScopeIterator::ScopeTypeWith:
232 2355 : return Scope::TypeEnum::With;
233 : case v8::debug::ScopeIterator::ScopeTypeClosure:
234 9490 : return Scope::TypeEnum::Closure;
235 : case v8::debug::ScopeIterator::ScopeTypeCatch:
236 1063 : return Scope::TypeEnum::Catch;
237 : case v8::debug::ScopeIterator::ScopeTypeBlock:
238 2658 : return Scope::TypeEnum::Block;
239 : case v8::debug::ScopeIterator::ScopeTypeScript:
240 128578 : return Scope::TypeEnum::Script;
241 : case v8::debug::ScopeIterator::ScopeTypeEval:
242 97 : return Scope::TypeEnum::Eval;
243 : case v8::debug::ScopeIterator::ScopeTypeModule:
244 1210 : return Scope::TypeEnum::Module;
245 : }
246 0 : UNREACHABLE();
247 : return String16();
248 : }
249 :
250 : namespace {
251 :
252 146904 : Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
253 : InjectedScript* injectedScript,
254 : std::unique_ptr<Array<Scope>>* scopes) {
255 : *scopes = Array<Scope>::create();
256 146904 : if (!injectedScript) return Response::OK();
257 146795 : if (iterator->Done()) return Response::OK();
258 :
259 142575 : String16 scriptId = String16::fromInteger(iterator->GetScriptId());
260 :
261 514403 : for (; !iterator->Done(); iterator->Advance()) {
262 371828 : std::unique_ptr<RemoteObject> object;
263 : Response result =
264 371828 : injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
265 1487312 : WrapMode::kNoPreview, &object);
266 371828 : if (!result.isSuccess()) return result;
267 :
268 : auto scope = Scope::create()
269 1115484 : .setType(scopeType(iterator->GetType()))
270 : .setObject(std::move(object))
271 : .build();
272 :
273 : String16 name = toProtocolStringWithTypeCheck(
274 371828 : isolate, iterator->GetFunctionDebugName());
275 371828 : if (!name.isEmpty()) scope->setName(name);
276 :
277 371828 : if (iterator->HasLocationInfo()) {
278 154428 : v8::debug::Location start = iterator->GetStartLocation();
279 : scope->setStartLocation(protocol::Debugger::Location::create()
280 154428 : .setScriptId(scriptId)
281 154428 : .setLineNumber(start.GetLineNumber())
282 154428 : .setColumnNumber(start.GetColumnNumber())
283 : .build());
284 :
285 154428 : v8::debug::Location end = iterator->GetEndLocation();
286 : scope->setEndLocation(protocol::Debugger::Location::create()
287 154428 : .setScriptId(scriptId)
288 154428 : .setLineNumber(end.GetLineNumber())
289 154428 : .setColumnNumber(end.GetColumnNumber())
290 : .build());
291 : }
292 371828 : (*scopes)->addItem(std::move(scope));
293 : }
294 142575 : return Response::OK();
295 : }
296 :
297 650 : protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
298 : const String16& key) {
299 650 : protocol::DictionaryValue* value = object->getObject(key);
300 650 : if (value) return value;
301 : std::unique_ptr<protocol::DictionaryValue> newDictionary =
302 495 : protocol::DictionaryValue::create();
303 : value = newDictionary.get();
304 990 : object->setObject(key, std::move(newDictionary));
305 : return value;
306 : }
307 : } // namespace
308 :
309 3834 : V8DebuggerAgentImpl::V8DebuggerAgentImpl(
310 3834 : V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
311 : protocol::DictionaryValue* state)
312 : : m_inspector(session->inspector()),
313 3834 : m_debugger(m_inspector->debugger()),
314 : m_session(session),
315 : m_enabled(false),
316 : m_state(state),
317 : m_frontend(frontendChannel),
318 34506 : m_isolate(m_inspector->isolate()) {}
319 :
320 : V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default;
321 :
322 3466 : void V8DebuggerAgentImpl::enableImpl() {
323 3466 : m_enabled = true;
324 6932 : m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
325 3466 : m_debugger->enable();
326 :
327 : std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts;
328 3466 : m_debugger->getCompiledScripts(m_session->contextGroupId(), compiledScripts);
329 73660 : for (size_t i = 0; i < compiledScripts.size(); i++)
330 66728 : didParseSource(std::move(compiledScripts[i]), true);
331 :
332 3466 : m_breakpointsActive = true;
333 3466 : m_debugger->setBreakpointsActive(true);
334 :
335 3466 : if (isPaused()) {
336 : didPause(0, v8::Local<v8::Value>(), std::vector<v8::debug::BreakpointId>(),
337 20 : v8::debug::kException, false, false, false);
338 3466 : }
339 3466 : }
340 :
341 6950 : Response V8DebuggerAgentImpl::enable(String16* outDebuggerId) {
342 6950 : *outDebuggerId = debuggerIdToString(
343 10401 : m_debugger->debuggerIdFor(m_session->contextGroupId()));
344 3475 : if (enabled()) return Response::OK();
345 :
346 6902 : if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
347 0 : return Response::Error("Script execution is prohibited");
348 :
349 3451 : enableImpl();
350 3451 : return Response::OK();
351 : }
352 :
353 5379 : Response V8DebuggerAgentImpl::disable() {
354 5379 : if (!enabled()) return Response::OK();
355 :
356 6932 : m_state->remove(DebuggerAgentState::breakpointsByRegex);
357 6932 : m_state->remove(DebuggerAgentState::breakpointsByUrl);
358 6932 : m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
359 6932 : m_state->remove(DebuggerAgentState::breakpointHints);
360 :
361 : m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
362 6932 : v8::debug::NoBreakOnException);
363 6932 : m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
364 :
365 3466 : if (m_breakpointsActive) {
366 3466 : m_debugger->setBreakpointsActive(false);
367 3466 : m_breakpointsActive = false;
368 : }
369 : m_blackboxedPositions.clear();
370 : m_blackboxPattern.reset();
371 : resetBlackboxedStateCache();
372 : m_scripts.clear();
373 8263 : for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
374 1331 : v8::debug::RemoveBreakpoint(m_isolate, it.first);
375 : }
376 : m_breakpointIdToDebuggerBreakpointIds.clear();
377 : m_debuggerBreakpointIdToBreakpointId.clear();
378 3466 : m_debugger->setAsyncCallStackDepth(this, 0);
379 3466 : clearBreakDetails();
380 3466 : m_skipAllPauses = false;
381 6932 : m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
382 6932 : m_state->remove(DebuggerAgentState::blackboxPattern);
383 3466 : m_enabled = false;
384 6932 : m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
385 3466 : m_debugger->disable();
386 3466 : return Response::OK();
387 : }
388 :
389 55 : void V8DebuggerAgentImpl::restore() {
390 : DCHECK(!m_enabled);
391 110 : if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
392 40 : return;
393 15 : if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
394 : return;
395 :
396 15 : enableImpl();
397 :
398 15 : int pauseState = v8::debug::NoBreakOnException;
399 30 : m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
400 15 : setPauseOnExceptionsImpl(pauseState);
401 :
402 : m_skipAllPauses =
403 30 : m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);
404 :
405 15 : int asyncCallStackDepth = 0;
406 : m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
407 30 : &asyncCallStackDepth);
408 15 : m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);
409 :
410 15 : String16 blackboxPattern;
411 30 : if (m_state->getString(DebuggerAgentState::blackboxPattern,
412 30 : &blackboxPattern)) {
413 0 : setBlackboxPattern(blackboxPattern);
414 : }
415 : }
416 :
417 138 : Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
418 138 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
419 138 : if (m_breakpointsActive == active) return Response::OK();
420 102 : m_breakpointsActive = active;
421 102 : m_debugger->setBreakpointsActive(active);
422 153 : if (!active && !m_breakReason.empty()) {
423 5 : clearBreakDetails();
424 5 : m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
425 : }
426 102 : return Response::OK();
427 : }
428 :
429 35 : Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
430 70 : m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
431 35 : m_skipAllPauses = skip;
432 35 : return Response::OK();
433 : }
434 :
435 1355 : static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
436 : BreakpointType type, const String16& selector) {
437 1355 : switch (type) {
438 : case BreakpointType::kByUrl:
439 1295 : return script.sourceURL() == selector;
440 : case BreakpointType::kByScriptHash:
441 80 : return script.hash() == selector;
442 : case BreakpointType::kByUrlRegex: {
443 20 : V8Regex regex(inspector, selector, true);
444 20 : return regex.match(script.sourceURL()) != -1;
445 : }
446 : default:
447 0 : UNREACHABLE();
448 : return false;
449 : }
450 : }
451 :
452 230 : Response V8DebuggerAgentImpl::setBreakpointByUrl(
453 : int lineNumber, Maybe<String16> optionalURL,
454 : Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash,
455 : Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition,
456 : String16* outBreakpointId,
457 : std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
458 : *locations = Array<protocol::Debugger::Location>::create();
459 :
460 685 : int specified = (optionalURL.isJust() ? 1 : 0) +
461 690 : (optionalURLRegex.isJust() ? 1 : 0) +
462 470 : (optionalScriptHash.isJust() ? 1 : 0);
463 230 : if (specified != 1) {
464 : return Response::Error(
465 0 : "Either url or urlRegex or scriptHash must be specified.");
466 : }
467 230 : int columnNumber = 0;
468 230 : if (optionalColumnNumber.isJust()) {
469 85 : columnNumber = optionalColumnNumber.fromJust();
470 85 : if (columnNumber < 0) return Response::Error("Incorrect column number");
471 : }
472 :
473 : BreakpointType type = BreakpointType::kByUrl;
474 230 : String16 selector;
475 230 : if (optionalURLRegex.isJust()) {
476 10 : selector = optionalURLRegex.fromJust();
477 : type = BreakpointType::kByUrlRegex;
478 225 : } else if (optionalURL.isJust()) {
479 430 : selector = optionalURL.fromJust();
480 : type = BreakpointType::kByUrl;
481 10 : } else if (optionalScriptHash.isJust()) {
482 20 : selector = optionalScriptHash.fromJust();
483 : type = BreakpointType::kByScriptHash;
484 : }
485 :
486 460 : String16 condition = optionalCondition.fromMaybe(String16());
487 : String16 breakpointId =
488 230 : generateBreakpointId(type, selector, lineNumber, columnNumber);
489 : protocol::DictionaryValue* breakpoints;
490 230 : switch (type) {
491 : case BreakpointType::kByUrlRegex:
492 : breakpoints =
493 10 : getOrCreateObject(m_state, DebuggerAgentState::breakpointsByRegex);
494 5 : break;
495 : case BreakpointType::kByUrl:
496 : breakpoints = getOrCreateObject(
497 : getOrCreateObject(m_state, DebuggerAgentState::breakpointsByUrl),
498 430 : selector);
499 215 : break;
500 : case BreakpointType::kByScriptHash:
501 : breakpoints = getOrCreateObject(
502 : getOrCreateObject(m_state,
503 : DebuggerAgentState::breakpointsByScriptHash),
504 20 : selector);
505 10 : break;
506 : default:
507 0 : UNREACHABLE();
508 : break;
509 : }
510 230 : if (breakpoints->get(breakpointId)) {
511 0 : return Response::Error("Breakpoint at specified location already exists.");
512 : }
513 :
514 230 : String16 hint;
515 1705 : for (const auto& script : m_scripts) {
516 2205 : if (!matches(m_inspector, *script.second, type, selector)) continue;
517 285 : if (!hint.isEmpty()) {
518 : adjustBreakpointLocation(*script.second, hint, &lineNumber,
519 70 : &columnNumber);
520 : }
521 : std::unique_ptr<protocol::Debugger::Location> location = setBreakpointImpl(
522 285 : breakpointId, script.first, condition, lineNumber, columnNumber);
523 285 : if (location && type != BreakpointType::kByUrlRegex) {
524 645 : hint = breakpointHint(*script.second, lineNumber, columnNumber);
525 : }
526 505 : if (location) (*locations)->addItem(std::move(location));
527 : }
528 230 : breakpoints->setString(breakpointId, condition);
529 230 : if (!hint.isEmpty()) {
530 : protocol::DictionaryValue* breakpointHints =
531 390 : getOrCreateObject(m_state, DebuggerAgentState::breakpointHints);
532 195 : breakpointHints->setString(breakpointId, hint);
533 : }
534 230 : *outBreakpointId = breakpointId;
535 230 : return Response::OK();
536 : }
537 :
538 1949 : Response V8DebuggerAgentImpl::setBreakpoint(
539 : std::unique_ptr<protocol::Debugger::Location> location,
540 : Maybe<String16> optionalCondition, String16* outBreakpointId,
541 : std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
542 : String16 breakpointId = generateBreakpointId(
543 : BreakpointType::kByScriptId, location->getScriptId(),
544 5847 : location->getLineNumber(), location->getColumnNumber(0));
545 1949 : if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
546 : m_breakpointIdToDebuggerBreakpointIds.end()) {
547 288 : return Response::Error("Breakpoint at specified location already exists.");
548 : }
549 10830 : *actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
550 : optionalCondition.fromMaybe(String16()),
551 : location->getLineNumber(),
552 : location->getColumnNumber(0));
553 1805 : if (!*actualLocation) return Response::Error("Could not resolve breakpoint");
554 1805 : *outBreakpointId = breakpointId;
555 1805 : return Response::OK();
556 : }
557 :
558 20 : Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
559 : const String16& functionObjectId, Maybe<String16> optionalCondition,
560 : String16* outBreakpointId) {
561 20 : InjectedScript::ObjectScope scope(m_session, functionObjectId);
562 20 : Response response = scope.initialize();
563 20 : if (!response.isSuccess()) return response;
564 20 : if (!scope.object()->IsFunction()) {
565 0 : return Response::Error("Could not find function with given id");
566 : }
567 : v8::Local<v8::Function> function =
568 20 : v8::Local<v8::Function>::Cast(scope.object());
569 : String16 breakpointId =
570 20 : generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
571 20 : if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
572 : m_breakpointIdToDebuggerBreakpointIds.end()) {
573 10 : return Response::Error("Breakpoint at specified location already exists.");
574 : }
575 : v8::Local<v8::String> condition =
576 60 : toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
577 15 : setBreakpointImpl(breakpointId, function, condition);
578 15 : *outBreakpointId = breakpointId;
579 35 : return Response::OK();
580 : }
581 :
582 789 : Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
583 794 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
584 : BreakpointType type;
585 784 : String16 selector;
586 784 : if (!parseBreakpointId(breakpointId, &type, &selector)) {
587 10 : return Response::OK();
588 : }
589 : protocol::DictionaryValue* breakpoints = nullptr;
590 774 : switch (type) {
591 : case BreakpointType::kByUrl: {
592 : protocol::DictionaryValue* breakpointsByUrl =
593 80 : m_state->getObject(DebuggerAgentState::breakpointsByUrl);
594 40 : if (breakpointsByUrl) {
595 40 : breakpoints = breakpointsByUrl->getObject(selector);
596 : }
597 : } break;
598 : case BreakpointType::kByScriptHash: {
599 : protocol::DictionaryValue* breakpointsByScriptHash =
600 20 : m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
601 10 : if (breakpointsByScriptHash) {
602 10 : breakpoints = breakpointsByScriptHash->getObject(selector);
603 : }
604 : } break;
605 : case BreakpointType::kByUrlRegex:
606 0 : breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
607 0 : break;
608 : default:
609 : break;
610 : }
611 774 : if (breakpoints) breakpoints->remove(breakpointId);
612 : protocol::DictionaryValue* breakpointHints =
613 1548 : m_state->getObject(DebuggerAgentState::breakpointHints);
614 774 : if (breakpointHints) breakpointHints->remove(breakpointId);
615 774 : removeBreakpointImpl(breakpointId);
616 774 : return Response::OK();
617 : }
618 :
619 844 : void V8DebuggerAgentImpl::removeBreakpointImpl(const String16& breakpointId) {
620 : DCHECK(enabled());
621 : BreakpointIdToDebuggerBreakpointIdsMap::iterator
622 : debuggerBreakpointIdsIterator =
623 : m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
624 844 : if (debuggerBreakpointIdsIterator ==
625 : m_breakpointIdToDebuggerBreakpointIds.end()) {
626 844 : return;
627 : }
628 2547 : for (const auto& id : debuggerBreakpointIdsIterator->second) {
629 859 : v8::debug::RemoveBreakpoint(m_isolate, id);
630 : m_debuggerBreakpointIdToBreakpointId.erase(id);
631 : }
632 : m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
633 : }
634 :
635 265 : Response V8DebuggerAgentImpl::getPossibleBreakpoints(
636 : std::unique_ptr<protocol::Debugger::Location> start,
637 : Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
638 : std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
639 : locations) {
640 : String16 scriptId = start->getScriptId();
641 :
642 530 : if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
643 : return Response::Error(
644 0 : "start.lineNumber and start.columnNumber should be >= 0");
645 :
646 : v8::debug::Location v8Start(start->getLineNumber(),
647 265 : start->getColumnNumber(0));
648 265 : v8::debug::Location v8End;
649 265 : if (end.isJust()) {
650 110 : if (end.fromJust()->getScriptId() != scriptId)
651 10 : return Response::Error("Locations should contain the same scriptId");
652 105 : int line = end.fromJust()->getLineNumber();
653 : int column = end.fromJust()->getColumnNumber(0);
654 105 : if (line < 0 || column < 0)
655 : return Response::Error(
656 0 : "end.lineNumber and end.columnNumber should be >= 0");
657 105 : v8End = v8::debug::Location(line, column);
658 : }
659 : auto it = m_scripts.find(scriptId);
660 265 : if (it == m_scripts.end()) return Response::Error("Script not found");
661 : std::vector<v8::debug::BreakLocation> v8Locations;
662 : {
663 255 : v8::HandleScope handleScope(m_isolate);
664 255 : int contextId = it->second->executionContextId();
665 255 : InspectedContext* inspected = m_inspector->getContext(contextId);
666 255 : if (!inspected) {
667 0 : return Response::Error("Cannot retrive script context");
668 : }
669 255 : v8::Context::Scope contextScope(inspected->context());
670 : v8::MicrotasksScope microtasks(m_isolate,
671 510 : v8::MicrotasksScope::kDoNotRunMicrotasks);
672 510 : v8::TryCatch tryCatch(m_isolate);
673 : it->second->getPossibleBreakpoints(
674 765 : v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations);
675 : }
676 :
677 : *locations = protocol::Array<protocol::Debugger::BreakLocation>::create();
678 8715 : for (size_t i = 0; i < v8Locations.size(); ++i) {
679 : std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
680 : protocol::Debugger::BreakLocation::create()
681 4230 : .setScriptId(scriptId)
682 8460 : .setLineNumber(v8Locations[i].GetLineNumber())
683 4230 : .setColumnNumber(v8Locations[i].GetColumnNumber())
684 : .build();
685 8460 : if (v8Locations[i].type() != v8::debug::kCommonBreakLocation) {
686 4960 : breakLocation->setType(breakLocationType(v8Locations[i].type()));
687 : }
688 4230 : (*locations)->addItem(std::move(breakLocation));
689 : }
690 255 : return Response::OK();
691 : }
692 :
693 60 : Response V8DebuggerAgentImpl::continueToLocation(
694 : std::unique_ptr<protocol::Debugger::Location> location,
695 60 : Maybe<String16> targetCallFrames) {
696 60 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
697 60 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
698 60 : ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
699 60 : if (it == m_scripts.end()) {
700 0 : return Response::Error("Cannot continue to specified location");
701 : }
702 60 : V8DebuggerScript* script = it->second.get();
703 : int contextId = script->executionContextId();
704 60 : InspectedContext* inspected = m_inspector->getContext(contextId);
705 60 : if (!inspected)
706 0 : return Response::Error("Cannot continue to specified location");
707 60 : v8::Context::Scope contextScope(inspected->context());
708 : return m_debugger->continueToLocation(
709 : m_session->contextGroupId(), script, std::move(location),
710 : targetCallFrames.fromMaybe(
711 300 : protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
712 : }
713 :
714 35 : Response V8DebuggerAgentImpl::getStackTrace(
715 : std::unique_ptr<protocol::Runtime::StackTraceId> inStackTraceId,
716 : std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
717 35 : bool isOk = false;
718 70 : int64_t id = inStackTraceId->getId().toInteger64(&isOk);
719 35 : std::pair<int64_t, int64_t> debuggerId;
720 35 : if (inStackTraceId->hasDebuggerId()) {
721 50 : debuggerId =
722 135 : m_debugger->debuggerIdFor(inStackTraceId->getDebuggerId(String16()));
723 : } else {
724 55 : debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
725 : }
726 35 : V8StackTraceId v8StackTraceId(id, debuggerId);
727 35 : if (!isOk || v8StackTraceId.IsInvalid()) {
728 0 : return Response::Error("Invalid stack trace id");
729 : }
730 : auto stack =
731 70 : m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
732 35 : if (!stack) {
733 0 : return Response::Error("Stack trace with given id is not found");
734 : }
735 105 : *outStackTrace = stack->buildInspectorObject(
736 : m_debugger, m_debugger->maxAsyncCallChainDepth());
737 35 : return Response::OK();
738 : }
739 :
740 11142 : bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
741 : const v8::debug::Location& start,
742 : const v8::debug::Location& end) {
743 : ScriptsMap::iterator it = m_scripts.find(scriptId);
744 11142 : if (it == m_scripts.end()) {
745 : // Unknown scripts are blackboxed.
746 : return true;
747 : }
748 11142 : if (m_blackboxPattern) {
749 930 : const String16& scriptSourceURL = it->second->sourceURL();
750 1415 : if (!scriptSourceURL.isEmpty() &&
751 485 : m_blackboxPattern->match(scriptSourceURL) != -1)
752 : return true;
753 : }
754 : auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
755 10892 : if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
756 :
757 315 : const std::vector<std::pair<int, int>>& ranges =
758 : itBlackboxedPositions->second;
759 : auto itStartRange = std::lower_bound(
760 : ranges.begin(), ranges.end(),
761 110 : std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
762 220 : positionComparator);
763 : auto itEndRange = std::lower_bound(
764 : itStartRange, ranges.end(),
765 110 : std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
766 220 : positionComparator);
767 : // Ranges array contains positions in script where blackbox state is changed.
768 : // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
769 : // blackboxed...
770 205 : return itStartRange == itEndRange &&
771 110 : std::distance(ranges.begin(), itStartRange) % 2;
772 : }
773 :
774 111623 : bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
775 111623 : return enabled() && (isOOMBreak || !m_skipAllPauses);
776 : }
777 :
778 : std::unique_ptr<protocol::Debugger::Location>
779 2200 : V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
780 : const String16& scriptId,
781 : const String16& condition,
782 : int lineNumber, int columnNumber) {
783 2200 : v8::HandleScope handles(m_isolate);
784 : DCHECK(enabled());
785 :
786 : ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
787 2200 : if (scriptIterator == m_scripts.end()) return nullptr;
788 2125 : V8DebuggerScript* script = scriptIterator->second.get();
789 2200 : if (lineNumber < script->startLine() || script->endLine() < lineNumber) {
790 : return nullptr;
791 : }
792 :
793 : v8::debug::BreakpointId debuggerBreakpointId;
794 2125 : v8::debug::Location location(lineNumber, columnNumber);
795 : int contextId = script->executionContextId();
796 2125 : InspectedContext* inspected = m_inspector->getContext(contextId);
797 2125 : if (!inspected) return nullptr;
798 :
799 : {
800 2125 : v8::Context::Scope contextScope(inspected->context());
801 2125 : if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
802 : return nullptr;
803 : }
804 : }
805 :
806 2095 : m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
807 : m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
808 2095 : debuggerBreakpointId);
809 :
810 : return protocol::Debugger::Location::create()
811 2095 : .setScriptId(scriptId)
812 2095 : .setLineNumber(location.GetLineNumber())
813 2095 : .setColumnNumber(location.GetColumnNumber())
814 2200 : .build();
815 : }
816 :
817 95 : void V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
818 : v8::Local<v8::Function> function,
819 : v8::Local<v8::String> condition) {
820 : v8::debug::BreakpointId debuggerBreakpointId;
821 95 : if (!v8::debug::SetFunctionBreakpoint(function, condition,
822 95 : &debuggerBreakpointId)) {
823 0 : return;
824 : }
825 95 : m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
826 : m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
827 95 : debuggerBreakpointId);
828 : }
829 :
830 0 : Response V8DebuggerAgentImpl::searchInContent(
831 : const String16& scriptId, const String16& query,
832 : Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
833 : std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
834 0 : v8::HandleScope handles(m_isolate);
835 : ScriptsMap::iterator it = m_scripts.find(scriptId);
836 0 : if (it == m_scripts.end())
837 0 : return Response::Error("No script for id: " + scriptId);
838 :
839 : std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
840 0 : searchInTextByLinesImpl(m_session, it->second->source(0), query,
841 : optionalCaseSensitive.fromMaybe(false),
842 0 : optionalIsRegex.fromMaybe(false));
843 : *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
844 0 : for (size_t i = 0; i < matches.size(); ++i)
845 0 : (*results)->addItem(std::move(matches[i]));
846 0 : return Response::OK();
847 : }
848 :
849 55 : Response V8DebuggerAgentImpl::setScriptSource(
850 : const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
851 : Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
852 : Maybe<bool>* stackChanged,
853 : Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
854 : Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
855 55 : Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
856 55 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
857 :
858 : ScriptsMap::iterator it = m_scripts.find(scriptId);
859 55 : if (it == m_scripts.end()) {
860 10 : return Response::Error("No script with given id found");
861 : }
862 50 : int contextId = it->second->executionContextId();
863 50 : InspectedContext* inspected = m_inspector->getContext(contextId);
864 50 : if (!inspected) {
865 0 : return Response::InternalError();
866 : }
867 50 : v8::HandleScope handleScope(m_isolate);
868 50 : v8::Local<v8::Context> context = inspected->context();
869 : v8::Context::Scope contextScope(context);
870 :
871 : v8::debug::LiveEditResult result;
872 100 : it->second->setSource(newContent, dryRun.fromMaybe(false), &result);
873 50 : if (result.status != v8::debug::LiveEditResult::OK) {
874 : *optOutCompileError =
875 : protocol::Runtime::ExceptionDetails::create()
876 30 : .setExceptionId(m_inspector->nextExceptionId())
877 20 : .setText(toProtocolString(m_isolate, result.message))
878 10 : .setLineNumber(result.line_number != -1 ? result.line_number - 1
879 10 : : 0)
880 10 : .setColumnNumber(result.column_number != -1 ? result.column_number
881 10 : : 0)
882 : .build();
883 10 : return Response::OK();
884 : } else {
885 40 : *stackChanged = result.stack_changed;
886 : }
887 40 : std::unique_ptr<Array<CallFrame>> callFrames;
888 40 : Response response = currentCallFrames(&callFrames);
889 40 : if (!response.isSuccess()) return response;
890 : *newCallFrames = std::move(callFrames);
891 80 : *asyncStackTrace = currentAsyncStackTrace();
892 80 : *asyncStackTraceId = currentExternalStackTrace();
893 90 : return Response::OK();
894 : }
895 :
896 104 : Response V8DebuggerAgentImpl::restartFrame(
897 : const String16& callFrameId,
898 : std::unique_ptr<Array<CallFrame>>* newCallFrames,
899 : Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
900 : Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
901 109 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
902 99 : InjectedScript::CallFrameScope scope(m_session, callFrameId);
903 99 : Response response = scope.initialize();
904 99 : if (!response.isSuccess()) return response;
905 99 : int frameOrdinal = static_cast<int>(scope.frameOrdinal());
906 99 : auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
907 99 : if (it->Done()) {
908 0 : return Response::Error("Could not find call frame with given id");
909 : }
910 99 : if (!it->Restart()) {
911 99 : return Response::InternalError();
912 : }
913 0 : response = currentCallFrames(newCallFrames);
914 0 : if (!response.isSuccess()) return response;
915 0 : *asyncStackTrace = currentAsyncStackTrace();
916 0 : *asyncStackTraceId = currentExternalStackTrace();
917 99 : return Response::OK();
918 : }
919 :
920 16094 : Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
921 16094 : String16* scriptSource) {
922 16094 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
923 : ScriptsMap::iterator it = m_scripts.find(scriptId);
924 16094 : if (it == m_scripts.end())
925 1000 : return Response::Error("No script for id: " + scriptId);
926 31188 : *scriptSource = it->second->source(0);
927 15594 : return Response::OK();
928 : }
929 :
930 445 : void V8DebuggerAgentImpl::pushBreakDetails(
931 : const String16& breakReason,
932 : std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
933 890 : m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
934 445 : }
935 :
936 0 : void V8DebuggerAgentImpl::popBreakDetails() {
937 100 : if (m_breakReason.empty()) return;
938 : m_breakReason.pop_back();
939 : }
940 :
941 114304 : void V8DebuggerAgentImpl::clearBreakDetails() {
942 : std::vector<BreakReason> emptyBreakReason;
943 114304 : m_breakReason.swap(emptyBreakReason);
944 114304 : }
945 :
946 120 : void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
947 : const String16& breakReason,
948 : std::unique_ptr<protocol::DictionaryValue> data) {
949 360 : if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
950 120 : if (m_breakReason.empty()) {
951 100 : m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
952 : }
953 240 : pushBreakDetails(breakReason, std::move(data));
954 : }
955 :
956 50 : void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
957 150 : if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
958 90 : if (m_breakReason.size() == 1) {
959 15 : m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
960 : }
961 : popBreakDetails();
962 : }
963 :
964 265 : Response V8DebuggerAgentImpl::pause() {
965 265 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
966 265 : if (isPaused()) return Response::OK();
967 265 : if (m_debugger->canBreakProgram()) {
968 260 : m_debugger->interruptAndBreak(m_session->contextGroupId());
969 : } else {
970 265 : if (m_breakReason.empty()) {
971 520 : m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
972 : }
973 795 : pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
974 : }
975 265 : return Response::OK();
976 : }
977 :
978 2283 : Response V8DebuggerAgentImpl::resume() {
979 2326 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
980 6720 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
981 4480 : m_debugger->continueProgram(m_session->contextGroupId());
982 2240 : return Response::OK();
983 : }
984 :
985 7607 : Response V8DebuggerAgentImpl::stepOver() {
986 7612 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
987 22806 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
988 15204 : m_debugger->stepOverStatement(m_session->contextGroupId());
989 7602 : return Response::OK();
990 : }
991 :
992 36408 : Response V8DebuggerAgentImpl::stepInto(Maybe<bool> inBreakOnAsyncCall) {
993 36413 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
994 109209 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
995 : m_debugger->stepIntoStatement(m_session->contextGroupId(),
996 72806 : inBreakOnAsyncCall.fromMaybe(false));
997 36403 : return Response::OK();
998 : }
999 :
1000 539 : Response V8DebuggerAgentImpl::stepOut() {
1001 544 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1002 1602 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
1003 1068 : m_debugger->stepOutOfFunction(m_session->contextGroupId());
1004 534 : return Response::OK();
1005 : }
1006 :
1007 95 : Response V8DebuggerAgentImpl::pauseOnAsyncCall(
1008 : std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1009 95 : bool isOk = false;
1010 190 : int64_t stackTraceId = inParentStackTraceId->getId().toInteger64(&isOk);
1011 95 : if (!isOk) {
1012 0 : return Response::Error("Invalid stack trace id");
1013 : }
1014 : m_debugger->pauseOnAsyncCall(m_session->contextGroupId(), stackTraceId,
1015 380 : inParentStackTraceId->getDebuggerId(String16()));
1016 95 : return Response::OK();
1017 : }
1018 :
1019 4746 : Response V8DebuggerAgentImpl::setPauseOnExceptions(
1020 4746 : const String16& stringPauseState) {
1021 4746 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
1022 : v8::debug::ExceptionBreakState pauseState;
1023 9492 : if (stringPauseState == "none") {
1024 : pauseState = v8::debug::NoBreakOnException;
1025 7268 : } else if (stringPauseState == "all") {
1026 : pauseState = v8::debug::BreakOnAnyException;
1027 4658 : } else if (stringPauseState == "uncaught") {
1028 : pauseState = v8::debug::BreakOnUncaughtException;
1029 : } else {
1030 0 : return Response::Error("Unknown pause on exceptions mode: " +
1031 0 : stringPauseState);
1032 : }
1033 4746 : setPauseOnExceptionsImpl(pauseState);
1034 4746 : return Response::OK();
1035 : }
1036 :
1037 4761 : void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
1038 : // TODO(dgozman): this changes the global state and forces all context groups
1039 : // to pause. We should make this flag be per-context-group.
1040 : m_debugger->setPauseOnExceptionsState(
1041 4761 : static_cast<v8::debug::ExceptionBreakState>(pauseState));
1042 9522 : m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
1043 4761 : }
1044 :
1045 11097 : Response V8DebuggerAgentImpl::evaluateOnCallFrame(
1046 : const String16& callFrameId, const String16& expression,
1047 : Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
1048 : Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
1049 : Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
1050 : std::unique_ptr<RemoteObject>* result,
1051 : Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
1052 11102 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1053 11092 : InjectedScript::CallFrameScope scope(m_session, callFrameId);
1054 11092 : Response response = scope.initialize();
1055 11092 : if (!response.isSuccess()) return response;
1056 11092 : if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
1057 11092 : if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
1058 :
1059 11092 : int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1060 11092 : auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1061 11092 : if (it->Done()) {
1062 0 : return Response::Error("Could not find call frame with given id");
1063 : }
1064 :
1065 : v8::MaybeLocal<v8::Value> maybeResultValue;
1066 : {
1067 11092 : V8InspectorImpl::EvaluateScope evaluateScope(scope);
1068 11092 : if (timeout.isJust()) {
1069 30 : response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
1070 15 : if (!response.isSuccess()) return response;
1071 : }
1072 : maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
1073 22184 : throwOnSideEffect.fromMaybe(false));
1074 : }
1075 : // Re-initialize after running client's code, as it could have destroyed
1076 : // context or session.
1077 22184 : response = scope.initialize();
1078 11092 : if (!response.isSuccess()) return response;
1079 : WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
1080 11092 : : WrapMode::kNoPreview;
1081 11092 : if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
1082 : return scope.injectedScript()->wrapEvaluateResult(
1083 : maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), mode,
1084 55460 : result, exceptionDetails);
1085 : }
1086 :
1087 221 : Response V8DebuggerAgentImpl::setVariableValue(
1088 : int scopeNumber, const String16& variableName,
1089 : std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
1090 221 : const String16& callFrameId) {
1091 221 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
1092 226 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1093 216 : InjectedScript::CallFrameScope scope(m_session, callFrameId);
1094 216 : Response response = scope.initialize();
1095 216 : if (!response.isSuccess()) return response;
1096 : v8::Local<v8::Value> newValue;
1097 432 : response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
1098 : &newValue);
1099 216 : if (!response.isSuccess()) return response;
1100 :
1101 216 : int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1102 216 : auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1103 216 : if (it->Done()) {
1104 0 : return Response::Error("Could not find call frame with given id");
1105 : }
1106 216 : auto scopeIterator = it->GetScopeIterator();
1107 557 : while (!scopeIterator->Done() && scopeNumber > 0) {
1108 125 : --scopeNumber;
1109 125 : scopeIterator->Advance();
1110 : }
1111 216 : if (scopeNumber != 0) {
1112 0 : return Response::Error("Could not find scope with given number");
1113 : }
1114 :
1115 216 : if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
1116 413 : newValue) ||
1117 197 : scope.tryCatch().HasCaught()) {
1118 19 : return Response::InternalError();
1119 : }
1120 413 : return Response::OK();
1121 : }
1122 :
1123 15 : Response V8DebuggerAgentImpl::setReturnValue(
1124 15 : std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
1125 15 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
1126 15 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1127 15 : auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1128 15 : if (iterator->Done()) {
1129 0 : return Response::Error("Could not find top call frame");
1130 : }
1131 30 : if (iterator->GetReturnValue().IsEmpty()) {
1132 : return Response::Error(
1133 10 : "Could not update return value at non-return position");
1134 : }
1135 20 : InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
1136 10 : Response response = scope.initialize();
1137 10 : if (!response.isSuccess()) return response;
1138 : v8::Local<v8::Value> newValue;
1139 20 : response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
1140 : &newValue);
1141 10 : if (!response.isSuccess()) return response;
1142 10 : v8::debug::SetReturnValue(m_isolate, newValue);
1143 10 : return Response::OK();
1144 : }
1145 :
1146 1020 : Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
1147 1700 : if (!enabled() && !m_session->runtimeAgent()->enabled()) {
1148 1340 : return Response::Error(kDebuggerNotEnabled);
1149 : }
1150 700 : m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
1151 350 : m_debugger->setAsyncCallStackDepth(this, depth);
1152 350 : return Response::OK();
1153 : }
1154 :
1155 105 : Response V8DebuggerAgentImpl::setBlackboxPatterns(
1156 : std::unique_ptr<protocol::Array<String16>> patterns) {
1157 105 : if (!patterns->length()) {
1158 : m_blackboxPattern = nullptr;
1159 : resetBlackboxedStateCache();
1160 0 : m_state->remove(DebuggerAgentState::blackboxPattern);
1161 0 : return Response::OK();
1162 : }
1163 :
1164 105 : String16Builder patternBuilder;
1165 105 : patternBuilder.append('(');
1166 220 : for (size_t i = 0; i < patterns->length() - 1; ++i) {
1167 10 : patternBuilder.append(patterns->get(i));
1168 10 : patternBuilder.append("|");
1169 : }
1170 210 : patternBuilder.append(patterns->get(patterns->length() - 1));
1171 105 : patternBuilder.append(')');
1172 105 : String16 pattern = patternBuilder.toString();
1173 105 : Response response = setBlackboxPattern(pattern);
1174 105 : if (!response.isSuccess()) return response;
1175 : resetBlackboxedStateCache();
1176 200 : m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
1177 100 : return Response::OK();
1178 : }
1179 :
1180 105 : Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
1181 : std::unique_ptr<V8Regex> regex(new V8Regex(
1182 105 : m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
1183 105 : if (!regex->isValid())
1184 10 : return Response::Error("Pattern parser error: " + regex->errorMessage());
1185 : m_blackboxPattern = std::move(regex);
1186 100 : return Response::OK();
1187 : }
1188 :
1189 0 : void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
1190 63834 : for (const auto& it : m_scripts) {
1191 60263 : it.second->resetBlackboxedStateCache();
1192 : }
1193 0 : }
1194 :
1195 55 : Response V8DebuggerAgentImpl::setBlackboxedRanges(
1196 : const String16& scriptId,
1197 : std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
1198 : inPositions) {
1199 : auto it = m_scripts.find(scriptId);
1200 55 : if (it == m_scripts.end())
1201 0 : return Response::Error("No script with passed id.");
1202 :
1203 55 : if (!inPositions->length()) {
1204 : m_blackboxedPositions.erase(scriptId);
1205 0 : it->second->resetBlackboxedStateCache();
1206 0 : return Response::OK();
1207 : }
1208 :
1209 : std::vector<std::pair<int, int>> positions;
1210 55 : positions.reserve(inPositions->length());
1211 290 : for (size_t i = 0; i < inPositions->length(); ++i) {
1212 190 : protocol::Debugger::ScriptPosition* position = inPositions->get(i);
1213 95 : if (position->getLineNumber() < 0)
1214 0 : return Response::Error("Position missing 'line' or 'line' < 0.");
1215 95 : if (position->getColumnNumber() < 0)
1216 10 : return Response::Error("Position missing 'column' or 'column' < 0.");
1217 : positions.push_back(
1218 180 : std::make_pair(position->getLineNumber(), position->getColumnNumber()));
1219 : }
1220 :
1221 110 : for (size_t i = 1; i < positions.size(); ++i) {
1222 120 : if (positions[i - 1].first < positions[i].first) continue;
1223 20 : if (positions[i - 1].first == positions[i].first &&
1224 10 : positions[i - 1].second < positions[i].second)
1225 : continue;
1226 : return Response::Error(
1227 20 : "Input positions array is not sorted or contains duplicate values.");
1228 : }
1229 :
1230 40 : m_blackboxedPositions[scriptId] = positions;
1231 40 : it->second->resetBlackboxedStateCache();
1232 40 : return Response::OK();
1233 : }
1234 :
1235 55594 : Response V8DebuggerAgentImpl::currentCallFrames(
1236 : std::unique_ptr<Array<CallFrame>>* result) {
1237 55594 : if (!isPaused()) {
1238 : *result = Array<CallFrame>::create();
1239 15 : return Response::OK();
1240 : }
1241 55579 : v8::HandleScope handles(m_isolate);
1242 : *result = Array<CallFrame>::create();
1243 55579 : auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1244 : int frameOrdinal = 0;
1245 404966 : for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
1246 146904 : int contextId = iterator->GetContextId();
1247 146904 : InjectedScript* injectedScript = nullptr;
1248 293699 : if (contextId) m_session->findInjectedScript(contextId, injectedScript);
1249 : String16 callFrameId =
1250 146904 : RemoteCallFrameId::serialize(contextId, frameOrdinal);
1251 :
1252 146904 : v8::debug::Location loc = iterator->GetSourceLocation();
1253 :
1254 146904 : std::unique_ptr<Array<Scope>> scopes;
1255 146904 : auto scopeIterator = iterator->GetScopeIterator();
1256 : Response res =
1257 293808 : buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
1258 146904 : if (!res.isSuccess()) return res;
1259 :
1260 146904 : std::unique_ptr<RemoteObject> protocolReceiver;
1261 146904 : if (injectedScript) {
1262 : v8::Local<v8::Value> receiver;
1263 293590 : if (iterator->GetReceiver().ToLocal(&receiver)) {
1264 432351 : res =
1265 : injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
1266 : WrapMode::kNoPreview, &protocolReceiver);
1267 144117 : if (!res.isSuccess()) return res;
1268 : }
1269 : }
1270 146904 : if (!protocolReceiver) {
1271 : protocolReceiver = RemoteObject::create()
1272 8361 : .setType(RemoteObject::TypeEnum::Undefined)
1273 : .build();
1274 : }
1275 :
1276 146904 : v8::Local<v8::debug::Script> script = iterator->GetScript();
1277 : DCHECK(!script.IsEmpty());
1278 : std::unique_ptr<protocol::Debugger::Location> location =
1279 : protocol::Debugger::Location::create()
1280 440712 : .setScriptId(String16::fromInteger(script->Id()))
1281 146904 : .setLineNumber(loc.GetLineNumber())
1282 146904 : .setColumnNumber(loc.GetColumnNumber())
1283 : .build();
1284 146904 : TranslateLocation(location.get(), m_debugger->wasmTranslation());
1285 146904 : String16 scriptId = String16::fromInteger(script->Id());
1286 : ScriptsMap::iterator scriptIterator =
1287 146904 : m_scripts.find(location->getScriptId());
1288 146904 : String16 url;
1289 146904 : if (scriptIterator != m_scripts.end()) {
1290 146904 : url = scriptIterator->second->sourceURL();
1291 : }
1292 :
1293 : auto frame = CallFrame::create()
1294 146904 : .setCallFrameId(callFrameId)
1295 : .setFunctionName(toProtocolString(
1296 293808 : m_isolate, iterator->GetFunctionDebugName()))
1297 : .setLocation(std::move(location))
1298 : .setUrl(url)
1299 : .setScopeChain(std::move(scopes))
1300 : .setThis(std::move(protocolReceiver))
1301 : .build();
1302 :
1303 146904 : v8::Local<v8::Function> func = iterator->GetFunction();
1304 146904 : if (!func.IsEmpty()) {
1305 : frame->setFunctionLocation(
1306 : protocol::Debugger::Location::create()
1307 438669 : .setScriptId(String16::fromInteger(func->ScriptId()))
1308 146223 : .setLineNumber(func->GetScriptLineNumber())
1309 146223 : .setColumnNumber(func->GetScriptColumnNumber())
1310 : .build());
1311 : }
1312 :
1313 146904 : v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
1314 146904 : if (!returnValue.IsEmpty() && injectedScript) {
1315 5707 : std::unique_ptr<RemoteObject> value;
1316 17121 : res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
1317 : WrapMode::kNoPreview, &value);
1318 5707 : if (!res.isSuccess()) return res;
1319 : frame->setReturnValue(std::move(value));
1320 : }
1321 146904 : (*result)->addItem(std::move(frame));
1322 : }
1323 111158 : return Response::OK();
1324 : }
1325 :
1326 : std::unique_ptr<protocol::Runtime::StackTrace>
1327 55594 : V8DebuggerAgentImpl::currentAsyncStackTrace() {
1328 : std::shared_ptr<AsyncStackTrace> asyncParent =
1329 56219 : m_debugger->currentAsyncParent();
1330 55594 : if (!asyncParent) return nullptr;
1331 : return asyncParent->buildInspectorObject(
1332 1250 : m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
1333 : }
1334 :
1335 : std::unique_ptr<protocol::Runtime::StackTraceId>
1336 55594 : V8DebuggerAgentImpl::currentExternalStackTrace() {
1337 55594 : V8StackTraceId externalParent = m_debugger->currentExternalParent();
1338 55594 : if (externalParent.IsInvalid()) return nullptr;
1339 : return protocol::Runtime::StackTraceId::create()
1340 75 : .setId(stackTraceIdToString(externalParent.id))
1341 50 : .setDebuggerId(debuggerIdToString(externalParent.debugger_id))
1342 : .build();
1343 : }
1344 :
1345 : std::unique_ptr<protocol::Runtime::StackTraceId>
1346 55554 : V8DebuggerAgentImpl::currentScheduledAsyncCall() {
1347 : v8_inspector::V8StackTraceId scheduledAsyncCall =
1348 55554 : m_debugger->scheduledAsyncCall();
1349 55554 : if (scheduledAsyncCall.IsInvalid()) return nullptr;
1350 : std::unique_ptr<protocol::Runtime::StackTraceId> asyncCallStackTrace =
1351 : protocol::Runtime::StackTraceId::create()
1352 285 : .setId(stackTraceIdToString(scheduledAsyncCall.id))
1353 : .build();
1354 : // TODO(kozyatinskiy): extract this check to IsLocal function.
1355 95 : if (scheduledAsyncCall.debugger_id.first ||
1356 : scheduledAsyncCall.debugger_id.second) {
1357 : asyncCallStackTrace->setDebuggerId(
1358 20 : debuggerIdToString(scheduledAsyncCall.debugger_id));
1359 : }
1360 : return asyncCallStackTrace;
1361 : }
1362 :
1363 0 : bool V8DebuggerAgentImpl::isPaused() const {
1364 117829 : return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
1365 : }
1366 :
1367 60428 : void V8DebuggerAgentImpl::didParseSource(
1368 : std::unique_ptr<V8DebuggerScript> script, bool success) {
1369 60428 : v8::HandleScope handles(m_isolate);
1370 60428 : if (!success) {
1371 : DCHECK(!script->isSourceLoadedLazily());
1372 5201 : String16 scriptSource = script->source(0);
1373 15603 : script->setSourceURL(findSourceURL(scriptSource, false));
1374 10402 : script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
1375 : }
1376 :
1377 60428 : int contextId = script->executionContextId();
1378 60428 : int contextGroupId = m_inspector->contextGroupId(contextId);
1379 : InspectedContext* inspected =
1380 60428 : m_inspector->getContext(contextGroupId, contextId);
1381 : std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1382 60428 : if (inspected) {
1383 : // Script reused between different groups/sessions can have a stale
1384 : // execution context id.
1385 181269 : executionContextAuxData = protocol::DictionaryValue::cast(
1386 : protocol::StringUtil::parseJSON(inspected->auxData()));
1387 : }
1388 60428 : bool isLiveEdit = script->isLiveEdit();
1389 60428 : bool hasSourceURLComment = script->hasSourceURLComment();
1390 60428 : bool isModule = script->isModule();
1391 60428 : String16 scriptId = script->scriptId();
1392 60428 : String16 scriptURL = script->sourceURL();
1393 :
1394 : m_scripts[scriptId] = std::move(script);
1395 :
1396 : ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1397 : DCHECK(scriptIterator != m_scripts.end());
1398 : V8DebuggerScript* scriptRef = scriptIterator->second.get();
1399 : // V8 could create functions for parsed scripts before reporting and asks
1400 : // inspector about blackboxed state, we should reset state each time when we
1401 : // make any change that change isFunctionBlackboxed output - adding parsed
1402 : // script is changing.
1403 60428 : scriptRef->resetBlackboxedStateCache();
1404 :
1405 60428 : Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
1406 : Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
1407 : std::move(executionContextAuxData));
1408 60428 : const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1409 : const bool* hasSourceURLParam =
1410 60428 : hasSourceURLComment ? &hasSourceURLComment : nullptr;
1411 60428 : const bool* isModuleParam = isModule ? &isModule : nullptr;
1412 : std::unique_ptr<V8StackTraceImpl> stack =
1413 120856 : V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
1414 : std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
1415 53284 : stack && !stack->isEmpty()
1416 : ? stack->buildInspectorObjectImpl(m_debugger, 0)
1417 166766 : : nullptr;
1418 60428 : if (success) {
1419 : // TODO(herhut, dgozman): Report correct length for WASM if needed for
1420 : // coverage. Or do not send the length at all and change coverage instead.
1421 55227 : if (scriptRef->isSourceLoadedLazily()) {
1422 : m_frontend.scriptParsed(
1423 102 : scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(),
1424 : std::move(executionContextAuxDataParam), isLiveEditParam,
1425 : std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0,
1426 714 : std::move(stackTrace));
1427 : } else {
1428 : m_frontend.scriptParsed(
1429 110250 : scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1430 110250 : scriptRef->endLine(), scriptRef->endColumn(), contextId,
1431 55125 : scriptRef->hash(), std::move(executionContextAuxDataParam),
1432 : isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
1433 606375 : isModuleParam, scriptRef->length(), std::move(stackTrace));
1434 : }
1435 : } else {
1436 : m_frontend.scriptFailedToParse(
1437 10402 : scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1438 10402 : scriptRef->endLine(), scriptRef->endColumn(), contextId,
1439 5201 : scriptRef->hash(), std::move(executionContextAuxDataParam),
1440 : std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
1441 52010 : scriptRef->length(), std::move(stackTrace));
1442 : }
1443 :
1444 60428 : if (!success) {
1445 5201 : if (scriptURL.isEmpty()) {
1446 5141 : m_failedToParseAnonymousScriptIds.push_back(scriptId);
1447 5141 : cleanupOldFailedToParseAnonymousScriptsIfNeeded();
1448 : }
1449 60428 : return;
1450 : }
1451 :
1452 : std::vector<protocol::DictionaryValue*> potentialBreakpoints;
1453 55227 : if (!scriptURL.isEmpty()) {
1454 : protocol::DictionaryValue* breakpointsByUrl =
1455 36470 : m_state->getObject(DebuggerAgentState::breakpointsByUrl);
1456 18235 : if (breakpointsByUrl) {
1457 540 : potentialBreakpoints.push_back(breakpointsByUrl->getObject(scriptURL));
1458 : }
1459 : potentialBreakpoints.push_back(
1460 54705 : m_state->getObject(DebuggerAgentState::breakpointsByRegex));
1461 : }
1462 : protocol::DictionaryValue* breakpointsByScriptHash =
1463 110454 : m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
1464 55227 : if (breakpointsByScriptHash) {
1465 : potentialBreakpoints.push_back(
1466 50 : breakpointsByScriptHash->getObject(scriptRef->hash()));
1467 : }
1468 : protocol::DictionaryValue* breakpointHints =
1469 110454 : m_state->getObject(DebuggerAgentState::breakpointHints);
1470 128984 : for (auto breakpoints : potentialBreakpoints) {
1471 18530 : if (!breakpoints) continue;
1472 330 : for (size_t i = 0; i < breakpoints->size(); ++i) {
1473 110 : auto breakpointWithCondition = breakpoints->at(i);
1474 110 : String16 breakpointId = breakpointWithCondition.first;
1475 :
1476 : BreakpointType type;
1477 110 : String16 selector;
1478 110 : int lineNumber = 0;
1479 110 : int columnNumber = 0;
1480 : parseBreakpointId(breakpointId, &type, &selector, &lineNumber,
1481 110 : &columnNumber);
1482 :
1483 110 : if (!matches(m_inspector, *scriptRef, type, selector)) continue;
1484 110 : String16 condition;
1485 110 : breakpointWithCondition.second->asString(&condition);
1486 110 : String16 hint;
1487 : bool hasHint =
1488 110 : breakpointHints && breakpointHints->getString(breakpointId, &hint);
1489 110 : if (hasHint) {
1490 90 : adjustBreakpointLocation(*scriptRef, hint, &lineNumber, &columnNumber);
1491 : }
1492 : std::unique_ptr<protocol::Debugger::Location> location =
1493 : setBreakpointImpl(breakpointId, scriptId, condition, lineNumber,
1494 110 : columnNumber);
1495 110 : if (location)
1496 140 : m_frontend.breakpointResolved(breakpointId, std::move(location));
1497 : }
1498 55227 : }
1499 : }
1500 :
1501 55554 : void V8DebuggerAgentImpl::didPause(
1502 : int contextId, v8::Local<v8::Value> exception,
1503 : const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
1504 : v8::debug::ExceptionType exceptionType, bool isUncaught, bool isOOMBreak,
1505 : bool isAssert) {
1506 55554 : v8::HandleScope handles(m_isolate);
1507 :
1508 55554 : std::vector<BreakReason> hitReasons;
1509 :
1510 55554 : if (isOOMBreak) {
1511 : hitReasons.push_back(
1512 5 : std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
1513 55549 : } else if (isAssert) {
1514 : hitReasons.push_back(std::make_pair(
1515 60 : protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
1516 55489 : } else if (!exception.IsEmpty()) {
1517 1887 : InjectedScript* injectedScript = nullptr;
1518 3774 : m_session->findInjectedScript(contextId, injectedScript);
1519 1887 : if (injectedScript) {
1520 : String16 breakReason =
1521 : exceptionType == v8::debug::kPromiseRejection
1522 : ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1523 1887 : : protocol::Debugger::Paused::ReasonEnum::Exception;
1524 1887 : std::unique_ptr<protocol::Runtime::RemoteObject> obj;
1525 : injectedScript->wrapObject(exception, kBacktraceObjectGroup,
1526 5661 : WrapMode::kNoPreview, &obj);
1527 : std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1528 1887 : if (obj) {
1529 3774 : breakAuxData = obj->toValue();
1530 3774 : breakAuxData->setBoolean("uncaught", isUncaught);
1531 : } else {
1532 : breakAuxData = nullptr;
1533 : }
1534 : hitReasons.push_back(
1535 1887 : std::make_pair(breakReason, std::move(breakAuxData)));
1536 : }
1537 : }
1538 :
1539 : std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
1540 :
1541 113723 : for (const auto& id : hitBreakpoints) {
1542 : auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
1543 2615 : if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
1544 2565 : continue;
1545 : }
1546 2510 : const String16& breakpointId = breakpointIterator->second;
1547 : hitBreakpointIds->addItem(breakpointId);
1548 : BreakpointType type;
1549 2510 : parseBreakpointId(breakpointId, &type);
1550 2510 : if (type != BreakpointType::kDebugCommand) continue;
1551 : hitReasons.push_back(std::make_pair(
1552 50 : protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
1553 : }
1554 :
1555 56374 : for (size_t i = 0; i < m_breakReason.size(); ++i) {
1556 55964 : hitReasons.push_back(std::move(m_breakReason[i]));
1557 : }
1558 55554 : clearBreakDetails();
1559 :
1560 55554 : String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1561 : std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1562 111108 : if (hitReasons.size() == 1) {
1563 2392 : breakReason = hitReasons[0].first;
1564 2392 : breakAuxData = std::move(hitReasons[0].second);
1565 53162 : } else if (hitReasons.size() > 1) {
1566 20 : breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
1567 : std::unique_ptr<protocol::ListValue> reasons =
1568 10 : protocol::ListValue::create();
1569 60 : for (size_t i = 0; i < hitReasons.size(); ++i) {
1570 : std::unique_ptr<protocol::DictionaryValue> reason =
1571 20 : protocol::DictionaryValue::create();
1572 80 : reason->setString("reason", hitReasons[i].first);
1573 40 : if (hitReasons[i].second)
1574 20 : reason->setObject("auxData", std::move(hitReasons[i].second));
1575 40 : reasons->pushValue(std::move(reason));
1576 : }
1577 20 : breakAuxData = protocol::DictionaryValue::create();
1578 30 : breakAuxData->setArray("reasons", std::move(reasons));
1579 : }
1580 :
1581 55554 : std::unique_ptr<Array<CallFrame>> protocolCallFrames;
1582 55554 : Response response = currentCallFrames(&protocolCallFrames);
1583 55554 : if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
1584 :
1585 : m_frontend.paused(std::move(protocolCallFrames), breakReason,
1586 : std::move(breakAuxData), std::move(hitBreakpointIds),
1587 : currentAsyncStackTrace(), currentExternalStackTrace(),
1588 777756 : currentScheduledAsyncCall());
1589 55554 : }
1590 :
1591 55279 : void V8DebuggerAgentImpl::didContinue() {
1592 55279 : clearBreakDetails();
1593 55279 : m_frontend.resumed();
1594 55279 : }
1595 :
1596 65 : void V8DebuggerAgentImpl::breakProgram(
1597 : const String16& breakReason,
1598 120 : std::unique_ptr<protocol::DictionaryValue> data) {
1599 75 : if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return;
1600 : std::vector<BreakReason> currentScheduledReason;
1601 : currentScheduledReason.swap(m_breakReason);
1602 120 : pushBreakDetails(breakReason, std::move(data));
1603 :
1604 65 : int contextGroupId = m_session->contextGroupId();
1605 : int sessionId = m_session->sessionId();
1606 60 : V8InspectorImpl* inspector = m_inspector;
1607 60 : m_debugger->breakProgram(contextGroupId);
1608 : // Check that session and |this| are still around.
1609 65 : if (!inspector->sessionById(contextGroupId, sessionId)) return;
1610 55 : if (!enabled()) return;
1611 :
1612 : popBreakDetails();
1613 : m_breakReason.swap(currentScheduledReason);
1614 55 : if (!m_breakReason.empty()) {
1615 10 : m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1616 55 : }
1617 : }
1618 :
1619 80 : void V8DebuggerAgentImpl::setBreakpointFor(v8::Local<v8::Function> function,
1620 : v8::Local<v8::String> condition,
1621 : BreakpointSource source) {
1622 : String16 breakpointId = generateBreakpointId(
1623 : source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1624 : : BreakpointType::kMonitorCommand,
1625 80 : function);
1626 80 : if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
1627 : m_breakpointIdToDebuggerBreakpointIds.end()) {
1628 80 : return;
1629 : }
1630 80 : setBreakpointImpl(breakpointId, function, condition);
1631 : }
1632 :
1633 70 : void V8DebuggerAgentImpl::removeBreakpointFor(v8::Local<v8::Function> function,
1634 : BreakpointSource source) {
1635 : String16 breakpointId = generateBreakpointId(
1636 : source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1637 : : BreakpointType::kMonitorCommand,
1638 70 : function);
1639 70 : removeBreakpointImpl(breakpointId);
1640 70 : }
1641 :
1642 5 : void V8DebuggerAgentImpl::reset() {
1643 10 : if (!enabled()) return;
1644 : m_blackboxedPositions.clear();
1645 : resetBlackboxedStateCache();
1646 : m_scripts.clear();
1647 : m_breakpointIdToDebuggerBreakpointIds.clear();
1648 : }
1649 :
1650 5141 : void V8DebuggerAgentImpl::cleanupOldFailedToParseAnonymousScriptsIfNeeded() {
1651 5141 : if (m_failedToParseAnonymousScriptIds.size() <=
1652 : kMaxScriptFailedToParseScripts)
1653 5141 : return;
1654 : static_assert(kMaxScriptFailedToParseScripts > 100,
1655 : "kMaxScriptFailedToParseScripts should be greater then 100");
1656 505 : while (m_failedToParseAnonymousScriptIds.size() >
1657 : kMaxScriptFailedToParseScripts - 100 + 1) {
1658 500 : String16 scriptId = m_failedToParseAnonymousScriptIds.front();
1659 500 : m_failedToParseAnonymousScriptIds.pop_front();
1660 : m_scripts.erase(scriptId);
1661 : }
1662 : }
1663 :
1664 : } // namespace v8_inspector
|