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/java-script-call-frame.h"
13 : #include "src/inspector/protocol/Protocol.h"
14 : #include "src/inspector/remote-object-id.h"
15 : #include "src/inspector/script-breakpoint.h"
16 : #include "src/inspector/search-util.h"
17 : #include "src/inspector/string-util.h"
18 : #include "src/inspector/v8-debugger-script.h"
19 : #include "src/inspector/v8-debugger.h"
20 : #include "src/inspector/v8-inspector-impl.h"
21 : #include "src/inspector/v8-inspector-session-impl.h"
22 : #include "src/inspector/v8-regex.h"
23 : #include "src/inspector/v8-runtime-agent-impl.h"
24 : #include "src/inspector/v8-stack-trace-impl.h"
25 : #include "src/inspector/v8-value-copier.h"
26 :
27 : #include "include/v8-inspector.h"
28 :
29 : namespace v8_inspector {
30 :
31 : using protocol::Array;
32 : using protocol::Maybe;
33 : using protocol::Debugger::BreakpointId;
34 : using protocol::Debugger::CallFrame;
35 : using protocol::Runtime::ExceptionDetails;
36 : using protocol::Runtime::ScriptId;
37 : using protocol::Runtime::RemoteObject;
38 :
39 : namespace DebuggerAgentState {
40 : static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
41 : static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
42 : static const char asyncCallStackDepth[] = "asyncCallStackDepth";
43 : static const char blackboxPattern[] = "blackboxPattern";
44 : static const char debuggerEnabled[] = "debuggerEnabled";
45 : static const char skipAllPauses[] = "skipAllPauses";
46 :
47 : // Breakpoint properties.
48 : static const char url[] = "url";
49 : static const char isRegex[] = "isRegex";
50 : static const char lineNumber[] = "lineNumber";
51 : static const char columnNumber[] = "columnNumber";
52 : static const char condition[] = "condition";
53 : static const char hint[] = "hint";
54 :
55 : } // namespace DebuggerAgentState
56 :
57 : static const char kBacktraceObjectGroup[] = "backtrace";
58 : static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
59 : static const char kDebuggerNotPaused[] =
60 : "Can only perform operation while paused.";
61 :
62 : static const size_t kBreakpointHintMaxLength = 128;
63 : static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;
64 :
65 : namespace {
66 :
67 77210 : void TranslateWasmStackTraceLocations(Array<CallFrame>* stackTrace,
68 : WasmTranslation* wasmTranslation) {
69 268542 : for (size_t i = 0, e = stackTrace->length(); i != e; ++i) {
70 191332 : protocol::Debugger::Location* location = stackTrace->get(i)->getLocation();
71 : String16 scriptId = location->getScriptId();
72 191332 : int lineNumber = location->getLineNumber();
73 191332 : int columnNumber = location->getColumnNumber(-1);
74 :
75 191332 : if (!wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
76 191332 : &scriptId, &lineNumber, &columnNumber)) {
77 : continue;
78 : }
79 :
80 : location->setScriptId(std::move(scriptId));
81 334 : location->setLineNumber(lineNumber);
82 334 : location->setColumnNumber(columnNumber);
83 : }
84 77210 : }
85 :
86 2616 : String16 breakpointIdSuffix(V8DebuggerAgentImpl::BreakpointSource source) {
87 2616 : switch (source) {
88 : case V8DebuggerAgentImpl::UserBreakpointSource:
89 : break;
90 : case V8DebuggerAgentImpl::DebugCommandBreakpointSource:
91 66 : return ":debug";
92 : case V8DebuggerAgentImpl::MonitorCommandBreakpointSource:
93 60 : return ":monitor";
94 : }
95 : return String16();
96 : }
97 :
98 2616 : String16 generateBreakpointId(const ScriptBreakpoint& breakpoint,
99 : V8DebuggerAgentImpl::BreakpointSource source) {
100 2616 : String16Builder builder;
101 2616 : builder.append(breakpoint.script_id);
102 2616 : builder.append(':');
103 2616 : builder.appendNumber(breakpoint.line_number);
104 2616 : builder.append(':');
105 2616 : builder.appendNumber(breakpoint.column_number);
106 5232 : builder.append(breakpointIdSuffix(source));
107 5232 : return builder.toString();
108 : }
109 :
110 270 : bool positionComparator(const std::pair<int, int>& a,
111 : const std::pair<int, int>& b) {
112 270 : if (a.first != b.first) return a.first < b.first;
113 54 : return a.second < b.second;
114 : }
115 :
116 2422 : std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
117 : const String16& scriptId, int lineNumber, int columnNumber) {
118 : return protocol::Debugger::Location::create()
119 2422 : .setScriptId(scriptId)
120 : .setLineNumber(lineNumber)
121 : .setColumnNumber(columnNumber)
122 2422 : .build();
123 : }
124 :
125 132 : String16 breakpointHint(const V8DebuggerScript& script,
126 : const ScriptBreakpoint& breakpoint) {
127 132 : int offset = script.offset(breakpoint.line_number, breakpoint.column_number);
128 132 : if (offset == V8DebuggerScript::kNoOffset) return String16();
129 126 : const String16& source = script.source();
130 : String16 hint =
131 252 : source.substring(offset, kBreakpointHintMaxLength).stripWhiteSpace();
132 666 : for (size_t i = 0; i < hint.length(); ++i) {
133 318 : if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') {
134 48 : return hint.substring(0, i);
135 : }
136 : }
137 : return hint;
138 : }
139 :
140 2422 : void adjustBreakpointLocation(const V8DebuggerScript& script,
141 : const String16& hint,
142 : ScriptBreakpoint* breakpoint) {
143 4814 : if (hint.isEmpty()) return;
144 : intptr_t sourceOffset =
145 36 : script.offset(breakpoint->line_number, breakpoint->column_number);
146 36 : if (sourceOffset == V8DebuggerScript::kNoOffset) return;
147 :
148 : intptr_t searchRegionOffset = std::max(
149 72 : sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
150 36 : size_t offset = sourceOffset - searchRegionOffset;
151 36 : String16 searchArea = script.source().substring(
152 72 : searchRegionOffset, offset + kBreakpointHintMaxSearchOffset);
153 :
154 : size_t nextMatch = searchArea.find(hint, offset);
155 : size_t prevMatch = searchArea.reverseFind(hint, offset);
156 36 : if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
157 : return;
158 : }
159 : size_t bestMatch;
160 30 : if (nextMatch == String16::kNotFound) {
161 : bestMatch = prevMatch;
162 30 : } else if (prevMatch == String16::kNotFound) {
163 : bestMatch = nextMatch;
164 : } else {
165 24 : bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
166 : }
167 30 : bestMatch += searchRegionOffset;
168 : v8::debug::Location hintPosition =
169 30 : script.location(static_cast<int>(bestMatch));
170 30 : if (hintPosition.IsEmpty()) return;
171 30 : breakpoint->line_number = hintPosition.GetLineNumber();
172 30 : breakpoint->column_number = hintPosition.GetColumnNumber();
173 : }
174 :
175 1650 : String16 breakLocationType(v8::debug::BreakLocationType type) {
176 1650 : switch (type) {
177 : case v8::debug::kCallBreakLocation:
178 804 : return protocol::Debugger::BreakLocation::TypeEnum::Call;
179 : case v8::debug::kReturnBreakLocation:
180 834 : return protocol::Debugger::BreakLocation::TypeEnum::Return;
181 : case v8::debug::kDebuggerStatementBreakLocation:
182 12 : return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
183 : case v8::debug::kCommonBreakLocation:
184 : return String16();
185 : }
186 : return String16();
187 : }
188 :
189 : } // namespace
190 :
191 4573 : V8DebuggerAgentImpl::V8DebuggerAgentImpl(
192 4573 : V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
193 : protocol::DictionaryValue* state)
194 : : m_inspector(session->inspector()),
195 4573 : m_debugger(m_inspector->debugger()),
196 : m_session(session),
197 : m_enabled(false),
198 : m_state(state),
199 : m_frontend(frontendChannel),
200 41157 : m_isolate(m_inspector->isolate()) {}
201 :
202 22865 : V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
203 :
204 4339 : void V8DebuggerAgentImpl::enableImpl() {
205 4339 : m_enabled = true;
206 8678 : m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
207 4339 : m_debugger->enable();
208 :
209 : std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts;
210 4339 : m_debugger->getCompiledScripts(m_session->contextGroupId(), compiledScripts);
211 32710 : for (size_t i = 0; i < compiledScripts.size(); i++)
212 24032 : didParseSource(std::move(compiledScripts[i]), true);
213 :
214 : // FIXME(WK44513): breakpoints activated flag should be synchronized between
215 : // all front-ends
216 4339 : m_debugger->setBreakpointsActivated(true);
217 4339 : }
218 :
219 215346 : bool V8DebuggerAgentImpl::enabled() { return m_enabled; }
220 :
221 4341 : Response V8DebuggerAgentImpl::enable() {
222 4341 : if (enabled()) return Response::OK();
223 :
224 4327 : if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
225 0 : return Response::Error("Script execution is prohibited");
226 :
227 4327 : enableImpl();
228 4327 : return Response::OK();
229 : }
230 :
231 5423 : Response V8DebuggerAgentImpl::disable() {
232 5423 : if (!enabled()) return Response::OK();
233 :
234 : m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
235 13017 : protocol::DictionaryValue::create());
236 : m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
237 8678 : v8::debug::NoBreakOnException);
238 8678 : m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
239 :
240 4339 : if (isPaused()) m_debugger->continueProgram(m_session->contextGroupId());
241 4339 : m_debugger->disable();
242 : JavaScriptCallFrames emptyCallFrames;
243 : m_pausedCallFrames.swap(emptyCallFrames);
244 : m_blackboxedPositions.clear();
245 : m_blackboxPattern.reset();
246 : resetBlackboxedStateCache();
247 : m_scripts.clear();
248 : m_breakpointIdToDebuggerBreakpointIds.clear();
249 4339 : m_debugger->setAsyncCallStackDepth(this, 0);
250 : m_continueToLocationBreakpointId = String16();
251 4339 : clearBreakDetails();
252 4339 : m_skipAllPauses = false;
253 8678 : m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
254 8678 : m_state->remove(DebuggerAgentState::blackboxPattern);
255 4339 : m_enabled = false;
256 8678 : m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
257 4339 : return Response::OK();
258 : }
259 :
260 36 : void V8DebuggerAgentImpl::restore() {
261 : DCHECK(!m_enabled);
262 72 : if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
263 24 : return;
264 12 : if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
265 : return;
266 :
267 12 : enableImpl();
268 :
269 12 : int pauseState = v8::debug::NoBreakOnException;
270 24 : m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
271 12 : setPauseOnExceptionsImpl(pauseState);
272 :
273 : m_skipAllPauses =
274 24 : m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);
275 :
276 12 : int asyncCallStackDepth = 0;
277 : m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
278 24 : &asyncCallStackDepth);
279 12 : m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);
280 :
281 : String16 blackboxPattern;
282 24 : if (m_state->getString(DebuggerAgentState::blackboxPattern,
283 24 : &blackboxPattern)) {
284 0 : setBlackboxPattern(blackboxPattern);
285 : }
286 : }
287 :
288 180 : Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
289 180 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
290 180 : m_debugger->setBreakpointsActivated(active);
291 298 : if (!active && !m_breakReason.empty()) {
292 6 : clearBreakDetails();
293 6 : m_debugger->setPauseOnNextStatement(false, m_session->contextGroupId());
294 : }
295 180 : return Response::OK();
296 : }
297 :
298 0 : Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
299 0 : m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
300 0 : m_skipAllPauses = skip;
301 0 : return Response::OK();
302 : }
303 :
304 : static std::unique_ptr<protocol::DictionaryValue>
305 60 : buildObjectForBreakpointCookie(const String16& url, int lineNumber,
306 : int columnNumber, const String16& condition,
307 : bool isRegex, const String16& hint) {
308 : std::unique_ptr<protocol::DictionaryValue> breakpointObject =
309 60 : protocol::DictionaryValue::create();
310 180 : breakpointObject->setString(DebuggerAgentState::url, url);
311 180 : breakpointObject->setInteger(DebuggerAgentState::lineNumber, lineNumber);
312 180 : breakpointObject->setInteger(DebuggerAgentState::columnNumber, columnNumber);
313 180 : breakpointObject->setString(DebuggerAgentState::condition, condition);
314 180 : breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex);
315 60 : if (!hint.isEmpty()) {
316 18 : breakpointObject->setString(DebuggerAgentState::hint, hint);
317 : }
318 60 : return breakpointObject;
319 : }
320 :
321 654 : static bool matches(V8InspectorImpl* inspector, const String16& url,
322 : const String16& pattern, bool isRegex) {
323 654 : if (isRegex) {
324 0 : V8Regex regex(inspector, pattern, true);
325 0 : return regex.match(url) != -1;
326 : }
327 654 : return url == pattern;
328 : }
329 :
330 60 : Response V8DebuggerAgentImpl::setBreakpointByUrl(
331 : int lineNumber, Maybe<String16> optionalURL,
332 : Maybe<String16> optionalURLRegex, Maybe<int> optionalColumnNumber,
333 : Maybe<String16> optionalCondition, String16* outBreakpointId,
334 : std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
335 : *locations = Array<protocol::Debugger::Location>::create();
336 120 : if (optionalURL.isJust() == optionalURLRegex.isJust())
337 0 : return Response::Error("Either url or urlRegex must be specified.");
338 :
339 : String16 url = optionalURL.isJust() ? optionalURL.fromJust()
340 60 : : optionalURLRegex.fromJust();
341 : int columnNumber = 0;
342 60 : if (optionalColumnNumber.isJust()) {
343 : columnNumber = optionalColumnNumber.fromJust();
344 36 : if (columnNumber < 0) return Response::Error("Incorrect column number");
345 : }
346 120 : String16 condition = optionalCondition.fromMaybe("");
347 : bool isRegex = optionalURLRegex.isJust();
348 :
349 420 : String16 breakpointId = (isRegex ? "/" + url + "/" : url) + ":" +
350 240 : String16::fromInteger(lineNumber) + ":" +
351 180 : String16::fromInteger(columnNumber);
352 : protocol::DictionaryValue* breakpointsCookie =
353 120 : m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
354 60 : if (!breakpointsCookie) {
355 : std::unique_ptr<protocol::DictionaryValue> newValue =
356 30 : protocol::DictionaryValue::create();
357 : breakpointsCookie = newValue.get();
358 : m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
359 90 : std::move(newValue));
360 : }
361 60 : if (breakpointsCookie->get(breakpointId))
362 0 : return Response::Error("Breakpoint at specified location already exists.");
363 :
364 : String16 hint;
365 : ScriptBreakpoint breakpoint(String16(), lineNumber, columnNumber, condition);
366 414 : for (const auto& script : m_scripts) {
367 588 : if (!matches(m_inspector, script.second->sourceURL(), url, isRegex))
368 240 : continue;
369 : breakpoint.script_id = script.first;
370 : std::unique_ptr<protocol::Debugger::Location> location =
371 54 : resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource, hint);
372 162 : if (!isRegex) hint = breakpointHint(*script.second, breakpoint);
373 72 : if (location) (*locations)->addItem(std::move(location));
374 : }
375 :
376 : breakpointsCookie->setObject(
377 : breakpointId,
378 : buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition,
379 120 : isRegex, hint));
380 :
381 : *outBreakpointId = breakpointId;
382 60 : return Response::OK();
383 : }
384 :
385 2490 : Response V8DebuggerAgentImpl::setBreakpoint(
386 : std::unique_ptr<protocol::Debugger::Location> location,
387 : Maybe<String16> optionalCondition, String16* outBreakpointId,
388 : std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
389 : ScriptBreakpoint breakpoint(
390 : location->getScriptId(), location->getLineNumber(),
391 4980 : location->getColumnNumber(0), optionalCondition.fromMaybe(String16()));
392 :
393 : String16 breakpointId =
394 2490 : generateBreakpointId(breakpoint, UserBreakpointSource);
395 2490 : if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
396 : m_breakpointIdToDebuggerBreakpointIds.end()) {
397 448 : return Response::Error("Breakpoint at specified location already exists.");
398 : }
399 6798 : *actualLocation = resolveBreakpoint(
400 : breakpointId, breakpoint, UserBreakpointSource, /* hint */ String16());
401 2266 : if (!*actualLocation) return Response::Error("Could not resolve breakpoint");
402 : *outBreakpointId = breakpointId;
403 2266 : return Response::OK();
404 : }
405 :
406 726 : Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
407 726 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
408 : protocol::DictionaryValue* breakpointsCookie =
409 1452 : m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
410 726 : if (breakpointsCookie) breakpointsCookie->remove(breakpointId);
411 726 : removeBreakpointImpl(breakpointId);
412 726 : return Response::OK();
413 : }
414 :
415 786 : void V8DebuggerAgentImpl::removeBreakpointImpl(const String16& breakpointId) {
416 : DCHECK(enabled());
417 : BreakpointIdToDebuggerBreakpointIdsMap::iterator
418 : debuggerBreakpointIdsIterator =
419 : m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
420 786 : if (debuggerBreakpointIdsIterator ==
421 : m_breakpointIdToDebuggerBreakpointIds.end())
422 786 : return;
423 1572 : const std::vector<String16>& ids = debuggerBreakpointIdsIterator->second;
424 2358 : for (size_t i = 0; i < ids.size(); ++i) {
425 : const String16& debuggerBreakpointId = ids[i];
426 :
427 786 : m_debugger->removeBreakpoint(debuggerBreakpointId);
428 : m_serverBreakpoints.erase(debuggerBreakpointId);
429 : }
430 : m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
431 : }
432 :
433 264 : Response V8DebuggerAgentImpl::getPossibleBreakpoints(
434 : std::unique_ptr<protocol::Debugger::Location> start,
435 : Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
436 : std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
437 : locations) {
438 : String16 scriptId = start->getScriptId();
439 :
440 528 : if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
441 : return Response::Error(
442 0 : "start.lineNumber and start.columnNumber should be >= 0");
443 :
444 : v8::debug::Location v8Start(start->getLineNumber(),
445 264 : start->getColumnNumber(0));
446 264 : v8::debug::Location v8End;
447 264 : if (end.isJust()) {
448 132 : if (end.fromJust()->getScriptId() != scriptId)
449 12 : return Response::Error("Locations should contain the same scriptId");
450 126 : int line = end.fromJust()->getLineNumber();
451 : int column = end.fromJust()->getColumnNumber(0);
452 126 : if (line < 0 || column < 0)
453 : return Response::Error(
454 0 : "end.lineNumber and end.columnNumber should be >= 0");
455 126 : v8End = v8::debug::Location(line, column);
456 : }
457 : auto it = m_scripts.find(scriptId);
458 264 : if (it == m_scripts.end()) return Response::Error("Script not found");
459 :
460 : std::vector<v8::debug::BreakLocation> v8Locations;
461 252 : if (!it->second->getPossibleBreakpoints(
462 504 : v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations)) {
463 0 : return Response::InternalError();
464 : }
465 :
466 : *locations = protocol::Array<protocol::Debugger::BreakLocation>::create();
467 5928 : for (size_t i = 0; i < v8Locations.size(); ++i) {
468 : std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
469 : protocol::Debugger::BreakLocation::create()
470 2838 : .setScriptId(scriptId)
471 5676 : .setLineNumber(v8Locations[i].GetLineNumber())
472 2838 : .setColumnNumber(v8Locations[i].GetColumnNumber())
473 : .build();
474 5676 : if (v8Locations[i].type() != v8::debug::kCommonBreakLocation) {
475 3300 : breakLocation->setType(breakLocationType(v8Locations[i].type()));
476 : }
477 2838 : (*locations)->addItem(std::move(breakLocation));
478 : }
479 252 : return Response::OK();
480 : }
481 :
482 36 : Response V8DebuggerAgentImpl::continueToLocation(
483 : std::unique_ptr<protocol::Debugger::Location> location) {
484 36 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
485 36 : if (!m_continueToLocationBreakpointId.isEmpty()) {
486 0 : m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
487 0 : m_continueToLocationBreakpointId = "";
488 : }
489 :
490 : ScriptBreakpoint breakpoint(location->getScriptId(),
491 : location->getLineNumber(),
492 36 : location->getColumnNumber(0), String16());
493 :
494 72 : m_continueToLocationBreakpointId = m_debugger->setBreakpoint(
495 : breakpoint, &breakpoint.line_number, &breakpoint.column_number);
496 : // TODO(kozyatinskiy): Return actual line and column number.
497 36 : return resume();
498 : }
499 :
500 16862 : bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
501 : const v8::debug::Location& start,
502 : const v8::debug::Location& end) {
503 : ScriptsMap::iterator it = m_scripts.find(scriptId);
504 16862 : if (it == m_scripts.end()) {
505 : // Unknown scripts are blackboxed.
506 : return true;
507 : }
508 16822 : if (m_blackboxPattern) {
509 766 : const String16& scriptSourceURL = it->second->sourceURL();
510 1169 : if (!scriptSourceURL.isEmpty() &&
511 403 : m_blackboxPattern->match(scriptSourceURL) != -1)
512 : return true;
513 : }
514 : auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
515 16631 : if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
516 :
517 378 : const std::vector<std::pair<int, int>>& ranges =
518 : itBlackboxedPositions->second;
519 : auto itStartRange = std::lower_bound(
520 : ranges.begin(), ranges.end(),
521 132 : std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
522 264 : positionComparator);
523 : auto itEndRange = std::lower_bound(
524 : itStartRange, ranges.end(),
525 132 : std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
526 264 : positionComparator);
527 : // Ranges array contains positions in script where blackbox state is changed.
528 : // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
529 : // blackboxed...
530 246 : return itStartRange == itEndRange &&
531 132 : std::distance(ranges.begin(), itStartRange) % 2;
532 : }
533 :
534 : std::unique_ptr<protocol::Debugger::Location>
535 2500 : V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
536 : const ScriptBreakpoint& breakpoint,
537 : BreakpointSource source,
538 : const String16& hint) {
539 2500 : v8::HandleScope handles(m_isolate);
540 : DCHECK(enabled());
541 : // FIXME: remove these checks once crbug.com/520702 is resolved.
542 2500 : CHECK(!breakpointId.isEmpty());
543 2500 : CHECK(!breakpoint.script_id.isEmpty());
544 2500 : ScriptsMap::iterator scriptIterator = m_scripts.find(breakpoint.script_id);
545 2500 : if (scriptIterator == m_scripts.end()) return nullptr;
546 7500 : if (breakpoint.line_number < scriptIterator->second->startLine() ||
547 2500 : scriptIterator->second->endLine() < breakpoint.line_number)
548 : return nullptr;
549 :
550 : // Translate from protocol location to v8 location for the debugger.
551 2422 : ScriptBreakpoint translatedBreakpoint = breakpoint;
552 : adjustBreakpointLocation(*scriptIterator->second, hint,
553 2422 : &translatedBreakpoint);
554 : m_debugger->wasmTranslation()->TranslateProtocolLocationToWasmScriptLocation(
555 : &translatedBreakpoint.script_id, &translatedBreakpoint.line_number,
556 2422 : &translatedBreakpoint.column_number);
557 :
558 : int actualLineNumber;
559 : int actualColumnNumber;
560 : String16 debuggerBreakpointId = m_debugger->setBreakpoint(
561 2422 : translatedBreakpoint, &actualLineNumber, &actualColumnNumber);
562 2422 : if (debuggerBreakpointId.isEmpty()) return nullptr;
563 :
564 : // Translate back from v8 location to protocol location for the return value.
565 : m_debugger->wasmTranslation()->TranslateWasmScriptLocationToProtocolLocation(
566 2422 : &translatedBreakpoint.script_id, &actualLineNumber, &actualColumnNumber);
567 :
568 : m_serverBreakpoints[debuggerBreakpointId] =
569 : std::make_pair(breakpointId, source);
570 2422 : CHECK(!breakpointId.isEmpty());
571 :
572 : m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
573 2422 : debuggerBreakpointId);
574 : return buildProtocolLocation(translatedBreakpoint.script_id, actualLineNumber,
575 4922 : actualColumnNumber);
576 : }
577 :
578 0 : Response V8DebuggerAgentImpl::searchInContent(
579 : const String16& scriptId, const String16& query,
580 : Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
581 : std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
582 0 : v8::HandleScope handles(m_isolate);
583 : ScriptsMap::iterator it = m_scripts.find(scriptId);
584 0 : if (it == m_scripts.end())
585 0 : return Response::Error("No script for id: " + scriptId);
586 :
587 : std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
588 0 : searchInTextByLinesImpl(m_session, it->second->source(), query,
589 : optionalCaseSensitive.fromMaybe(false),
590 0 : optionalIsRegex.fromMaybe(false));
591 : *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
592 0 : for (size_t i = 0; i < matches.size(); ++i)
593 0 : (*results)->addItem(std::move(matches[i]));
594 0 : return Response::OK();
595 : }
596 :
597 30 : Response V8DebuggerAgentImpl::setScriptSource(
598 : const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
599 : Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
600 : Maybe<bool>* stackChanged,
601 : Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
602 : Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
603 30 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
604 :
605 : ScriptsMap::iterator it = m_scripts.find(scriptId);
606 30 : if (it == m_scripts.end()) {
607 12 : return Response::Error("No script with given id found");
608 : }
609 24 : if (it->second->isModule()) {
610 : // TODO(kozyatinskiy): LiveEdit should support ES6 module
611 12 : return Response::Error("Editing module's script is not supported.");
612 : }
613 :
614 18 : v8::HandleScope handles(m_isolate);
615 18 : v8::Local<v8::String> newSource = toV8String(m_isolate, newContent);
616 18 : bool compileError = false;
617 : Response response = m_debugger->setScriptSource(
618 : scriptId, newSource, dryRun.fromMaybe(false), optOutCompileError,
619 36 : &m_pausedCallFrames, stackChanged, &compileError);
620 18 : if (!response.isSuccess() || compileError) return response;
621 :
622 6 : it->second->setSource(newContent);
623 6 : std::unique_ptr<Array<CallFrame>> callFrames;
624 12 : response = currentCallFrames(&callFrames);
625 6 : if (!response.isSuccess()) return response;
626 : *newCallFrames = std::move(callFrames);
627 12 : *asyncStackTrace = currentAsyncStackTrace();
628 24 : return Response::OK();
629 : }
630 :
631 160 : Response V8DebuggerAgentImpl::restartFrame(
632 : const String16& callFrameId,
633 : std::unique_ptr<Array<CallFrame>>* newCallFrames,
634 : Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) {
635 166 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
636 : InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
637 154 : callFrameId);
638 154 : Response response = scope.initialize();
639 154 : if (!response.isSuccess()) return response;
640 308 : if (scope.frameOrdinal() >= m_pausedCallFrames.size())
641 0 : return Response::Error("Could not find call frame with given id");
642 :
643 : v8::Local<v8::Value> resultValue;
644 : v8::Local<v8::Boolean> result;
645 154 : if (!m_pausedCallFrames[scope.frameOrdinal()]->restart().ToLocal(
646 462 : &resultValue) ||
647 308 : scope.tryCatch().HasCaught() ||
648 462 : !resultValue->ToBoolean(scope.context()).ToLocal(&result) ||
649 154 : !result->Value()) {
650 0 : return Response::InternalError();
651 : }
652 308 : JavaScriptCallFrames frames = m_debugger->currentCallFrames();
653 : m_pausedCallFrames.swap(frames);
654 :
655 308 : response = currentCallFrames(newCallFrames);
656 154 : if (!response.isSuccess()) return response;
657 308 : *asyncStackTrace = currentAsyncStackTrace();
658 308 : return Response::OK();
659 : }
660 :
661 854 : Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
662 : String16* scriptSource) {
663 854 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
664 : ScriptsMap::iterator it = m_scripts.find(scriptId);
665 854 : if (it == m_scripts.end())
666 0 : return Response::Error("No script for id: " + scriptId);
667 : *scriptSource = it->second->source();
668 854 : return Response::OK();
669 : }
670 :
671 345 : void V8DebuggerAgentImpl::pushBreakDetails(
672 : const String16& breakReason,
673 : std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
674 690 : m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
675 345 : }
676 :
677 0 : void V8DebuggerAgentImpl::popBreakDetails() {
678 111 : if (m_breakReason.empty()) return;
679 : m_breakReason.pop_back();
680 : }
681 :
682 158380 : void V8DebuggerAgentImpl::clearBreakDetails() {
683 : std::vector<BreakReason> emptyBreakReason;
684 158380 : m_breakReason.swap(emptyBreakReason);
685 158380 : }
686 :
687 114 : void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
688 : const String16& breakReason,
689 : std::unique_ptr<protocol::DictionaryValue> data) {
690 342 : if (!enabled() || isPaused() || !m_debugger->breakpointsActivated()) return;
691 111 : if (m_breakReason.empty()) {
692 87 : m_debugger->setPauseOnNextStatement(true, m_session->contextGroupId());
693 : }
694 222 : pushBreakDetails(breakReason, std::move(data));
695 : }
696 :
697 54 : void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
698 162 : if (!enabled() || isPaused() || !m_debugger->breakpointsActivated()) return;
699 102 : if (m_breakReason.size() == 1) {
700 15 : m_debugger->setPauseOnNextStatement(false, m_session->contextGroupId());
701 : }
702 : popBreakDetails();
703 : }
704 :
705 168 : Response V8DebuggerAgentImpl::pause() {
706 168 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
707 168 : if (isPaused()) return Response::OK();
708 168 : if (m_breakReason.empty()) {
709 162 : m_debugger->setPauseOnNextStatement(true, m_session->contextGroupId());
710 : }
711 504 : pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
712 168 : return Response::OK();
713 : }
714 :
715 1644 : Response V8DebuggerAgentImpl::resume() {
716 1654 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
717 4902 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
718 3268 : m_debugger->continueProgram(m_session->contextGroupId());
719 1634 : return Response::OK();
720 : }
721 :
722 11616 : Response V8DebuggerAgentImpl::stepOver() {
723 11616 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
724 34848 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
725 23232 : m_debugger->stepOverStatement(m_session->contextGroupId());
726 11616 : return Response::OK();
727 : }
728 :
729 50590 : Response V8DebuggerAgentImpl::stepInto() {
730 50591 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
731 151767 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
732 101178 : m_debugger->stepIntoStatement(m_session->contextGroupId());
733 50589 : return Response::OK();
734 : }
735 :
736 661 : Response V8DebuggerAgentImpl::stepOut() {
737 661 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
738 1983 : m_session->releaseObjectGroup(kBacktraceObjectGroup);
739 1322 : m_debugger->stepOutOfFunction(m_session->contextGroupId());
740 661 : return Response::OK();
741 : }
742 :
743 90 : void V8DebuggerAgentImpl::scheduleStepIntoAsync(
744 : std::unique_ptr<ScheduleStepIntoAsyncCallback> callback) {
745 90 : if (!isPaused()) {
746 0 : callback->sendFailure(Response::Error(kDebuggerNotPaused));
747 90 : return;
748 : }
749 : m_debugger->scheduleStepIntoAsync(std::move(callback),
750 270 : m_session->contextGroupId());
751 : }
752 :
753 7088 : Response V8DebuggerAgentImpl::setPauseOnExceptions(
754 : const String16& stringPauseState) {
755 7088 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
756 : v8::debug::ExceptionBreakState pauseState;
757 14176 : if (stringPauseState == "none") {
758 : pauseState = v8::debug::NoBreakOnException;
759 10952 : } else if (stringPauseState == "all") {
760 : pauseState = v8::debug::BreakOnAnyException;
761 7096 : } else if (stringPauseState == "uncaught") {
762 : pauseState = v8::debug::BreakOnUncaughtException;
763 : } else {
764 0 : return Response::Error("Unknown pause on exceptions mode: " +
765 0 : stringPauseState);
766 : }
767 7088 : setPauseOnExceptionsImpl(pauseState);
768 7088 : return Response::OK();
769 : }
770 :
771 7100 : void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
772 : m_debugger->setPauseOnExceptionsState(
773 7100 : static_cast<v8::debug::ExceptionBreakState>(pauseState));
774 14200 : m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
775 7100 : }
776 :
777 14240 : Response V8DebuggerAgentImpl::evaluateOnCallFrame(
778 : const String16& callFrameId, const String16& expression,
779 : Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
780 : Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
781 : Maybe<bool> throwOnSideEffect, std::unique_ptr<RemoteObject>* result,
782 : Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
783 14246 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
784 : InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
785 14234 : callFrameId);
786 14234 : Response response = scope.initialize();
787 14234 : if (!response.isSuccess()) return response;
788 42702 : if (scope.frameOrdinal() >= m_pausedCallFrames.size())
789 0 : return Response::Error("Could not find call frame with given id");
790 :
791 14234 : if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
792 14234 : if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
793 :
794 : v8::MaybeLocal<v8::Value> maybeResultValue =
795 14234 : m_pausedCallFrames[scope.frameOrdinal()]->evaluate(
796 : toV8String(m_isolate, expression),
797 28468 : throwOnSideEffect.fromMaybe(false));
798 :
799 : // Re-initialize after running client's code, as it could have destroyed
800 : // context or session.
801 28468 : response = scope.initialize();
802 14234 : if (!response.isSuccess()) return response;
803 : return scope.injectedScript()->wrapEvaluateResult(
804 : maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""),
805 : returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), result,
806 85404 : exceptionDetails);
807 : }
808 :
809 176 : Response V8DebuggerAgentImpl::setVariableValue(
810 : int scopeNumber, const String16& variableName,
811 : std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
812 : const String16& callFrameId) {
813 176 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
814 182 : if (!isPaused()) return Response::Error(kDebuggerNotPaused);
815 : InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
816 170 : callFrameId);
817 170 : Response response = scope.initialize();
818 170 : if (!response.isSuccess()) return response;
819 : v8::Local<v8::Value> newValue;
820 340 : response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
821 : &newValue);
822 170 : if (!response.isSuccess()) return response;
823 :
824 510 : if (scope.frameOrdinal() >= m_pausedCallFrames.size())
825 0 : return Response::Error("Could not find call frame with given id");
826 : v8::MaybeLocal<v8::Value> result =
827 170 : m_pausedCallFrames[scope.frameOrdinal()]->setVariableValue(
828 340 : scopeNumber, toV8String(m_isolate, variableName), newValue);
829 340 : if (scope.tryCatch().HasCaught() || result.IsEmpty())
830 8 : return Response::InternalError();
831 332 : return Response::OK();
832 : }
833 :
834 198 : Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
835 198 : if (!enabled()) return Response::Error(kDebuggerNotEnabled);
836 396 : m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
837 198 : m_debugger->setAsyncCallStackDepth(this, depth);
838 198 : return Response::OK();
839 : }
840 :
841 56 : Response V8DebuggerAgentImpl::setBlackboxPatterns(
842 : std::unique_ptr<protocol::Array<String16>> patterns) {
843 56 : if (!patterns->length()) {
844 : m_blackboxPattern = nullptr;
845 : resetBlackboxedStateCache();
846 0 : m_state->remove(DebuggerAgentState::blackboxPattern);
847 0 : return Response::OK();
848 : }
849 :
850 56 : String16Builder patternBuilder;
851 56 : patternBuilder.append('(');
852 114 : for (size_t i = 0; i < patterns->length() - 1; ++i) {
853 2 : patternBuilder.append(patterns->get(i));
854 2 : patternBuilder.append("|");
855 : }
856 112 : patternBuilder.append(patterns->get(patterns->length() - 1));
857 56 : patternBuilder.append(')');
858 56 : String16 pattern = patternBuilder.toString();
859 56 : Response response = setBlackboxPattern(pattern);
860 56 : if (!response.isSuccess()) return response;
861 : resetBlackboxedStateCache();
862 110 : m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
863 55 : return Response::OK();
864 : }
865 :
866 56 : Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
867 : std::unique_ptr<V8Regex> regex(new V8Regex(
868 56 : m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
869 56 : if (!regex->isValid())
870 2 : return Response::Error("Pattern parser error: " + regex->errorMessage());
871 : m_blackboxPattern = std::move(regex);
872 55 : return Response::OK();
873 : }
874 :
875 0 : void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
876 41194 : for (const auto& it : m_scripts) {
877 36800 : it.second->resetBlackboxedStateCache();
878 : }
879 0 : }
880 :
881 66 : Response V8DebuggerAgentImpl::setBlackboxedRanges(
882 : const String16& scriptId,
883 : std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
884 : inPositions) {
885 : auto it = m_scripts.find(scriptId);
886 66 : if (it == m_scripts.end())
887 0 : return Response::Error("No script with passed id.");
888 :
889 66 : if (!inPositions->length()) {
890 : m_blackboxedPositions.erase(scriptId);
891 0 : it->second->resetBlackboxedStateCache();
892 0 : return Response::OK();
893 : }
894 :
895 : std::vector<std::pair<int, int>> positions;
896 66 : positions.reserve(inPositions->length());
897 348 : for (size_t i = 0; i < inPositions->length(); ++i) {
898 228 : protocol::Debugger::ScriptPosition* position = inPositions->get(i);
899 114 : if (position->getLineNumber() < 0)
900 0 : return Response::Error("Position missing 'line' or 'line' < 0.");
901 114 : if (position->getColumnNumber() < 0)
902 12 : return Response::Error("Position missing 'column' or 'column' < 0.");
903 : positions.push_back(
904 216 : std::make_pair(position->getLineNumber(), position->getColumnNumber()));
905 : }
906 :
907 132 : for (size_t i = 1; i < positions.size(); ++i) {
908 144 : if (positions[i - 1].first < positions[i].first) continue;
909 24 : if (positions[i - 1].first == positions[i].first &&
910 12 : positions[i - 1].second < positions[i].second)
911 : continue;
912 : return Response::Error(
913 24 : "Input positions array is not sorted or contains duplicate values.");
914 : }
915 :
916 48 : m_blackboxedPositions[scriptId] = positions;
917 48 : it->second->resetBlackboxedStateCache();
918 48 : return Response::OK();
919 : }
920 :
921 77220 : Response V8DebuggerAgentImpl::currentCallFrames(
922 : std::unique_ptr<Array<CallFrame>>* result) {
923 77220 : if (!isPaused()) {
924 : *result = Array<CallFrame>::create();
925 6 : return Response::OK();
926 : }
927 77214 : v8::HandleScope handles(m_isolate);
928 : v8::Local<v8::Context> debuggerContext =
929 77214 : v8::debug::GetDebugContext(m_isolate);
930 : v8::Context::Scope contextScope(debuggerContext);
931 :
932 77214 : v8::Local<v8::Array> objects = v8::Array::New(m_isolate);
933 :
934 537260 : for (size_t frameOrdinal = 0; frameOrdinal < m_pausedCallFrames.size();
935 : ++frameOrdinal) {
936 : const std::unique_ptr<JavaScriptCallFrame>& currentCallFrame =
937 268630 : m_pausedCallFrames[frameOrdinal];
938 :
939 : v8::Local<v8::Object> details;
940 382840 : if (!currentCallFrame->details().ToLocal(&details))
941 6 : return Response::InternalError();
942 :
943 191418 : int contextId = currentCallFrame->contextId();
944 :
945 191418 : InjectedScript* injectedScript = nullptr;
946 382836 : if (contextId) m_session->findInjectedScript(contextId, injectedScript);
947 :
948 : String16 callFrameId =
949 191418 : RemoteCallFrameId::serialize(contextId, static_cast<int>(frameOrdinal));
950 191418 : if (!details
951 : ->Set(debuggerContext,
952 : toV8StringInternalized(m_isolate, "callFrameId"),
953 574254 : toV8String(m_isolate, callFrameId))
954 382836 : .FromMaybe(false)) {
955 0 : return Response::InternalError();
956 : }
957 :
958 191418 : if (injectedScript) {
959 : v8::Local<v8::Value> scopeChain;
960 191418 : if (!details
961 : ->Get(debuggerContext,
962 382836 : toV8StringInternalized(m_isolate, "scopeChain"))
963 574254 : .ToLocal(&scopeChain) ||
964 191418 : !scopeChain->IsArray()) {
965 2 : return Response::InternalError();
966 : }
967 191418 : v8::Local<v8::Array> scopeChainArray = scopeChain.As<v8::Array>();
968 : Response response = injectedScript->wrapPropertyInArray(
969 : scopeChainArray, toV8StringInternalized(m_isolate, "object"),
970 382836 : kBacktraceObjectGroup);
971 191418 : if (!response.isSuccess()) return response;
972 765672 : response = injectedScript->wrapObjectProperty(
973 : details, toV8StringInternalized(m_isolate, "this"),
974 : kBacktraceObjectGroup);
975 191418 : if (!response.isSuccess()) return response;
976 191416 : if (details
977 : ->Has(debuggerContext,
978 382832 : toV8StringInternalized(m_isolate, "returnValue"))
979 382832 : .FromMaybe(false)) {
980 34744 : response = injectedScript->wrapObjectProperty(
981 : details, toV8StringInternalized(m_isolate, "returnValue"),
982 : kBacktraceObjectGroup);
983 8686 : if (!response.isSuccess()) return response;
984 : }
985 : } else {
986 0 : if (!details
987 : ->Set(debuggerContext,
988 : toV8StringInternalized(m_isolate, "scopeChain"),
989 0 : v8::Array::New(m_isolate, 0))
990 0 : .FromMaybe(false)) {
991 0 : return Response::InternalError();
992 : }
993 0 : v8::Local<v8::Object> remoteObject = v8::Object::New(m_isolate);
994 0 : if (!remoteObject
995 : ->Set(debuggerContext, toV8StringInternalized(m_isolate, "type"),
996 0 : toV8StringInternalized(m_isolate, "undefined"))
997 0 : .FromMaybe(false)) {
998 0 : return Response::InternalError();
999 : }
1000 0 : if (!details
1001 : ->Set(debuggerContext, toV8StringInternalized(m_isolate, "this"),
1002 0 : remoteObject)
1003 0 : .FromMaybe(false)) {
1004 0 : return Response::InternalError();
1005 : }
1006 0 : if (!details
1007 : ->Delete(debuggerContext,
1008 0 : toV8StringInternalized(m_isolate, "returnValue"))
1009 0 : .FromMaybe(false)) {
1010 0 : return Response::InternalError();
1011 : }
1012 : }
1013 :
1014 382832 : if (!objects->Set(debuggerContext, static_cast<int>(frameOrdinal), details)
1015 382832 : .FromMaybe(false)) {
1016 0 : return Response::InternalError();
1017 : }
1018 : }
1019 :
1020 77210 : std::unique_ptr<protocol::Value> protocolValue;
1021 77210 : Response response = toProtocolValue(debuggerContext, objects, &protocolValue);
1022 77210 : if (!response.isSuccess()) return response;
1023 154420 : protocol::ErrorSupport errorSupport;
1024 154420 : *result = Array<CallFrame>::fromValue(protocolValue.get(), &errorSupport);
1025 77210 : if (!*result) return Response::Error(errorSupport.errors());
1026 : TranslateWasmStackTraceLocations(result->get(),
1027 77210 : m_debugger->wasmTranslation());
1028 154424 : return Response::OK();
1029 : }
1030 :
1031 : std::unique_ptr<protocol::Runtime::StackTrace>
1032 77220 : V8DebuggerAgentImpl::currentAsyncStackTrace() {
1033 : std::shared_ptr<AsyncStackTrace> asyncParent =
1034 77598 : m_debugger->currentAsyncParent();
1035 77220 : if (!asyncParent) return nullptr;
1036 : return asyncParent->buildInspectorObject(
1037 : m_debugger->currentAsyncCreation().get(),
1038 1134 : m_debugger->maxAsyncCallChainDepth() - 1);
1039 : }
1040 :
1041 161072 : bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
1042 :
1043 36676 : void V8DebuggerAgentImpl::didParseSource(
1044 : std::unique_ptr<V8DebuggerScript> script, bool success) {
1045 36676 : v8::HandleScope handles(m_isolate);
1046 : String16 scriptSource = script->source();
1047 37044 : if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
1048 36676 : if (!success)
1049 368 : script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
1050 :
1051 36676 : int contextId = script->executionContextId();
1052 36676 : int contextGroupId = m_inspector->contextGroupId(contextId);
1053 : InspectedContext* inspected =
1054 36676 : m_inspector->getContext(contextGroupId, contextId);
1055 : std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1056 36676 : if (inspected) {
1057 : // Script reused between different groups/sessions can have a stale
1058 : // execution context id.
1059 109710 : executionContextAuxData = protocol::DictionaryValue::cast(
1060 : protocol::StringUtil::parseJSON(inspected->auxData()));
1061 : }
1062 36676 : bool isLiveEdit = script->isLiveEdit();
1063 36676 : bool hasSourceURL = script->hasSourceURL();
1064 36676 : bool isModule = script->isModule();
1065 : String16 scriptId = script->scriptId();
1066 36676 : String16 scriptURL = script->sourceURL();
1067 :
1068 : m_scripts[scriptId] = std::move(script);
1069 :
1070 : ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1071 : DCHECK(scriptIterator != m_scripts.end());
1072 36676 : V8DebuggerScript* scriptRef = scriptIterator->second.get();
1073 : // V8 could create functions for parsed scripts before reporting and asks
1074 : // inspector about blackboxed state, we should reset state each time when we
1075 : // make any change that change isFunctionBlackboxed output - adding parsed
1076 : // script is changing.
1077 36676 : scriptRef->resetBlackboxedStateCache();
1078 :
1079 36676 : Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
1080 : Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
1081 : std::move(executionContextAuxData));
1082 36676 : const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1083 36676 : const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
1084 36676 : const bool* isModuleParam = isModule ? &isModule : nullptr;
1085 : std::unique_ptr<V8StackTraceImpl> stack =
1086 73352 : V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
1087 : std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
1088 69451 : stack && !stack->isEmpty() ? stack->buildInspectorObjectImpl() : nullptr;
1089 36676 : if (success) {
1090 : m_frontend.scriptParsed(
1091 : scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1092 : scriptRef->endLine(), scriptRef->endColumn(), contextId,
1093 36492 : scriptRef->hash(), std::move(executionContextAuxDataParam),
1094 : isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
1095 : isModuleParam, static_cast<int>(scriptRef->source().length()),
1096 291936 : std::move(stackTrace));
1097 : } else {
1098 : m_frontend.scriptFailedToParse(
1099 : scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1100 : scriptRef->endLine(), scriptRef->endColumn(), contextId,
1101 184 : scriptRef->hash(), std::move(executionContextAuxDataParam),
1102 : std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
1103 1288 : static_cast<int>(scriptRef->source().length()), std::move(stackTrace));
1104 : }
1105 :
1106 36676 : if (scriptURL.isEmpty() || !success) return;
1107 :
1108 : protocol::DictionaryValue* breakpointsCookie =
1109 25854 : m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
1110 12927 : if (!breakpointsCookie) return;
1111 :
1112 948 : for (size_t i = 0; i < breakpointsCookie->size(); ++i) {
1113 360 : auto cookie = breakpointsCookie->at(i);
1114 : protocol::DictionaryValue* breakpointObject =
1115 360 : protocol::DictionaryValue::cast(cookie.second);
1116 : bool isRegex;
1117 720 : breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
1118 : String16 url;
1119 720 : breakpointObject->getString(DebuggerAgentState::url, &url);
1120 360 : if (!matches(m_inspector, scriptURL, url, isRegex)) continue;
1121 : ScriptBreakpoint breakpoint;
1122 : breakpoint.script_id = scriptId;
1123 : breakpointObject->getInteger(DebuggerAgentState::lineNumber,
1124 228 : &breakpoint.line_number);
1125 : breakpointObject->getInteger(DebuggerAgentState::columnNumber,
1126 228 : &breakpoint.column_number);
1127 : breakpointObject->getString(DebuggerAgentState::condition,
1128 228 : &breakpoint.condition);
1129 : String16 hint;
1130 228 : bool hasHint = breakpointObject->getString(DebuggerAgentState::hint, &hint);
1131 : std::unique_ptr<protocol::Debugger::Location> location =
1132 114 : resolveBreakpoint(cookie.first, breakpoint, UserBreakpointSource, hint);
1133 114 : if (!hasHint) {
1134 156 : hint = breakpointHint(*scriptRef, breakpoint);
1135 78 : if (!hint.isEmpty())
1136 72 : breakpointObject->setString(DebuggerAgentState::hint, hint);
1137 : }
1138 114 : if (location)
1139 144 : m_frontend.breakpointResolved(cookie.first, std::move(location));
1140 228 : }
1141 : }
1142 :
1143 77060 : void V8DebuggerAgentImpl::didPause(int contextId,
1144 : v8::Local<v8::Value> exception,
1145 : const std::vector<String16>& hitBreakpoints,
1146 : bool isPromiseRejection, bool isUncaught,
1147 : bool isOOMBreak) {
1148 77060 : JavaScriptCallFrames frames = m_debugger->currentCallFrames();
1149 : m_pausedCallFrames.swap(frames);
1150 154120 : v8::HandleScope handles(m_isolate);
1151 :
1152 77060 : std::vector<BreakReason> hitReasons;
1153 :
1154 77060 : if (isOOMBreak) {
1155 : hitReasons.push_back(
1156 6 : std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
1157 77054 : } else if (!exception.IsEmpty()) {
1158 2674 : InjectedScript* injectedScript = nullptr;
1159 5348 : m_session->findInjectedScript(contextId, injectedScript);
1160 2674 : if (injectedScript) {
1161 : String16 breakReason =
1162 : isPromiseRejection
1163 : ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1164 2674 : : protocol::Debugger::Paused::ReasonEnum::Exception;
1165 2674 : std::unique_ptr<protocol::Runtime::RemoteObject> obj;
1166 : injectedScript->wrapObject(exception, kBacktraceObjectGroup, false, false,
1167 8022 : &obj);
1168 : std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1169 2674 : if (obj) {
1170 5348 : breakAuxData = obj->toValue();
1171 5348 : breakAuxData->setBoolean("uncaught", isUncaught);
1172 : } else {
1173 : breakAuxData = nullptr;
1174 : }
1175 : hitReasons.push_back(
1176 2674 : std::make_pair(breakReason, std::move(breakAuxData)));
1177 : }
1178 : }
1179 :
1180 : std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
1181 :
1182 : bool hasDebugCommandBreakpointReason = false;
1183 156963 : for (const auto& point : hitBreakpoints) {
1184 : DebugServerBreakpointToBreakpointIdAndSourceMap::iterator
1185 : breakpointIterator = m_serverBreakpoints.find(point);
1186 2843 : if (breakpointIterator != m_serverBreakpoints.end()) {
1187 2807 : const String16& localId = breakpointIterator->second.first;
1188 : hitBreakpointIds->addItem(localId);
1189 :
1190 2807 : BreakpointSource source = breakpointIterator->second.second;
1191 5614 : if (!hasDebugCommandBreakpointReason &&
1192 2807 : source == DebugCommandBreakpointSource) {
1193 : hasDebugCommandBreakpointReason = true;
1194 : hitReasons.push_back(std::make_pair(
1195 36 : protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
1196 : }
1197 : }
1198 : }
1199 :
1200 77672 : for (size_t i = 0; i < m_breakReason.size(); ++i) {
1201 77366 : hitReasons.push_back(std::move(m_breakReason[i]));
1202 : }
1203 77060 : clearBreakDetails();
1204 :
1205 77060 : String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1206 : std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1207 154120 : if (hitReasons.size() == 1) {
1208 : breakReason = hitReasons[0].first;
1209 2998 : breakAuxData = std::move(hitReasons[0].second);
1210 74062 : } else if (hitReasons.size() > 1) {
1211 24 : breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
1212 : std::unique_ptr<protocol::ListValue> reasons =
1213 12 : protocol::ListValue::create();
1214 72 : for (size_t i = 0; i < hitReasons.size(); ++i) {
1215 : std::unique_ptr<protocol::DictionaryValue> reason =
1216 24 : protocol::DictionaryValue::create();
1217 96 : reason->setString("reason", hitReasons[i].first);
1218 48 : if (hitReasons[i].second)
1219 24 : reason->setObject("auxData", std::move(hitReasons[i].second));
1220 48 : reasons->pushValue(std::move(reason));
1221 : }
1222 24 : breakAuxData = protocol::DictionaryValue::create();
1223 36 : breakAuxData->setArray("reasons", std::move(reasons));
1224 : }
1225 :
1226 77060 : std::unique_ptr<Array<CallFrame>> protocolCallFrames;
1227 77060 : Response response = currentCallFrames(&protocolCallFrames);
1228 77060 : if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
1229 : m_frontend.paused(std::move(protocolCallFrames), breakReason,
1230 : std::move(breakAuxData), std::move(hitBreakpointIds),
1231 539420 : currentAsyncStackTrace());
1232 :
1233 77060 : if (!m_continueToLocationBreakpointId.isEmpty()) {
1234 36 : m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
1235 72 : m_continueToLocationBreakpointId = "";
1236 77060 : }
1237 77060 : }
1238 :
1239 76975 : void V8DebuggerAgentImpl::didContinue() {
1240 : JavaScriptCallFrames emptyCallFrames;
1241 : m_pausedCallFrames.swap(emptyCallFrames);
1242 76975 : clearBreakDetails();
1243 76975 : m_frontend.resumed();
1244 76975 : }
1245 :
1246 78 : void V8DebuggerAgentImpl::breakProgram(
1247 : const String16& breakReason,
1248 : std::unique_ptr<protocol::DictionaryValue> data) {
1249 96 : if (!enabled() || !m_debugger->canBreakProgram() || m_skipAllPauses) return;
1250 : std::vector<BreakReason> currentScheduledReason;
1251 : currentScheduledReason.swap(m_breakReason);
1252 132 : pushBreakDetails(breakReason, std::move(data));
1253 78 : if (!m_debugger->breakProgram(m_session->contextGroupId())) return;
1254 : popBreakDetails();
1255 : m_breakReason.swap(currentScheduledReason);
1256 60 : if (!m_breakReason.empty()) {
1257 12 : m_debugger->setPauseOnNextStatement(true, m_session->contextGroupId());
1258 60 : }
1259 : }
1260 :
1261 24 : void V8DebuggerAgentImpl::breakProgramOnException(
1262 : const String16& breakReason,
1263 : std::unique_ptr<protocol::DictionaryValue> data) {
1264 48 : if (!enabled() ||
1265 24 : m_debugger->getPauseOnExceptionsState() == v8::debug::NoBreakOnException)
1266 24 : return;
1267 24 : breakProgram(breakReason, std::move(data));
1268 : }
1269 :
1270 66 : void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId,
1271 : int lineNumber, int columnNumber,
1272 : BreakpointSource source,
1273 : const String16& condition) {
1274 : ScriptBreakpoint breakpoint(scriptId, lineNumber, columnNumber, condition);
1275 66 : String16 breakpointId = generateBreakpointId(breakpoint, source);
1276 198 : resolveBreakpoint(breakpointId, breakpoint, source, /* hint */ String16());
1277 66 : }
1278 :
1279 60 : void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId,
1280 : int lineNumber, int columnNumber,
1281 : BreakpointSource source) {
1282 : removeBreakpointImpl(generateBreakpointId(
1283 : ScriptBreakpoint(scriptId, lineNumber, columnNumber, String16()),
1284 180 : source));
1285 60 : }
1286 :
1287 0 : void V8DebuggerAgentImpl::reset() {
1288 0 : if (!enabled()) return;
1289 : m_blackboxedPositions.clear();
1290 : resetBlackboxedStateCache();
1291 : m_scripts.clear();
1292 : m_breakpointIdToDebuggerBreakpointIds.clear();
1293 : }
1294 :
1295 : } // namespace v8_inspector
|