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
32namespace v8_inspector {
33
34using protocol::Array;
35using protocol::Maybe;
36using protocol::Debugger::BreakpointId;
37using protocol::Debugger::CallFrame;
38using protocol::Debugger::Scope;
39using protocol::Runtime::ExceptionDetails;
40using protocol::Runtime::RemoteObject;
41using protocol::Runtime::ScriptId;
42
43namespace InstrumentationEnum =
44    protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum;
45
46namespace DebuggerAgentState {
47static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
48static const char asyncCallStackDepth[] = "asyncCallStackDepth";
49static const char blackboxPattern[] = "blackboxPattern";
50static const char debuggerEnabled[] = "debuggerEnabled";
51static const char skipAllPauses[] = "skipAllPauses";
52
53static const char breakpointsByRegex[] = "breakpointsByRegex";
54static const char breakpointsByUrl[] = "breakpointsByUrl";
55static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
56static const char breakpointHints[] = "breakpointHints";
57static const char instrumentationBreakpoints[] = "instrumentationBreakpoints";
58
59}  // namespace DebuggerAgentState
60
61static const char kBacktraceObjectGroup[] = "backtrace";
62static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
63static const char kDebuggerNotPaused[] =
64    "Can only perform operation while paused.";
65
66static const size_t kBreakpointHintMaxLength = 128;
67static 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).
70static 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.
80static constexpr size_t kWasmBytecodeMaxLength =
81    (v8::String::kMaxLength / 4) * 3;
82static constexpr const char kWasmBytecodeExceedsTransferLimit[] =
83    "WebAssembly bytecode exceeds the transfer limit";
84#endif  // V8_ENABLE_WEBASSEMBLY
85
86namespace {
87
88enum class BreakpointType {
89  kByUrl = 1,
90  kByUrlRegex,
91  kByScriptHash,
92  kByScriptId,
93  kDebugCommand,
94  kMonitorCommand,
95  kBreakpointAtEntry,
96  kInstrumentationBreakpoint
97};
98
99String16 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
113String16 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
122String16 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
131bool 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
175bool 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
181String16 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
195void 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
239String16 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
253String16 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
279Response 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
324protocol::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
335Response 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
343Response 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
355bool 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
367V8DebuggerAgentImpl::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
378V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default;
379
380void 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
401Response 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
416Response 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
456void 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
484Response 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
496Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
497  m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
498  m_skipAllPauses = skip;
499  return Response::Success();
500}
501
502static 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
521Response 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
611Response 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
635Response 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
662Response 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
677Response 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
735void 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
758Response 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
823Response 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
845Response 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
875bool 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
909bool 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
936bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
937  return enabled() && (isOOMBreak || !m_skipAllPauses);
938}
939
940std::unique_ptr<protocol::Debugger::Location>
941V8DebuggerAgentImpl::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
986void 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
999Response 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
1015Response 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
1062Response 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
1070Response 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
1102Response 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
1123void 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
1129void V8DebuggerAgentImpl::popBreakDetails() {
1130  if (m_breakReason.empty()) return;
1131  m_breakReason.pop_back();
1132}
1133
1134void V8DebuggerAgentImpl::clearBreakDetails() {
1135  std::vector<BreakReason> emptyBreakReason;
1136  m_breakReason.swap(emptyBreakReason);
1137}
1138
1139void 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
1149void 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
1157Response 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
1171Response 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
1179Response 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
1195Response 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
1213Response 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
1220Response V8DebuggerAgentImpl::pauseOnAsyncCall(
1221    std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1222  // Deprecated, just return OK.
1223  return Response::Success();
1224}
1225
1226Response 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
1244void 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
1252Response 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
1294Response 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
1330Response 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
1354Response 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
1363Response 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
1388Response 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
1398void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
1399  for (const auto& it : m_scripts) {
1400    it.second->resetBlackboxedStateCache();
1401  }
1402}
1403
1404Response 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
1436Response 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
1519std::unique_ptr<protocol::Runtime::StackTrace>
1520V8DebuggerAgentImpl::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
1528std::unique_ptr<protocol::Runtime::StackTraceId>
1529V8DebuggerAgentImpl::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
1539bool V8DebuggerAgentImpl::isPaused() const {
1540  return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
1541}
1542
1543static 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
1553static 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
1570static 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
1587void 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
1724void 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
1751void 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
1786void 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
1896void V8DebuggerAgentImpl::didContinue() {
1897  m_frontend.resumed();
1898  m_frontend.flush();
1899}
1900
1901void 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
1924void 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
1938void 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
1948void 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
1958void 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
1982Response 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