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