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