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