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