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