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