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