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