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