blob: f82ce98600e073fadb2984fc0586f69cfd351513 [file] [log] [blame]
Ben Murdochf3b273f2017-01-17 12:11:28 +00001// 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
Rubin Xu7bc1b612021-02-16 09:38:50 +00009#include "../../third_party/inspector_protocol/crdtp/json.h"
10#include "include/v8-inspector.h"
11#include "src/base/safe_conversions.h"
Ben Murdochc8c1d9e2017-03-08 14:04:23 +000012#include "src/debug/debug-interface.h"
Ben Murdochf3b273f2017-01-17 12:11:28 +000013#include "src/inspector/injected-script.h"
14#include "src/inspector/inspected-context.h"
Rubin Xu7bc1b612021-02-16 09:38:50 +000015#include "src/inspector/protocol/Debugger.h"
Ben Murdochf3b273f2017-01-17 12:11:28 +000016#include "src/inspector/protocol/Protocol.h"
17#include "src/inspector/remote-object-id.h"
Ben Murdochf3b273f2017-01-17 12:11:28 +000018#include "src/inspector/search-util.h"
19#include "src/inspector/string-util.h"
20#include "src/inspector/v8-debugger-script.h"
21#include "src/inspector/v8-debugger.h"
22#include "src/inspector/v8-inspector-impl.h"
23#include "src/inspector/v8-inspector-session-impl.h"
24#include "src/inspector/v8-regex.h"
25#include "src/inspector/v8-runtime-agent-impl.h"
26#include "src/inspector/v8-stack-trace-impl.h"
Rubin Xu7bc1b612021-02-16 09:38:50 +000027#include "src/inspector/v8-value-utils.h"
Ben Murdochf3b273f2017-01-17 12:11:28 +000028
29namespace v8_inspector {
30
31using protocol::Array;
32using protocol::Maybe;
33using protocol::Debugger::BreakpointId;
34using protocol::Debugger::CallFrame;
Rubin Xu7bc1b612021-02-16 09:38:50 +000035using protocol::Debugger::Scope;
Ben Murdochf3b273f2017-01-17 12:11:28 +000036using protocol::Runtime::ExceptionDetails;
Ben Murdochf3b273f2017-01-17 12:11:28 +000037using protocol::Runtime::RemoteObject;
Rubin Xu7bc1b612021-02-16 09:38:50 +000038using protocol::Runtime::ScriptId;
39
40namespace InstrumentationEnum =
41 protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum;
Ben Murdochf3b273f2017-01-17 12:11:28 +000042
43namespace DebuggerAgentState {
Ben Murdochf3b273f2017-01-17 12:11:28 +000044static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
45static const char asyncCallStackDepth[] = "asyncCallStackDepth";
46static const char blackboxPattern[] = "blackboxPattern";
47static const char debuggerEnabled[] = "debuggerEnabled";
Ben Murdochf3b273f2017-01-17 12:11:28 +000048static const char skipAllPauses[] = "skipAllPauses";
49
Rubin Xu7bc1b612021-02-16 09:38:50 +000050static const char breakpointsByRegex[] = "breakpointsByRegex";
51static const char breakpointsByUrl[] = "breakpointsByUrl";
52static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
53static const char breakpointHints[] = "breakpointHints";
54static const char instrumentationBreakpoints[] = "instrumentationBreakpoints";
55
Ben Murdochf3b273f2017-01-17 12:11:28 +000056} // namespace DebuggerAgentState
57
Ben Murdochc8c1d9e2017-03-08 14:04:23 +000058static const char kBacktraceObjectGroup[] = "backtrace";
59static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
60static const char kDebuggerNotPaused[] =
61 "Can only perform operation while paused.";
Ben Murdochf3b273f2017-01-17 12:11:28 +000062
Rubin Xu7bc1b612021-02-16 09:38:50 +000063static const size_t kBreakpointHintMaxLength = 128;
64static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;
65// Limit the number of breakpoints returned, as we otherwise may exceed
66// the maximum length of a message in mojo (see https://crbug.com/1105172).
67static const size_t kMaxNumBreakpoints = 1000;
68
69// TODO(1099680): getScriptSource and getWasmBytecode return Wasm wire bytes
70// as protocol::Binary, which is encoded as JSON string in the communication
71// to the DevTools front-end and hence leads to either crashing the renderer
72// that is being debugged or the renderer that's running the front-end if we
73// allow arbitrarily big Wasm byte sequences here. Ideally we would find a
74// different way to transfer the wire bytes (middle- to long-term), but as a
75// short-term solution, we should at least not crash.
76static const size_t kWasmBytecodeMaxLength = (v8::String::kMaxLength / 4) * 3;
77static const char kWasmBytecodeExceedsTransferLimit[] =
78 "WebAssembly bytecode exceeds the transfer limit";
79
Ben Murdoch62ed6312017-06-06 11:06:27 +010080namespace {
81
Rubin Xu7bc1b612021-02-16 09:38:50 +000082enum class BreakpointType {
83 kByUrl = 1,
84 kByUrlRegex,
85 kByScriptHash,
86 kByScriptId,
87 kDebugCommand,
88 kMonitorCommand,
89 kBreakpointAtEntry,
90 kInstrumentationBreakpoint
91};
Ben Murdoch62ed6312017-06-06 11:06:27 +010092
Rubin Xu7bc1b612021-02-16 09:38:50 +000093String16 generateBreakpointId(BreakpointType type,
94 const String16& scriptSelector, int lineNumber,
95 int columnNumber) {
Ben Murdochc8c1d9e2017-03-08 14:04:23 +000096 String16Builder builder;
Rubin Xu7bc1b612021-02-16 09:38:50 +000097 builder.appendNumber(static_cast<int>(type));
Ben Murdochc8c1d9e2017-03-08 14:04:23 +000098 builder.append(':');
Rubin Xu7bc1b612021-02-16 09:38:50 +000099 builder.appendNumber(lineNumber);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000100 builder.append(':');
Rubin Xu7bc1b612021-02-16 09:38:50 +0000101 builder.appendNumber(columnNumber);
102 builder.append(':');
103 builder.append(scriptSelector);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000104 return builder.toString();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000105}
106
Rubin Xu7bc1b612021-02-16 09:38:50 +0000107String16 generateBreakpointId(BreakpointType type,
108 v8::Local<v8::Function> function) {
109 String16Builder builder;
110 builder.appendNumber(static_cast<int>(type));
111 builder.append(':');
112 builder.appendNumber(v8::debug::GetDebuggingId(function));
113 return builder.toString();
114}
115
116String16 generateInstrumentationBreakpointId(const String16& instrumentation) {
117 String16Builder builder;
118 builder.appendNumber(
119 static_cast<int>(BreakpointType::kInstrumentationBreakpoint));
120 builder.append(':');
121 builder.append(instrumentation);
122 return builder.toString();
123}
124
125bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
126 String16* scriptSelector = nullptr,
127 int* lineNumber = nullptr, int* columnNumber = nullptr) {
128 size_t typeLineSeparator = breakpointId.find(':');
129 if (typeLineSeparator == String16::kNotFound) return false;
130
131 int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
132 if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
133 rawType > static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
134 return false;
135 }
136 if (type) *type = static_cast<BreakpointType>(rawType);
137 if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
138 rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
139 rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry) ||
140 rawType == static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
141 // The script and source position are not encoded in this case.
142 return true;
143 }
144
145 size_t lineColumnSeparator = breakpointId.find(':', typeLineSeparator + 1);
146 if (lineColumnSeparator == String16::kNotFound) return false;
147 size_t columnSelectorSeparator =
148 breakpointId.find(':', lineColumnSeparator + 1);
149 if (columnSelectorSeparator == String16::kNotFound) return false;
150 if (scriptSelector) {
151 *scriptSelector = breakpointId.substring(columnSelectorSeparator + 1);
152 }
153 if (lineNumber) {
154 *lineNumber = breakpointId
155 .substring(typeLineSeparator + 1,
156 lineColumnSeparator - typeLineSeparator - 1)
157 .toInteger();
158 }
159 if (columnNumber) {
160 *columnNumber =
161 breakpointId
162 .substring(lineColumnSeparator + 1,
163 columnSelectorSeparator - lineColumnSeparator - 1)
164 .toInteger();
165 }
166 return true;
167}
168
Ben Murdoch62ed6312017-06-06 11:06:27 +0100169bool positionComparator(const std::pair<int, int>& a,
170 const std::pair<int, int>& b) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000171 if (a.first != b.first) return a.first < b.first;
172 return a.second < b.second;
173}
174
Rubin Xu7bc1b612021-02-16 09:38:50 +0000175String16 breakpointHint(const V8DebuggerScript& script, int lineNumber,
176 int columnNumber) {
177 int offset = script.offset(lineNumber, columnNumber);
178 if (offset == V8DebuggerScript::kNoOffset) return String16();
179 String16 hint =
180 script.source(offset, kBreakpointHintMaxLength).stripWhiteSpace();
181 for (size_t i = 0; i < hint.length(); ++i) {
182 if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') {
183 return hint.substring(0, i);
184 }
185 }
186 return hint;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000187}
188
Rubin Xu7bc1b612021-02-16 09:38:50 +0000189void adjustBreakpointLocation(const V8DebuggerScript& script,
190 const String16& hint, int* lineNumber,
191 int* columnNumber) {
192 if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
193 return;
194 if (hint.isEmpty()) return;
195 intptr_t sourceOffset = script.offset(*lineNumber, *columnNumber);
196 if (sourceOffset == V8DebuggerScript::kNoOffset) return;
197
198 intptr_t searchRegionOffset = std::max(
199 sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
200 size_t offset = sourceOffset - searchRegionOffset;
201 String16 searchArea = script.source(searchRegionOffset,
202 offset + kBreakpointHintMaxSearchOffset);
203
204 size_t nextMatch = searchArea.find(hint, offset);
205 size_t prevMatch = searchArea.reverseFind(hint, offset);
206 if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
207 return;
208 }
209 size_t bestMatch;
210 if (nextMatch == String16::kNotFound) {
211 bestMatch = prevMatch;
212 } else if (prevMatch == String16::kNotFound) {
213 bestMatch = nextMatch;
214 } else {
215 bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
216 }
217 bestMatch += searchRegionOffset;
218 v8::debug::Location hintPosition =
219 script.location(static_cast<int>(bestMatch));
220 if (hintPosition.IsEmpty()) return;
221 *lineNumber = hintPosition.GetLineNumber();
222 *columnNumber = hintPosition.GetColumnNumber();
223}
224
225String16 breakLocationType(v8::debug::BreakLocationType type) {
226 switch (type) {
227 case v8::debug::kCallBreakLocation:
228 return protocol::Debugger::BreakLocation::TypeEnum::Call;
229 case v8::debug::kReturnBreakLocation:
230 return protocol::Debugger::BreakLocation::TypeEnum::Return;
231 case v8::debug::kDebuggerStatementBreakLocation:
232 return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
233 case v8::debug::kCommonBreakLocation:
234 return String16();
235 }
236 return String16();
237}
238
239String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
240 switch (type) {
241 case v8::debug::ScopeIterator::ScopeTypeGlobal:
242 return Scope::TypeEnum::Global;
243 case v8::debug::ScopeIterator::ScopeTypeLocal:
244 return Scope::TypeEnum::Local;
245 case v8::debug::ScopeIterator::ScopeTypeWith:
246 return Scope::TypeEnum::With;
247 case v8::debug::ScopeIterator::ScopeTypeClosure:
248 return Scope::TypeEnum::Closure;
249 case v8::debug::ScopeIterator::ScopeTypeCatch:
250 return Scope::TypeEnum::Catch;
251 case v8::debug::ScopeIterator::ScopeTypeBlock:
252 return Scope::TypeEnum::Block;
253 case v8::debug::ScopeIterator::ScopeTypeScript:
254 return Scope::TypeEnum::Script;
255 case v8::debug::ScopeIterator::ScopeTypeEval:
256 return Scope::TypeEnum::Eval;
257 case v8::debug::ScopeIterator::ScopeTypeModule:
258 return Scope::TypeEnum::Module;
259 case v8::debug::ScopeIterator::ScopeTypeWasmExpressionStack:
260 return Scope::TypeEnum::WasmExpressionStack;
261 }
262 UNREACHABLE();
263 return String16();
264}
265
266Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
267 InjectedScript* injectedScript,
268 std::unique_ptr<Array<Scope>>* scopes) {
269 *scopes = std::make_unique<Array<Scope>>();
270 if (!injectedScript) return Response::Success();
271 if (iterator->Done()) return Response::Success();
272
273 String16 scriptId = String16::fromInteger(iterator->GetScriptId());
274
275 for (; !iterator->Done(); iterator->Advance()) {
276 std::unique_ptr<RemoteObject> object;
277 Response result =
278 injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
279 WrapMode::kNoPreview, &object);
280 if (!result.IsSuccess()) return result;
281
282 auto scope = Scope::create()
283 .setType(scopeType(iterator->GetType()))
284 .setObject(std::move(object))
285 .build();
286
287 String16 name = toProtocolStringWithTypeCheck(
288 isolate, iterator->GetFunctionDebugName());
289 if (!name.isEmpty()) scope->setName(name);
290
291 if (iterator->HasLocationInfo()) {
292 v8::debug::Location start = iterator->GetStartLocation();
293 scope->setStartLocation(protocol::Debugger::Location::create()
294 .setScriptId(scriptId)
295 .setLineNumber(start.GetLineNumber())
296 .setColumnNumber(start.GetColumnNumber())
297 .build());
298
299 v8::debug::Location end = iterator->GetEndLocation();
300 scope->setEndLocation(protocol::Debugger::Location::create()
301 .setScriptId(scriptId)
302 .setLineNumber(end.GetLineNumber())
303 .setColumnNumber(end.GetColumnNumber())
304 .build());
305 }
306 (*scopes)->emplace_back(std::move(scope));
307 }
308 return Response::Success();
309}
310
311protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
312 const String16& key) {
313 protocol::DictionaryValue* value = object->getObject(key);
314 if (value) return value;
315 std::unique_ptr<protocol::DictionaryValue> newDictionary =
316 protocol::DictionaryValue::create();
317 value = newDictionary.get();
318 object->setObject(key, std::move(newDictionary));
319 return value;
320}
321
322Response isValidPosition(protocol::Debugger::ScriptPosition* position) {
323 if (position->getLineNumber() < 0)
324 return Response::ServerError("Position missing 'line' or 'line' < 0.");
325 if (position->getColumnNumber() < 0)
326 return Response::ServerError("Position missing 'column' or 'column' < 0.");
327 return Response::Success();
328}
329
330Response isValidRangeOfPositions(std::vector<std::pair<int, int>>& positions) {
331 for (size_t i = 1; i < positions.size(); ++i) {
332 if (positions[i - 1].first < positions[i].first) continue;
333 if (positions[i - 1].first == positions[i].first &&
334 positions[i - 1].second < positions[i].second)
335 continue;
336 return Response::ServerError(
337 "Input positions array is not sorted or contains duplicate values.");
338 }
339 return Response::Success();
340}
Ben Murdoch62ed6312017-06-06 11:06:27 +0100341} // namespace
342
Ben Murdochf3b273f2017-01-17 12:11:28 +0000343V8DebuggerAgentImpl::V8DebuggerAgentImpl(
344 V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
345 protocol::DictionaryValue* state)
346 : m_inspector(session->inspector()),
347 m_debugger(m_inspector->debugger()),
348 m_session(session),
349 m_enabled(false),
350 m_state(state),
351 m_frontend(frontendChannel),
Rubin Xu7bc1b612021-02-16 09:38:50 +0000352 m_isolate(m_inspector->isolate()) {}
Ben Murdochf3b273f2017-01-17 12:11:28 +0000353
Rubin Xu7bc1b612021-02-16 09:38:50 +0000354V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000355
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000356void V8DebuggerAgentImpl::enableImpl() {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000357 m_enabled = true;
358 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
359 m_debugger->enable();
360
Rubin Xu7bc1b612021-02-16 09:38:50 +0000361 std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts =
362 m_debugger->getCompiledScripts(m_session->contextGroupId(), this);
363 for (auto& script : compiledScripts) {
364 didParseSource(std::move(script), true);
365 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000366
Rubin Xu7bc1b612021-02-16 09:38:50 +0000367 m_breakpointsActive = true;
368 m_debugger->setBreakpointsActive(true);
369
370 if (isPaused()) {
371 didPause(0, v8::Local<v8::Value>(), std::vector<v8::debug::BreakpointId>(),
372 v8::debug::kException, false, false, false);
373 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000374}
375
Rubin Xu7bc1b612021-02-16 09:38:50 +0000376Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
377 String16* outDebuggerId) {
378 m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
379 maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
380 *outDebuggerId =
381 m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
382 if (enabled()) return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000383
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000384 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
Rubin Xu7bc1b612021-02-16 09:38:50 +0000385 return Response::ServerError("Script execution is prohibited");
Ben Murdochf3b273f2017-01-17 12:11:28 +0000386
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000387 enableImpl();
Rubin Xu7bc1b612021-02-16 09:38:50 +0000388 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000389}
390
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000391Response V8DebuggerAgentImpl::disable() {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000392 if (!enabled()) return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000393
Rubin Xu7bc1b612021-02-16 09:38:50 +0000394 m_state->remove(DebuggerAgentState::breakpointsByRegex);
395 m_state->remove(DebuggerAgentState::breakpointsByUrl);
396 m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
397 m_state->remove(DebuggerAgentState::breakpointHints);
398 m_state->remove(DebuggerAgentState::instrumentationBreakpoints);
399
Ben Murdochf3b273f2017-01-17 12:11:28 +0000400 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
Ben Murdoch62ed6312017-06-06 11:06:27 +0100401 v8::debug::NoBreakOnException);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000402 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
403
Rubin Xu7bc1b612021-02-16 09:38:50 +0000404 if (m_breakpointsActive) {
405 m_debugger->setBreakpointsActive(false);
406 m_breakpointsActive = false;
407 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000408 m_blackboxedPositions.clear();
Ben Murdoch62ed6312017-06-06 11:06:27 +0100409 m_blackboxPattern.reset();
410 resetBlackboxedStateCache();
Rubin Xu7bc1b612021-02-16 09:38:50 +0000411 m_skipList.clear();
Ben Murdoch62ed6312017-06-06 11:06:27 +0100412 m_scripts.clear();
Rubin Xu7bc1b612021-02-16 09:38:50 +0000413 m_cachedScriptIds.clear();
414 m_cachedScriptSize = 0;
415 for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
416 v8::debug::RemoveBreakpoint(m_isolate, it.first);
417 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000418 m_breakpointIdToDebuggerBreakpointIds.clear();
Rubin Xu7bc1b612021-02-16 09:38:50 +0000419 m_debuggerBreakpointIdToBreakpointId.clear();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000420 m_debugger->setAsyncCallStackDepth(this, 0);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000421 clearBreakDetails();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000422 m_skipAllPauses = false;
Ben Murdoch62ed6312017-06-06 11:06:27 +0100423 m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000424 m_state->remove(DebuggerAgentState::blackboxPattern);
425 m_enabled = false;
426 m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
Rubin Xu7bc1b612021-02-16 09:38:50 +0000427 m_debugger->disable();
428 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000429}
430
431void V8DebuggerAgentImpl::restore() {
432 DCHECK(!m_enabled);
433 if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
434 return;
435 if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
436 return;
437
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000438 enableImpl();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000439
Ben Murdoch62ed6312017-06-06 11:06:27 +0100440 int pauseState = v8::debug::NoBreakOnException;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000441 m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000442 setPauseOnExceptionsImpl(pauseState);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000443
444 m_skipAllPauses =
445 m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);
446
447 int asyncCallStackDepth = 0;
448 m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
449 &asyncCallStackDepth);
450 m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);
451
452 String16 blackboxPattern;
453 if (m_state->getString(DebuggerAgentState::blackboxPattern,
454 &blackboxPattern)) {
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000455 setBlackboxPattern(blackboxPattern);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000456 }
457}
458
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000459Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000460 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
461 if (m_breakpointsActive == active) return Response::Success();
462 m_breakpointsActive = active;
463 m_debugger->setBreakpointsActive(active);
464 if (!active && !m_breakReason.empty()) {
465 clearBreakDetails();
466 m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
467 }
468 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000469}
470
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000471Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
Ben Murdoch62ed6312017-06-06 11:06:27 +0100472 m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000473 m_skipAllPauses = skip;
Rubin Xu7bc1b612021-02-16 09:38:50 +0000474 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000475}
476
Rubin Xu7bc1b612021-02-16 09:38:50 +0000477static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
478 BreakpointType type, const String16& selector) {
479 switch (type) {
480 case BreakpointType::kByUrl:
481 return script.sourceURL() == selector;
482 case BreakpointType::kByScriptHash:
483 return script.hash() == selector;
484 case BreakpointType::kByUrlRegex: {
485 V8Regex regex(inspector, selector, true);
486 return regex.match(script.sourceURL()) != -1;
487 }
488 case BreakpointType::kByScriptId: {
489 return script.scriptId() == selector;
490 }
491 default:
492 return false;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000493 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000494}
495
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000496Response V8DebuggerAgentImpl::setBreakpointByUrl(
497 int lineNumber, Maybe<String16> optionalURL,
Rubin Xu7bc1b612021-02-16 09:38:50 +0000498 Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash,
499 Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition,
500 String16* outBreakpointId,
Ben Murdochf3b273f2017-01-17 12:11:28 +0000501 std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000502 *locations = std::make_unique<Array<protocol::Debugger::Location>>();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000503
Rubin Xu7bc1b612021-02-16 09:38:50 +0000504 int specified = (optionalURL.isJust() ? 1 : 0) +
505 (optionalURLRegex.isJust() ? 1 : 0) +
506 (optionalScriptHash.isJust() ? 1 : 0);
507 if (specified != 1) {
508 return Response::ServerError(
509 "Either url or urlRegex or scriptHash must be specified.");
510 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000511 int columnNumber = 0;
512 if (optionalColumnNumber.isJust()) {
513 columnNumber = optionalColumnNumber.fromJust();
Rubin Xu7bc1b612021-02-16 09:38:50 +0000514 if (columnNumber < 0)
515 return Response::ServerError("Incorrect column number");
Ben Murdochf3b273f2017-01-17 12:11:28 +0000516 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000517
Rubin Xu7bc1b612021-02-16 09:38:50 +0000518 BreakpointType type = BreakpointType::kByUrl;
519 String16 selector;
520 if (optionalURLRegex.isJust()) {
521 selector = optionalURLRegex.fromJust();
522 type = BreakpointType::kByUrlRegex;
523 } else if (optionalURL.isJust()) {
524 selector = optionalURL.fromJust();
525 type = BreakpointType::kByUrl;
526 } else if (optionalScriptHash.isJust()) {
527 selector = optionalScriptHash.fromJust();
528 type = BreakpointType::kByScriptHash;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000529 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000530
Rubin Xu7bc1b612021-02-16 09:38:50 +0000531 String16 condition = optionalCondition.fromMaybe(String16());
532 String16 breakpointId =
533 generateBreakpointId(type, selector, lineNumber, columnNumber);
534 protocol::DictionaryValue* breakpoints;
535 switch (type) {
536 case BreakpointType::kByUrlRegex:
537 breakpoints =
538 getOrCreateObject(m_state, DebuggerAgentState::breakpointsByRegex);
539 break;
540 case BreakpointType::kByUrl:
541 breakpoints = getOrCreateObject(
542 getOrCreateObject(m_state, DebuggerAgentState::breakpointsByUrl),
543 selector);
544 break;
545 case BreakpointType::kByScriptHash:
546 breakpoints = getOrCreateObject(
547 getOrCreateObject(m_state,
548 DebuggerAgentState::breakpointsByScriptHash),
549 selector);
550 break;
551 default:
552 UNREACHABLE();
553 }
554 if (breakpoints->get(breakpointId)) {
555 return Response::ServerError(
556 "Breakpoint at specified location already exists.");
557 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000558
Rubin Xu7bc1b612021-02-16 09:38:50 +0000559 String16 hint;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000560 for (const auto& script : m_scripts) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000561 if (!matches(m_inspector, *script.second, type, selector)) continue;
562 if (!hint.isEmpty()) {
563 adjustBreakpointLocation(*script.second, hint, &lineNumber,
564 &columnNumber);
565 }
566 std::unique_ptr<protocol::Debugger::Location> location = setBreakpointImpl(
567 breakpointId, script.first, condition, lineNumber, columnNumber);
568 if (location && type != BreakpointType::kByUrlRegex) {
569 hint = breakpointHint(*script.second, lineNumber, columnNumber);
570 }
571 if (location) (*locations)->emplace_back(std::move(location));
Ben Murdochf3b273f2017-01-17 12:11:28 +0000572 }
Rubin Xu7bc1b612021-02-16 09:38:50 +0000573 breakpoints->setString(breakpointId, condition);
574 if (!hint.isEmpty()) {
575 protocol::DictionaryValue* breakpointHints =
576 getOrCreateObject(m_state, DebuggerAgentState::breakpointHints);
577 breakpointHints->setString(breakpointId, hint);
578 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000579 *outBreakpointId = breakpointId;
Rubin Xu7bc1b612021-02-16 09:38:50 +0000580 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000581}
582
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000583Response V8DebuggerAgentImpl::setBreakpoint(
Ben Murdochf3b273f2017-01-17 12:11:28 +0000584 std::unique_ptr<protocol::Debugger::Location> location,
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000585 Maybe<String16> optionalCondition, String16* outBreakpointId,
Ben Murdochf3b273f2017-01-17 12:11:28 +0000586 std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000587 String16 breakpointId = generateBreakpointId(
588 BreakpointType::kByScriptId, location->getScriptId(),
589 location->getLineNumber(), location->getColumnNumber(0));
Ben Murdochf3b273f2017-01-17 12:11:28 +0000590 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
591 m_breakpointIdToDebuggerBreakpointIds.end()) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000592 return Response::ServerError(
593 "Breakpoint at specified location already exists.");
Ben Murdochf3b273f2017-01-17 12:11:28 +0000594 }
Rubin Xu7bc1b612021-02-16 09:38:50 +0000595 *actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
596 optionalCondition.fromMaybe(String16()),
597 location->getLineNumber(),
598 location->getColumnNumber(0));
599 if (!*actualLocation)
600 return Response::ServerError("Could not resolve breakpoint");
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000601 *outBreakpointId = breakpointId;
Rubin Xu7bc1b612021-02-16 09:38:50 +0000602 return Response::Success();
603}
604
605Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
606 const String16& functionObjectId, Maybe<String16> optionalCondition,
607 String16* outBreakpointId) {
608 InjectedScript::ObjectScope scope(m_session, functionObjectId);
609 Response response = scope.initialize();
610 if (!response.IsSuccess()) return response;
611 if (!scope.object()->IsFunction()) {
612 return Response::ServerError("Could not find function with given id");
613 }
614 v8::Local<v8::Function> function =
615 v8::Local<v8::Function>::Cast(scope.object());
616 String16 breakpointId =
617 generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
618 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
619 m_breakpointIdToDebuggerBreakpointIds.end()) {
620 return Response::ServerError(
621 "Breakpoint at specified location already exists.");
622 }
623 v8::Local<v8::String> condition =
624 toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
625 setBreakpointImpl(breakpointId, function, condition);
626 *outBreakpointId = breakpointId;
627 return Response::Success();
628}
629
630Response V8DebuggerAgentImpl::setInstrumentationBreakpoint(
631 const String16& instrumentation, String16* outBreakpointId) {
632 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
633 String16 breakpointId = generateInstrumentationBreakpointId(instrumentation);
634 protocol::DictionaryValue* breakpoints = getOrCreateObject(
635 m_state, DebuggerAgentState::instrumentationBreakpoints);
636 if (breakpoints->get(breakpointId)) {
637 return Response::ServerError(
638 "Instrumentation breakpoint is already enabled.");
639 }
640 breakpoints->setBoolean(breakpointId, true);
641 *outBreakpointId = breakpointId;
642 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000643}
644
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000645Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000646 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
647 BreakpointType type;
648 String16 selector;
649 if (!parseBreakpointId(breakpointId, &type, &selector)) {
650 return Response::Success();
651 }
652 protocol::DictionaryValue* breakpoints = nullptr;
653 switch (type) {
654 case BreakpointType::kByUrl: {
655 protocol::DictionaryValue* breakpointsByUrl =
656 m_state->getObject(DebuggerAgentState::breakpointsByUrl);
657 if (breakpointsByUrl) {
658 breakpoints = breakpointsByUrl->getObject(selector);
659 }
660 } break;
661 case BreakpointType::kByScriptHash: {
662 protocol::DictionaryValue* breakpointsByScriptHash =
663 m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
664 if (breakpointsByScriptHash) {
665 breakpoints = breakpointsByScriptHash->getObject(selector);
666 }
667 } break;
668 case BreakpointType::kByUrlRegex:
669 breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
670 break;
671 case BreakpointType::kInstrumentationBreakpoint:
672 breakpoints =
673 m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
674 break;
675 default:
676 break;
677 }
678 if (breakpoints) breakpoints->remove(breakpointId);
679 protocol::DictionaryValue* breakpointHints =
680 m_state->getObject(DebuggerAgentState::breakpointHints);
681 if (breakpointHints) breakpointHints->remove(breakpointId);
682
683 // Get a list of scripts to remove breakpoints.
684 // TODO(duongn): we can do better here if from breakpoint id we can tell it is
685 // not Wasm breakpoint.
686 std::vector<V8DebuggerScript*> scripts;
687 for (const auto& scriptIter : m_scripts) {
688 if (!matches(m_inspector, *scriptIter.second, type, selector)) continue;
689 V8DebuggerScript* script = scriptIter.second.get();
690 scripts.push_back(script);
691 }
692 removeBreakpointImpl(breakpointId, scripts);
693
694 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000695}
696
Rubin Xu7bc1b612021-02-16 09:38:50 +0000697void V8DebuggerAgentImpl::removeBreakpointImpl(
698 const String16& breakpointId,
699 const std::vector<V8DebuggerScript*>& scripts) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000700 DCHECK(enabled());
701 BreakpointIdToDebuggerBreakpointIdsMap::iterator
702 debuggerBreakpointIdsIterator =
703 m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
704 if (debuggerBreakpointIdsIterator ==
Rubin Xu7bc1b612021-02-16 09:38:50 +0000705 m_breakpointIdToDebuggerBreakpointIds.end()) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000706 return;
Rubin Xu7bc1b612021-02-16 09:38:50 +0000707 }
708 for (const auto& id : debuggerBreakpointIdsIterator->second) {
709 for (auto& script : scripts) {
710 script->removeWasmBreakpoint(id);
711 }
712 v8::debug::RemoveBreakpoint(m_isolate, id);
713 m_debuggerBreakpointIdToBreakpointId.erase(id);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000714 }
715 m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
716}
717
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000718Response V8DebuggerAgentImpl::getPossibleBreakpoints(
719 std::unique_ptr<protocol::Debugger::Location> start,
Rubin Xu7bc1b612021-02-16 09:38:50 +0000720 Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
721 std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
722 locations) {
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000723 String16 scriptId = start->getScriptId();
724
725 if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
Rubin Xu7bc1b612021-02-16 09:38:50 +0000726 return Response::ServerError(
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000727 "start.lineNumber and start.columnNumber should be >= 0");
728
Ben Murdoch62ed6312017-06-06 11:06:27 +0100729 v8::debug::Location v8Start(start->getLineNumber(),
730 start->getColumnNumber(0));
731 v8::debug::Location v8End;
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000732 if (end.isJust()) {
733 if (end.fromJust()->getScriptId() != scriptId)
Rubin Xu7bc1b612021-02-16 09:38:50 +0000734 return Response::ServerError(
735 "Locations should contain the same scriptId");
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000736 int line = end.fromJust()->getLineNumber();
737 int column = end.fromJust()->getColumnNumber(0);
738 if (line < 0 || column < 0)
Rubin Xu7bc1b612021-02-16 09:38:50 +0000739 return Response::ServerError(
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000740 "end.lineNumber and end.columnNumber should be >= 0");
Ben Murdoch62ed6312017-06-06 11:06:27 +0100741 v8End = v8::debug::Location(line, column);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000742 }
743 auto it = m_scripts.find(scriptId);
Rubin Xu7bc1b612021-02-16 09:38:50 +0000744 if (it == m_scripts.end()) return Response::ServerError("Script not found");
745 std::vector<v8::debug::BreakLocation> v8Locations;
746 {
747 v8::HandleScope handleScope(m_isolate);
748 int contextId = it->second->executionContextId();
749 InspectedContext* inspected = m_inspector->getContext(contextId);
750 if (!inspected) {
751 return Response::ServerError("Cannot retrive script context");
752 }
753 v8::Context::Scope contextScope(inspected->context());
754 v8::MicrotasksScope microtasks(m_isolate,
755 v8::MicrotasksScope::kDoNotRunMicrotasks);
756 v8::TryCatch tryCatch(m_isolate);
757 it->second->getPossibleBreakpoints(
758 v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000759 }
Rubin Xu7bc1b612021-02-16 09:38:50 +0000760
761 *locations =
762 std::make_unique<protocol::Array<protocol::Debugger::BreakLocation>>();
763
764 // TODO(1106269): Return an error instead of capping the number of
765 // breakpoints.
766 const size_t numBreakpointsToSend =
767 std::min(v8Locations.size(), kMaxNumBreakpoints);
768 for (size_t i = 0; i < numBreakpointsToSend; ++i) {
769 std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
770 protocol::Debugger::BreakLocation::create()
771 .setScriptId(scriptId)
772 .setLineNumber(v8Locations[i].GetLineNumber())
773 .setColumnNumber(v8Locations[i].GetColumnNumber())
774 .build();
775 if (v8Locations[i].type() != v8::debug::kCommonBreakLocation) {
776 breakLocation->setType(breakLocationType(v8Locations[i].type()));
777 }
778 (*locations)->emplace_back(std::move(breakLocation));
779 }
780 return Response::Success();
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000781}
782
783Response V8DebuggerAgentImpl::continueToLocation(
Rubin Xu7bc1b612021-02-16 09:38:50 +0000784 std::unique_ptr<protocol::Debugger::Location> location,
785 Maybe<String16> targetCallFrames) {
786 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
787 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
788 ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
789 if (it == m_scripts.end()) {
790 return Response::ServerError("Cannot continue to specified location");
Ben Murdochf3b273f2017-01-17 12:11:28 +0000791 }
Rubin Xu7bc1b612021-02-16 09:38:50 +0000792 V8DebuggerScript* script = it->second.get();
793 int contextId = script->executionContextId();
794 InspectedContext* inspected = m_inspector->getContext(contextId);
795 if (!inspected)
796 return Response::ServerError("Cannot continue to specified location");
797 v8::HandleScope handleScope(m_isolate);
798 v8::Context::Scope contextScope(inspected->context());
799 return m_debugger->continueToLocation(
800 m_session->contextGroupId(), script, std::move(location),
801 targetCallFrames.fromMaybe(
802 protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
803}
Ben Murdochf3b273f2017-01-17 12:11:28 +0000804
Rubin Xu7bc1b612021-02-16 09:38:50 +0000805Response V8DebuggerAgentImpl::getStackTrace(
806 std::unique_ptr<protocol::Runtime::StackTraceId> inStackTraceId,
807 std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
808 bool isOk = false;
809 int64_t id = inStackTraceId->getId().toInteger64(&isOk);
810 if (!isOk) return Response::ServerError("Invalid stack trace id");
Ben Murdochf3b273f2017-01-17 12:11:28 +0000811
Rubin Xu7bc1b612021-02-16 09:38:50 +0000812 V8DebuggerId debuggerId;
813 if (inStackTraceId->hasDebuggerId()) {
814 debuggerId = V8DebuggerId(inStackTraceId->getDebuggerId(String16()));
815 } else {
816 debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
817 }
818 if (!debuggerId.isValid())
819 return Response::ServerError("Invalid stack trace id");
820
821 V8StackTraceId v8StackTraceId(id, debuggerId.pair());
822 if (v8StackTraceId.IsInvalid())
823 return Response::ServerError("Invalid stack trace id");
824 auto stack =
825 m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
826 if (!stack) {
827 return Response::ServerError("Stack trace with given id is not found");
828 }
829 *outStackTrace = stack->buildInspectorObject(
830 m_debugger, m_debugger->maxAsyncCallChainDepth());
831 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000832}
833
Ben Murdoch62ed6312017-06-06 11:06:27 +0100834bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
835 const v8::debug::Location& start,
836 const v8::debug::Location& end) {
837 ScriptsMap::iterator it = m_scripts.find(scriptId);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000838 if (it == m_scripts.end()) {
839 // Unknown scripts are blackboxed.
840 return true;
841 }
842 if (m_blackboxPattern) {
843 const String16& scriptSourceURL = it->second->sourceURL();
844 if (!scriptSourceURL.isEmpty() &&
845 m_blackboxPattern->match(scriptSourceURL) != -1)
846 return true;
847 }
Ben Murdoch62ed6312017-06-06 11:06:27 +0100848 auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000849 if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;
850
851 const std::vector<std::pair<int, int>>& ranges =
852 itBlackboxedPositions->second;
Ben Murdoch62ed6312017-06-06 11:06:27 +0100853 auto itStartRange = std::lower_bound(
Ben Murdochf3b273f2017-01-17 12:11:28 +0000854 ranges.begin(), ranges.end(),
Ben Murdoch62ed6312017-06-06 11:06:27 +0100855 std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
856 positionComparator);
857 auto itEndRange = std::lower_bound(
858 itStartRange, ranges.end(),
859 std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
860 positionComparator);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000861 // Ranges array contains positions in script where blackbox state is changed.
862 // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
863 // blackboxed...
Ben Murdoch62ed6312017-06-06 11:06:27 +0100864 return itStartRange == itEndRange &&
865 std::distance(ranges.begin(), itStartRange) % 2;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000866}
867
Rubin Xu7bc1b612021-02-16 09:38:50 +0000868bool V8DebuggerAgentImpl::shouldBeSkipped(const String16& scriptId, int line,
869 int column) {
870 if (m_skipList.empty()) return false;
871
872 auto it = m_skipList.find(scriptId);
873 if (it == m_skipList.end()) return false;
874
875 const std::vector<std::pair<int, int>>& ranges = it->second;
876 DCHECK(!ranges.empty());
877 const std::pair<int, int> location = std::make_pair(line, column);
878 auto itLowerBound = std::lower_bound(ranges.begin(), ranges.end(), location,
879 positionComparator);
880
881 bool shouldSkip = false;
882 if (itLowerBound != ranges.end()) {
883 // Skip lists are defined as pairs of locations that specify the
884 // start and the end of ranges to skip: [ranges[0], ranges[1], ..], where
885 // locations in [ranges[0], ranges[1]) should be skipped, i.e.
886 // [(lineStart, columnStart), (lineEnd, columnEnd)).
887 const bool isSameAsLowerBound = location == *itLowerBound;
888 const bool isUnevenIndex = (itLowerBound - ranges.begin()) % 2;
889 shouldSkip = isSameAsLowerBound ^ isUnevenIndex;
890 }
891
892 return shouldSkip;
893}
894
895bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
896 return enabled() && (isOOMBreak || !m_skipAllPauses);
897}
898
Ben Murdochf3b273f2017-01-17 12:11:28 +0000899std::unique_ptr<protocol::Debugger::Location>
Rubin Xu7bc1b612021-02-16 09:38:50 +0000900V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
901 const String16& scriptId,
902 const String16& condition,
903 int lineNumber, int columnNumber) {
Ben Murdoch62ed6312017-06-06 11:06:27 +0100904 v8::HandleScope handles(m_isolate);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000905 DCHECK(enabled());
Rubin Xu7bc1b612021-02-16 09:38:50 +0000906
907 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000908 if (scriptIterator == m_scripts.end()) return nullptr;
Rubin Xu7bc1b612021-02-16 09:38:50 +0000909 V8DebuggerScript* script = scriptIterator->second.get();
910 if (lineNumber < script->startLine() || script->endLine() < lineNumber) {
Ben Murdochf3b273f2017-01-17 12:11:28 +0000911 return nullptr;
Rubin Xu7bc1b612021-02-16 09:38:50 +0000912 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000913
Rubin Xu7bc1b612021-02-16 09:38:50 +0000914 v8::debug::BreakpointId debuggerBreakpointId;
915 v8::debug::Location location(lineNumber, columnNumber);
916 int contextId = script->executionContextId();
917 InspectedContext* inspected = m_inspector->getContext(contextId);
918 if (!inspected) return nullptr;
Ben Murdoch62ed6312017-06-06 11:06:27 +0100919
Rubin Xu7bc1b612021-02-16 09:38:50 +0000920 {
921 v8::Context::Scope contextScope(inspected->context());
922 if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
923 return nullptr;
924 }
925 }
Ben Murdochf3b273f2017-01-17 12:11:28 +0000926
Rubin Xu7bc1b612021-02-16 09:38:50 +0000927 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
Ben Murdochf3b273f2017-01-17 12:11:28 +0000928 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
929 debuggerBreakpointId);
Rubin Xu7bc1b612021-02-16 09:38:50 +0000930
931 return protocol::Debugger::Location::create()
932 .setScriptId(scriptId)
933 .setLineNumber(location.GetLineNumber())
934 .setColumnNumber(location.GetColumnNumber())
935 .build();
936}
937
938void V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
939 v8::Local<v8::Function> function,
940 v8::Local<v8::String> condition) {
941 v8::debug::BreakpointId debuggerBreakpointId;
942 if (!v8::debug::SetFunctionBreakpoint(function, condition,
943 &debuggerBreakpointId)) {
944 return;
945 }
946 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
947 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
948 debuggerBreakpointId);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000949}
950
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000951Response V8DebuggerAgentImpl::searchInContent(
952 const String16& scriptId, const String16& query,
953 Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
Ben Murdochf3b273f2017-01-17 12:11:28 +0000954 std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
955 v8::HandleScope handles(m_isolate);
956 ScriptsMap::iterator it = m_scripts.find(scriptId);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000957 if (it == m_scripts.end())
Rubin Xu7bc1b612021-02-16 09:38:50 +0000958 return Response::ServerError("No script for id: " + scriptId.utf8());
Ben Murdochf3b273f2017-01-17 12:11:28 +0000959
Rubin Xu7bc1b612021-02-16 09:38:50 +0000960 *results = std::make_unique<protocol::Array<protocol::Debugger::SearchMatch>>(
961 searchInTextByLinesImpl(m_session, it->second->source(0), query,
Ben Murdoch62ed6312017-06-06 11:06:27 +0100962 optionalCaseSensitive.fromMaybe(false),
Rubin Xu7bc1b612021-02-16 09:38:50 +0000963 optionalIsRegex.fromMaybe(false)));
964 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +0000965}
966
Ben Murdochc8c1d9e2017-03-08 14:04:23 +0000967Response V8DebuggerAgentImpl::setScriptSource(
968 const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
Ben Murdochf3b273f2017-01-17 12:11:28 +0000969 Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
Rubin Xu7bc1b612021-02-16 09:38:50 +0000970 Maybe<bool>* stackChanged,
971 Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
972 Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
Ben Murdochf3b273f2017-01-17 12:11:28 +0000973 Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000974 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
Ben Murdochf3b273f2017-01-17 12:11:28 +0000975
Ben Murdoch62ed6312017-06-06 11:06:27 +0100976 ScriptsMap::iterator it = m_scripts.find(scriptId);
977 if (it == m_scripts.end()) {
Rubin Xu7bc1b612021-02-16 09:38:50 +0000978 return Response::ServerError("No script with given id found");
Ben Murdoch62ed6312017-06-06 11:06:27 +0100979 }
Rubin Xu7bc1b612021-02-16 09:38:50 +0000980 int contextId = it->second->executionContextId();
981 InspectedContext* inspected = m_inspector->getContext(contextId);
982 if (!inspected) {
983 return Response::InternalError();
Ben Murdoch62ed6312017-06-06 11:06:27 +0100984 }
Rubin Xu7bc1b612021-02-16 09:38:50 +0000985 v8::HandleScope handleScope(m_isolate);
986 v8::Local<v8::Context> context = inspected->context();
987 v8::Context::Scope contextScope(context);
Ben Murdoch62ed6312017-06-06 11:06:27 +0100988
Rubin Xu7bc1b612021-02-16 09:38:50 +0000989 v8::debug::LiveEditResult result;
990 it->second->setSource(newContent, dryRun.fromMaybe(false), &result);
991 if (result.status != v8::debug::LiveEditResult::OK) {
992 *optOutCompileError =
993 protocol::Runtime::ExceptionDetails::create()
994 .setExceptionId(m_inspector->nextExceptionId())
995 .setText(toProtocolString(m_isolate, result.message))
996 .setLineNumber(result.line_number != -1 ? result.line_number - 1
997 : 0)
998 .setColumnNumber(result.column_number != -1 ? result.column_number
999 : 0)
1000 .build();
1001 return Response::Success();
1002 } else {
1003 *stackChanged = result.stack_changed;
1004 }
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001005 std::unique_ptr<Array<CallFrame>> callFrames;
Rubin Xu7bc1b612021-02-16 09:38:50 +00001006 Response response = currentCallFrames(&callFrames);
1007 if (!response.IsSuccess()) return response;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001008 *newCallFrames = std::move(callFrames);
1009 *asyncStackTrace = currentAsyncStackTrace();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001010 *asyncStackTraceId = currentExternalStackTrace();
1011 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001012}
1013
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001014Response V8DebuggerAgentImpl::restartFrame(
1015 const String16& callFrameId,
Ben Murdochf3b273f2017-01-17 12:11:28 +00001016 std::unique_ptr<Array<CallFrame>>* newCallFrames,
Rubin Xu7bc1b612021-02-16 09:38:50 +00001017 Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
1018 Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
1019 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1020 InjectedScript::CallFrameScope scope(m_session, callFrameId);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001021 Response response = scope.initialize();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001022 if (!response.IsSuccess()) return response;
1023 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1024 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1025 if (it->Done()) {
1026 return Response::ServerError("Could not find call frame with given id");
1027 }
1028 if (!it->Restart()) {
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001029 return Response::InternalError();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001030 }
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001031 response = currentCallFrames(newCallFrames);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001032 if (!response.IsSuccess()) return response;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001033 *asyncStackTrace = currentAsyncStackTrace();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001034 *asyncStackTraceId = currentExternalStackTrace();
1035 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001036}
1037
Rubin Xu7bc1b612021-02-16 09:38:50 +00001038Response V8DebuggerAgentImpl::getScriptSource(
1039 const String16& scriptId, String16* scriptSource,
1040 Maybe<protocol::Binary>* bytecode) {
1041 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001042 ScriptsMap::iterator it = m_scripts.find(scriptId);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001043 if (it == m_scripts.end())
Rubin Xu7bc1b612021-02-16 09:38:50 +00001044 return Response::ServerError("No script for id: " + scriptId.utf8());
1045 *scriptSource = it->second->source(0);
1046 v8::MemorySpan<const uint8_t> span;
1047 if (it->second->wasmBytecode().To(&span)) {
1048 if (span.size() > kWasmBytecodeMaxLength) {
1049 return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
1050 }
1051 *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
1052 }
1053 return Response::Success();
1054}
1055
1056Response V8DebuggerAgentImpl::getWasmBytecode(const String16& scriptId,
1057 protocol::Binary* bytecode) {
1058 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1059 ScriptsMap::iterator it = m_scripts.find(scriptId);
1060 if (it == m_scripts.end())
1061 return Response::ServerError("No script for id: " + scriptId.utf8());
1062 v8::MemorySpan<const uint8_t> span;
1063 if (!it->second->wasmBytecode().To(&span))
1064 return Response::ServerError("Script with id " + scriptId.utf8() +
1065 " is not WebAssembly");
1066 if (span.size() > kWasmBytecodeMaxLength) {
1067 return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
1068 }
1069 *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
1070 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001071}
1072
Ben Murdoch62ed6312017-06-06 11:06:27 +01001073void V8DebuggerAgentImpl::pushBreakDetails(
1074 const String16& breakReason,
1075 std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
1076 m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
1077}
1078
1079void V8DebuggerAgentImpl::popBreakDetails() {
1080 if (m_breakReason.empty()) return;
1081 m_breakReason.pop_back();
1082}
1083
1084void V8DebuggerAgentImpl::clearBreakDetails() {
1085 std::vector<BreakReason> emptyBreakReason;
1086 m_breakReason.swap(emptyBreakReason);
1087}
1088
Ben Murdochf3b273f2017-01-17 12:11:28 +00001089void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
1090 const String16& breakReason,
1091 std::unique_ptr<protocol::DictionaryValue> data) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001092 if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1093 if (m_breakReason.empty()) {
1094 m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1095 }
Ben Murdoch62ed6312017-06-06 11:06:27 +01001096 pushBreakDetails(breakReason, std::move(data));
Ben Murdochf3b273f2017-01-17 12:11:28 +00001097}
1098
Ben Murdochf3b273f2017-01-17 12:11:28 +00001099void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001100 if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1101 if (m_breakReason.size() == 1) {
1102 m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
1103 }
Ben Murdoch62ed6312017-06-06 11:06:27 +01001104 popBreakDetails();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001105}
1106
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001107Response V8DebuggerAgentImpl::pause() {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001108 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1109 if (isPaused()) return Response::Success();
1110 if (m_debugger->canBreakProgram()) {
1111 m_debugger->interruptAndBreak(m_session->contextGroupId());
1112 } else {
1113 if (m_breakReason.empty()) {
1114 m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1115 }
1116 pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
1117 }
1118 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001119}
1120
Rubin Xu7bc1b612021-02-16 09:38:50 +00001121Response V8DebuggerAgentImpl::resume(Maybe<bool> terminateOnResume) {
1122 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001123 m_session->releaseObjectGroup(kBacktraceObjectGroup);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001124 m_debugger->continueProgram(m_session->contextGroupId(),
1125 terminateOnResume.fromMaybe(false));
1126 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001127}
1128
Rubin Xu7bc1b612021-02-16 09:38:50 +00001129Response V8DebuggerAgentImpl::stepOver(
1130 Maybe<protocol::Array<protocol::Debugger::LocationRange>> inSkipList) {
1131 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1132
1133 if (inSkipList.isJust()) {
1134 const Response res = processSkipList(inSkipList.fromJust());
1135 if (res.IsError()) return res;
1136 } else {
1137 m_skipList.clear();
1138 }
1139
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001140 m_session->releaseObjectGroup(kBacktraceObjectGroup);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001141 m_debugger->stepOverStatement(m_session->contextGroupId());
1142 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001143}
1144
Rubin Xu7bc1b612021-02-16 09:38:50 +00001145Response V8DebuggerAgentImpl::stepInto(
1146 Maybe<bool> inBreakOnAsyncCall,
1147 Maybe<protocol::Array<protocol::Debugger::LocationRange>> inSkipList) {
1148 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1149
1150 if (inSkipList.isJust()) {
1151 const Response res = processSkipList(inSkipList.fromJust());
1152 if (res.IsError()) return res;
1153 } else {
1154 m_skipList.clear();
1155 }
1156
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001157 m_session->releaseObjectGroup(kBacktraceObjectGroup);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001158 m_debugger->stepIntoStatement(m_session->contextGroupId(),
1159 inBreakOnAsyncCall.fromMaybe(false));
1160 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001161}
1162
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001163Response V8DebuggerAgentImpl::stepOut() {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001164 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001165 m_session->releaseObjectGroup(kBacktraceObjectGroup);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001166 m_debugger->stepOutOfFunction(m_session->contextGroupId());
1167 return Response::Success();
1168}
1169
1170Response V8DebuggerAgentImpl::pauseOnAsyncCall(
1171 std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1172 // Deprecated, just return OK.
1173 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001174}
1175
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001176Response V8DebuggerAgentImpl::setPauseOnExceptions(
1177 const String16& stringPauseState) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001178 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
Ben Murdoch62ed6312017-06-06 11:06:27 +01001179 v8::debug::ExceptionBreakState pauseState;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001180 if (stringPauseState == "none") {
Ben Murdoch62ed6312017-06-06 11:06:27 +01001181 pauseState = v8::debug::NoBreakOnException;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001182 } else if (stringPauseState == "all") {
Ben Murdoch62ed6312017-06-06 11:06:27 +01001183 pauseState = v8::debug::BreakOnAnyException;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001184 } else if (stringPauseState == "uncaught") {
Ben Murdoch62ed6312017-06-06 11:06:27 +01001185 pauseState = v8::debug::BreakOnUncaughtException;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001186 } else {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001187 return Response::ServerError("Unknown pause on exceptions mode: " +
1188 stringPauseState.utf8());
Ben Murdochf3b273f2017-01-17 12:11:28 +00001189 }
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001190 setPauseOnExceptionsImpl(pauseState);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001191 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001192}
1193
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001194void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001195 // TODO(dgozman): this changes the global state and forces all context groups
1196 // to pause. We should make this flag be per-context-group.
Ben Murdochf3b273f2017-01-17 12:11:28 +00001197 m_debugger->setPauseOnExceptionsState(
Ben Murdoch62ed6312017-06-06 11:06:27 +01001198 static_cast<v8::debug::ExceptionBreakState>(pauseState));
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001199 m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001200}
1201
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001202Response V8DebuggerAgentImpl::evaluateOnCallFrame(
1203 const String16& callFrameId, const String16& expression,
1204 Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
1205 Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
Rubin Xu7bc1b612021-02-16 09:38:50 +00001206 Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
1207 std::unique_ptr<RemoteObject>* result,
Ben Murdochf3b273f2017-01-17 12:11:28 +00001208 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001209 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1210 InjectedScript::CallFrameScope scope(m_session, callFrameId);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001211 Response response = scope.initialize();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001212 if (!response.IsSuccess()) return response;
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001213 if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001214 if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
1215
Rubin Xu7bc1b612021-02-16 09:38:50 +00001216 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1217 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1218 if (it->Done()) {
1219 return Response::ServerError("Could not find call frame with given id");
1220 }
1221
1222 v8::MaybeLocal<v8::Value> maybeResultValue;
1223 {
1224 V8InspectorImpl::EvaluateScope evaluateScope(scope);
1225 if (timeout.isJust()) {
1226 response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
1227 if (!response.IsSuccess()) return response;
1228 }
1229 maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
1230 throwOnSideEffect.fromMaybe(false));
1231 }
1232 // Re-initialize after running client's code, as it could have destroyed
1233 // context or session.
1234 response = scope.initialize();
1235 if (!response.IsSuccess()) return response;
1236 WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
1237 : WrapMode::kNoPreview;
1238 if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
1239 return scope.injectedScript()->wrapEvaluateResult(
1240 maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), mode,
1241 result, exceptionDetails);
1242}
1243
1244Response V8DebuggerAgentImpl::executeWasmEvaluator(
1245 const String16& callFrameId, const protocol::Binary& evaluator,
1246 Maybe<double> timeout,
1247 std::unique_ptr<protocol::Runtime::RemoteObject>* result,
1248 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
1249 if (!v8::debug::StackTraceIterator::SupportsWasmDebugEvaluate()) {
1250 return Response::ServerError(
1251 "--wasm-expose-debug-eval is required to execte evaluator modules");
1252 }
1253 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1254 InjectedScript::CallFrameScope scope(m_session, callFrameId);
1255 Response response = scope.initialize();
1256 if (!response.IsSuccess()) return response;
1257
1258 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1259 std::unique_ptr<v8::debug::StackTraceIterator> it =
1260 v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1261 if (it->Done()) {
1262 return Response::ServerError("Could not find call frame with given id");
1263 }
1264 if (!it->GetScript()->IsWasm()) {
1265 return Response::ServerError(
1266 "executeWasmEvaluator can only be called on WebAssembly frames");
1267 }
1268
1269 v8::MaybeLocal<v8::Value> maybeResultValue;
1270 {
1271 V8InspectorImpl::EvaluateScope evaluateScope(scope);
1272 if (timeout.isJust()) {
1273 response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
1274 if (!response.IsSuccess()) return response;
1275 }
1276 v8::MaybeLocal<v8::String> eval_result =
1277 it->EvaluateWasm({evaluator.data(), evaluator.size()}, frameOrdinal);
1278 if (!eval_result.IsEmpty()) maybeResultValue = eval_result.ToLocalChecked();
1279 }
Ben Murdochf3b273f2017-01-17 12:11:28 +00001280
1281 // Re-initialize after running client's code, as it could have destroyed
1282 // context or session.
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001283 response = scope.initialize();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001284 if (!response.IsSuccess()) return response;
1285
1286 String16 object_group = "";
1287 InjectedScript* injected_script = scope.injectedScript();
1288 return injected_script->wrapEvaluateResult(maybeResultValue, scope.tryCatch(),
1289 object_group, WrapMode::kNoPreview,
1290 result, exceptionDetails);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001291}
1292
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001293Response V8DebuggerAgentImpl::setVariableValue(
1294 int scopeNumber, const String16& variableName,
Ben Murdochf3b273f2017-01-17 12:11:28 +00001295 std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
1296 const String16& callFrameId) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001297 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1298 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1299 InjectedScript::CallFrameScope scope(m_session, callFrameId);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001300 Response response = scope.initialize();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001301 if (!response.IsSuccess()) return response;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001302 v8::Local<v8::Value> newValue;
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001303 response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
1304 &newValue);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001305 if (!response.IsSuccess()) return response;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001306
Rubin Xu7bc1b612021-02-16 09:38:50 +00001307 int frameOrdinal = static_cast<int>(scope.frameOrdinal());
1308 auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
1309 if (it->Done()) {
1310 return Response::ServerError("Could not find call frame with given id");
1311 }
1312 auto scopeIterator = it->GetScopeIterator();
1313 while (!scopeIterator->Done() && scopeNumber > 0) {
1314 --scopeNumber;
1315 scopeIterator->Advance();
1316 }
1317 if (scopeNumber != 0) {
1318 return Response::ServerError("Could not find scope with given number");
1319 }
1320
1321 if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
1322 newValue) ||
1323 scope.tryCatch().HasCaught()) {
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001324 return Response::InternalError();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001325 }
1326 return Response::Success();
1327}
1328
1329Response V8DebuggerAgentImpl::setReturnValue(
1330 std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
1331 if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1332 if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1333 v8::HandleScope handleScope(m_isolate);
1334 auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1335 if (iterator->Done()) {
1336 return Response::ServerError("Could not find top call frame");
1337 }
1338 if (iterator->GetReturnValue().IsEmpty()) {
1339 return Response::ServerError(
1340 "Could not update return value at non-return position");
1341 }
1342 InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
1343 Response response = scope.initialize();
1344 if (!response.IsSuccess()) return response;
1345 v8::Local<v8::Value> newValue;
1346 response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
1347 &newValue);
1348 if (!response.IsSuccess()) return response;
1349 v8::debug::SetReturnValue(m_isolate, newValue);
1350 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001351}
1352
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001353Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001354 if (!enabled() && !m_session->runtimeAgent()->enabled()) {
1355 return Response::ServerError(kDebuggerNotEnabled);
1356 }
Ben Murdochf3b273f2017-01-17 12:11:28 +00001357 m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
1358 m_debugger->setAsyncCallStackDepth(this, depth);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001359 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001360}
1361
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001362Response V8DebuggerAgentImpl::setBlackboxPatterns(
Ben Murdochf3b273f2017-01-17 12:11:28 +00001363 std::unique_ptr<protocol::Array<String16>> patterns) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001364 if (patterns->empty()) {
Ben Murdochf3b273f2017-01-17 12:11:28 +00001365 m_blackboxPattern = nullptr;
Ben Murdoch62ed6312017-06-06 11:06:27 +01001366 resetBlackboxedStateCache();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001367 m_state->remove(DebuggerAgentState::blackboxPattern);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001368 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001369 }
1370
1371 String16Builder patternBuilder;
1372 patternBuilder.append('(');
Rubin Xu7bc1b612021-02-16 09:38:50 +00001373 for (size_t i = 0; i < patterns->size() - 1; ++i) {
1374 patternBuilder.append((*patterns)[i]);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001375 patternBuilder.append("|");
1376 }
Rubin Xu7bc1b612021-02-16 09:38:50 +00001377 patternBuilder.append(patterns->back());
Ben Murdochf3b273f2017-01-17 12:11:28 +00001378 patternBuilder.append(')');
1379 String16 pattern = patternBuilder.toString();
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001380 Response response = setBlackboxPattern(pattern);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001381 if (!response.IsSuccess()) return response;
Ben Murdoch62ed6312017-06-06 11:06:27 +01001382 resetBlackboxedStateCache();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001383 m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001384 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001385}
1386
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001387Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
Ben Murdochf3b273f2017-01-17 12:11:28 +00001388 std::unique_ptr<V8Regex> regex(new V8Regex(
1389 m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001390 if (!regex->isValid())
Rubin Xu7bc1b612021-02-16 09:38:50 +00001391 return Response::ServerError("Pattern parser error: " +
1392 regex->errorMessage().utf8());
Ben Murdochf3b273f2017-01-17 12:11:28 +00001393 m_blackboxPattern = std::move(regex);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001394 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001395}
1396
Ben Murdoch62ed6312017-06-06 11:06:27 +01001397void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
1398 for (const auto& it : m_scripts) {
1399 it.second->resetBlackboxedStateCache();
1400 }
1401}
1402
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001403Response V8DebuggerAgentImpl::setBlackboxedRanges(
1404 const String16& scriptId,
Ben Murdochf3b273f2017-01-17 12:11:28 +00001405 std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
1406 inPositions) {
Ben Murdoch62ed6312017-06-06 11:06:27 +01001407 auto it = m_scripts.find(scriptId);
1408 if (it == m_scripts.end())
Rubin Xu7bc1b612021-02-16 09:38:50 +00001409 return Response::ServerError("No script with passed id.");
Ben Murdochf3b273f2017-01-17 12:11:28 +00001410
Rubin Xu7bc1b612021-02-16 09:38:50 +00001411 if (inPositions->empty()) {
Ben Murdochf3b273f2017-01-17 12:11:28 +00001412 m_blackboxedPositions.erase(scriptId);
Ben Murdoch62ed6312017-06-06 11:06:27 +01001413 it->second->resetBlackboxedStateCache();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001414 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001415 }
1416
1417 std::vector<std::pair<int, int>> positions;
Rubin Xu7bc1b612021-02-16 09:38:50 +00001418 positions.reserve(inPositions->size());
1419 for (const std::unique_ptr<protocol::Debugger::ScriptPosition>& position :
1420 *inPositions) {
1421 Response res = isValidPosition(position.get());
1422 if (res.IsError()) return res;
1423
Ben Murdochf3b273f2017-01-17 12:11:28 +00001424 positions.push_back(
1425 std::make_pair(position->getLineNumber(), position->getColumnNumber()));
1426 }
Rubin Xu7bc1b612021-02-16 09:38:50 +00001427 Response res = isValidRangeOfPositions(positions);
1428 if (res.IsError()) return res;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001429
1430 m_blackboxedPositions[scriptId] = positions;
Ben Murdoch62ed6312017-06-06 11:06:27 +01001431 it->second->resetBlackboxedStateCache();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001432 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001433}
1434
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001435Response V8DebuggerAgentImpl::currentCallFrames(
1436 std::unique_ptr<Array<CallFrame>>* result) {
Ben Murdoch62ed6312017-06-06 11:06:27 +01001437 if (!isPaused()) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001438 *result = std::make_unique<Array<CallFrame>>();
1439 return Response::Success();
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001440 }
Ben Murdochf3b273f2017-01-17 12:11:28 +00001441 v8::HandleScope handles(m_isolate);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001442 *result = std::make_unique<Array<CallFrame>>();
1443 auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
1444 int frameOrdinal = 0;
1445 for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
1446 int contextId = iterator->GetContextId();
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001447 InjectedScript* injectedScript = nullptr;
1448 if (contextId) m_session->findInjectedScript(contextId, injectedScript);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001449 String16 callFrameId = RemoteCallFrameId::serialize(
1450 m_inspector->isolateId(), contextId, frameOrdinal);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001451
Rubin Xu7bc1b612021-02-16 09:38:50 +00001452 v8::debug::Location loc = iterator->GetSourceLocation();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001453
Rubin Xu7bc1b612021-02-16 09:38:50 +00001454 std::unique_ptr<Array<Scope>> scopes;
1455 auto scopeIterator = iterator->GetScopeIterator();
1456 Response res =
1457 buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
1458 if (!res.IsSuccess()) return res;
1459
1460 std::unique_ptr<RemoteObject> protocolReceiver;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001461 if (injectedScript) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001462 v8::Local<v8::Value> receiver;
1463 if (iterator->GetReceiver().ToLocal(&receiver)) {
1464 res =
1465 injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
1466 WrapMode::kNoPreview, &protocolReceiver);
1467 if (!res.IsSuccess()) return res;
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001468 }
Rubin Xu7bc1b612021-02-16 09:38:50 +00001469 }
1470 if (!protocolReceiver) {
1471 protocolReceiver = RemoteObject::create()
1472 .setType(RemoteObject::TypeEnum::Undefined)
1473 .build();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001474 }
1475
Rubin Xu7bc1b612021-02-16 09:38:50 +00001476 v8::Local<v8::debug::Script> script = iterator->GetScript();
1477 DCHECK(!script.IsEmpty());
1478 std::unique_ptr<protocol::Debugger::Location> location =
1479 protocol::Debugger::Location::create()
1480 .setScriptId(String16::fromInteger(script->Id()))
1481 .setLineNumber(loc.GetLineNumber())
1482 .setColumnNumber(loc.GetColumnNumber())
1483 .build();
1484 String16 scriptId = String16::fromInteger(script->Id());
1485 ScriptsMap::iterator scriptIterator =
1486 m_scripts.find(location->getScriptId());
1487 String16 url;
1488 if (scriptIterator != m_scripts.end()) {
1489 url = scriptIterator->second->sourceURL();
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001490 }
Rubin Xu7bc1b612021-02-16 09:38:50 +00001491
1492 auto frame = CallFrame::create()
1493 .setCallFrameId(callFrameId)
1494 .setFunctionName(toProtocolString(
1495 m_isolate, iterator->GetFunctionDebugName()))
1496 .setLocation(std::move(location))
1497 .setUrl(url)
1498 .setScopeChain(std::move(scopes))
1499 .setThis(std::move(protocolReceiver))
1500 .build();
1501
1502 v8::Local<v8::Function> func = iterator->GetFunction();
1503 if (!func.IsEmpty()) {
1504 frame->setFunctionLocation(
1505 protocol::Debugger::Location::create()
1506 .setScriptId(String16::fromInteger(func->ScriptId()))
1507 .setLineNumber(func->GetScriptLineNumber())
1508 .setColumnNumber(func->GetScriptColumnNumber())
1509 .build());
1510 }
1511
1512 v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
1513 if (!returnValue.IsEmpty() && injectedScript) {
1514 std::unique_ptr<RemoteObject> value;
1515 res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
1516 WrapMode::kNoPreview, &value);
1517 if (!res.IsSuccess()) return res;
1518 frame->setReturnValue(std::move(value));
1519 }
1520 (*result)->emplace_back(std::move(frame));
Ben Murdochf3b273f2017-01-17 12:11:28 +00001521 }
Rubin Xu7bc1b612021-02-16 09:38:50 +00001522 return Response::Success();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001523}
1524
Rubin Xu7bc1b612021-02-16 09:38:50 +00001525std::unique_ptr<protocol::Runtime::StackTrace>
1526V8DebuggerAgentImpl::currentAsyncStackTrace() {
1527 std::shared_ptr<AsyncStackTrace> asyncParent =
1528 m_debugger->currentAsyncParent();
1529 if (!asyncParent) return nullptr;
1530 return asyncParent->buildInspectorObject(
1531 m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001532}
1533
Rubin Xu7bc1b612021-02-16 09:38:50 +00001534std::unique_ptr<protocol::Runtime::StackTraceId>
1535V8DebuggerAgentImpl::currentExternalStackTrace() {
1536 V8StackTraceId externalParent = m_debugger->currentExternalParent();
1537 if (externalParent.IsInvalid()) return nullptr;
1538 return protocol::Runtime::StackTraceId::create()
1539 .setId(stackTraceIdToString(externalParent.id))
1540 .setDebuggerId(V8DebuggerId(externalParent.debugger_id).toString())
1541 .build();
1542}
1543
1544bool V8DebuggerAgentImpl::isPaused() const {
1545 return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
1546}
1547
1548static String16 getScriptLanguage(const V8DebuggerScript& script) {
1549 switch (script.getLanguage()) {
1550 case V8DebuggerScript::Language::WebAssembly:
1551 return protocol::Debugger::ScriptLanguageEnum::WebAssembly;
1552 case V8DebuggerScript::Language::JavaScript:
1553 return protocol::Debugger::ScriptLanguageEnum::JavaScript;
1554 }
1555}
1556
1557static const char* getDebugSymbolTypeName(
1558 v8::debug::WasmScript::DebugSymbolsType type) {
1559 switch (type) {
1560 case v8::debug::WasmScript::DebugSymbolsType::None:
1561 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::None;
1562 case v8::debug::WasmScript::DebugSymbolsType::SourceMap:
1563 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1564 SourceMap;
1565 case v8::debug::WasmScript::DebugSymbolsType::EmbeddedDWARF:
1566 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1567 EmbeddedDWARF;
1568 case v8::debug::WasmScript::DebugSymbolsType::ExternalDWARF:
1569 return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
1570 ExternalDWARF;
1571 }
1572}
1573
1574static std::unique_ptr<protocol::Debugger::DebugSymbols> getDebugSymbols(
1575 const V8DebuggerScript& script) {
1576 v8::debug::WasmScript::DebugSymbolsType type;
1577 if (!script.getDebugSymbolsType().To(&type)) return {};
1578
1579 std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
1580 v8_inspector::protocol::Debugger::DebugSymbols::create()
1581 .setType(getDebugSymbolTypeName(type))
1582 .build();
1583 String16 externalUrl;
1584 if (script.getExternalDebugSymbolsURL().To(&externalUrl)) {
1585 debugSymbols->setExternalURL(externalUrl);
1586 }
1587 return debugSymbols;
1588}
Ben Murdoch62ed6312017-06-06 11:06:27 +01001589
Ben Murdochf3b273f2017-01-17 12:11:28 +00001590void V8DebuggerAgentImpl::didParseSource(
1591 std::unique_ptr<V8DebuggerScript> script, bool success) {
1592 v8::HandleScope handles(m_isolate);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001593 if (!success) {
1594 DCHECK(!script->isSourceLoadedLazily());
1595 String16 scriptSource = script->source(0);
1596 script->setSourceURL(findSourceURL(scriptSource, false));
Ben Murdochf3b273f2017-01-17 12:11:28 +00001597 script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
Rubin Xu7bc1b612021-02-16 09:38:50 +00001598 }
Ben Murdochf3b273f2017-01-17 12:11:28 +00001599
Ben Murdoch62ed6312017-06-06 11:06:27 +01001600 int contextId = script->executionContextId();
1601 int contextGroupId = m_inspector->contextGroupId(contextId);
1602 InspectedContext* inspected =
1603 m_inspector->getContext(contextGroupId, contextId);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001604 std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
Ben Murdoch62ed6312017-06-06 11:06:27 +01001605 if (inspected) {
1606 // Script reused between different groups/sessions can have a stale
1607 // execution context id.
Rubin Xu7bc1b612021-02-16 09:38:50 +00001608 const String16& aux = inspected->auxData();
1609 std::vector<uint8_t> cbor;
1610 v8_crdtp::json::ConvertJSONToCBOR(
1611 v8_crdtp::span<uint16_t>(aux.characters16(), aux.length()), &cbor);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001612 executionContextAuxData = protocol::DictionaryValue::cast(
Rubin Xu7bc1b612021-02-16 09:38:50 +00001613 protocol::Value::parseBinary(cbor.data(), cbor.size()));
Ben Murdoch62ed6312017-06-06 11:06:27 +01001614 }
Ben Murdochf3b273f2017-01-17 12:11:28 +00001615 bool isLiveEdit = script->isLiveEdit();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001616 bool hasSourceURLComment = script->hasSourceURLComment();
Ben Murdoch62ed6312017-06-06 11:06:27 +01001617 bool isModule = script->isModule();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001618 String16 scriptId = script->scriptId();
1619 String16 scriptURL = script->sourceURL();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001620 String16 embedderName = script->embedderName();
1621 String16 scriptLanguage = getScriptLanguage(*script);
1622 Maybe<int> codeOffset;
1623 if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly)
1624 codeOffset = script->codeOffset();
1625 std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
1626 getDebugSymbols(*script);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001627
Ben Murdoch62ed6312017-06-06 11:06:27 +01001628 m_scripts[scriptId] = std::move(script);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001629 // Release the strong reference to get notified when debugger is the only
1630 // one that holds the script. Has to be done after script added to m_scripts.
1631 m_scripts[scriptId]->MakeWeak();
Ben Murdoch62ed6312017-06-06 11:06:27 +01001632
1633 ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
1634 DCHECK(scriptIterator != m_scripts.end());
1635 V8DebuggerScript* scriptRef = scriptIterator->second.get();
1636 // V8 could create functions for parsed scripts before reporting and asks
1637 // inspector about blackboxed state, we should reset state each time when we
1638 // make any change that change isFunctionBlackboxed output - adding parsed
1639 // script is changing.
1640 scriptRef->resetBlackboxedStateCache();
1641
1642 Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001643 Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
Ben Murdochf3b273f2017-01-17 12:11:28 +00001644 std::move(executionContextAuxData));
1645 const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
Rubin Xu7bc1b612021-02-16 09:38:50 +00001646 const bool* hasSourceURLParam =
1647 hasSourceURLComment ? &hasSourceURLComment : nullptr;
Ben Murdoch62ed6312017-06-06 11:06:27 +01001648 const bool* isModuleParam = isModule ? &isModule : nullptr;
Rubin Xu7bc1b612021-02-16 09:38:50 +00001649 std::unique_ptr<V8StackTraceImpl> stack =
1650 V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
1651 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
1652 stack && !stack->isEmpty()
1653 ? stack->buildInspectorObjectImpl(m_debugger, 0)
1654 : nullptr;
1655
1656 if (!success) {
Ben Murdochf3b273f2017-01-17 12:11:28 +00001657 m_frontend.scriptFailedToParse(
Ben Murdoch62ed6312017-06-06 11:06:27 +01001658 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1659 scriptRef->endLine(), scriptRef->endColumn(), contextId,
Rubin Xu7bc1b612021-02-16 09:38:50 +00001660 scriptRef->hash(), std::move(executionContextAuxDataParam),
1661 std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
1662 scriptRef->length(), std::move(stackTrace), std::move(codeOffset),
1663 std::move(scriptLanguage), embedderName);
1664 return;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001665 }
Rubin Xu7bc1b612021-02-16 09:38:50 +00001666
1667 if (scriptRef->isSourceLoadedLazily()) {
1668 m_frontend.scriptParsed(
1669 scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(),
1670 std::move(executionContextAuxDataParam), isLiveEditParam,
1671 std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0,
1672 std::move(stackTrace), std::move(codeOffset), std::move(scriptLanguage),
1673 std::move(debugSymbols), embedderName);
1674 } else {
1675 m_frontend.scriptParsed(
1676 scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1677 scriptRef->endLine(), scriptRef->endColumn(), contextId,
1678 scriptRef->hash(), std::move(executionContextAuxDataParam),
1679 isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
1680 isModuleParam, scriptRef->length(), std::move(stackTrace),
1681 std::move(codeOffset), std::move(scriptLanguage),
1682 std::move(debugSymbols), embedderName);
1683 }
1684
1685 std::vector<protocol::DictionaryValue*> potentialBreakpoints;
1686 if (!scriptURL.isEmpty()) {
1687 protocol::DictionaryValue* breakpointsByUrl =
1688 m_state->getObject(DebuggerAgentState::breakpointsByUrl);
1689 if (breakpointsByUrl) {
1690 potentialBreakpoints.push_back(breakpointsByUrl->getObject(scriptURL));
1691 }
1692 potentialBreakpoints.push_back(
1693 m_state->getObject(DebuggerAgentState::breakpointsByRegex));
1694 }
1695 protocol::DictionaryValue* breakpointsByScriptHash =
1696 m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
1697 if (breakpointsByScriptHash) {
1698 potentialBreakpoints.push_back(
1699 breakpointsByScriptHash->getObject(scriptRef->hash()));
1700 }
1701 protocol::DictionaryValue* breakpointHints =
1702 m_state->getObject(DebuggerAgentState::breakpointHints);
1703 for (auto breakpoints : potentialBreakpoints) {
1704 if (!breakpoints) continue;
1705 for (size_t i = 0; i < breakpoints->size(); ++i) {
1706 auto breakpointWithCondition = breakpoints->at(i);
1707 String16 breakpointId = breakpointWithCondition.first;
1708
1709 BreakpointType type;
1710 String16 selector;
1711 int lineNumber = 0;
1712 int columnNumber = 0;
1713 parseBreakpointId(breakpointId, &type, &selector, &lineNumber,
1714 &columnNumber);
1715
1716 if (!matches(m_inspector, *scriptRef, type, selector)) continue;
1717 String16 condition;
1718 breakpointWithCondition.second->asString(&condition);
1719 String16 hint;
1720 bool hasHint =
1721 breakpointHints && breakpointHints->getString(breakpointId, &hint);
1722 if (hasHint) {
1723 adjustBreakpointLocation(*scriptRef, hint, &lineNumber, &columnNumber);
1724 }
1725 std::unique_ptr<protocol::Debugger::Location> location =
1726 setBreakpointImpl(breakpointId, scriptId, condition, lineNumber,
1727 columnNumber);
1728 if (location)
1729 m_frontend.breakpointResolved(breakpointId, std::move(location));
1730 }
1731 }
1732 setScriptInstrumentationBreakpointIfNeeded(scriptRef);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001733}
1734
Rubin Xu7bc1b612021-02-16 09:38:50 +00001735void V8DebuggerAgentImpl::setScriptInstrumentationBreakpointIfNeeded(
1736 V8DebuggerScript* scriptRef) {
1737 protocol::DictionaryValue* breakpoints =
1738 m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
1739 if (!breakpoints) return;
1740 bool isBlackboxed = isFunctionBlackboxed(
1741 scriptRef->scriptId(), v8::debug::Location(0, 0),
1742 v8::debug::Location(scriptRef->endLine(), scriptRef->endColumn()));
1743 if (isBlackboxed) return;
1744
1745 String16 sourceMapURL = scriptRef->sourceMappingURL();
1746 String16 breakpointId = generateInstrumentationBreakpointId(
1747 InstrumentationEnum::BeforeScriptExecution);
1748 if (!breakpoints->get(breakpointId)) {
1749 if (sourceMapURL.isEmpty()) return;
1750 breakpointId = generateInstrumentationBreakpointId(
1751 InstrumentationEnum::BeforeScriptWithSourceMapExecution);
1752 if (!breakpoints->get(breakpointId)) return;
1753 }
1754 v8::debug::BreakpointId debuggerBreakpointId;
1755 if (!scriptRef->setBreakpointOnRun(&debuggerBreakpointId)) return;
1756 std::unique_ptr<protocol::DictionaryValue> data =
1757 protocol::DictionaryValue::create();
1758 data->setString("url", scriptRef->sourceURL());
1759 data->setString("scriptId", scriptRef->scriptId());
1760 if (!sourceMapURL.isEmpty()) data->setString("sourceMapURL", sourceMapURL);
1761
1762 m_breakpointsOnScriptRun[debuggerBreakpointId] = std::move(data);
1763 m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
1764 m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
1765 debuggerBreakpointId);
1766}
1767
1768void V8DebuggerAgentImpl::didPause(
1769 int contextId, v8::Local<v8::Value> exception,
1770 const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
1771 v8::debug::ExceptionType exceptionType, bool isUncaught, bool isOOMBreak,
1772 bool isAssert) {
Ben Murdochf3b273f2017-01-17 12:11:28 +00001773 v8::HandleScope handles(m_isolate);
1774
Ben Murdoch62ed6312017-06-06 11:06:27 +01001775 std::vector<BreakReason> hitReasons;
1776
1777 if (isOOMBreak) {
1778 hitReasons.push_back(
1779 std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
Rubin Xu7bc1b612021-02-16 09:38:50 +00001780 } else if (isAssert) {
1781 hitReasons.push_back(std::make_pair(
1782 protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
Ben Murdoch62ed6312017-06-06 11:06:27 +01001783 } else if (!exception.IsEmpty()) {
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001784 InjectedScript* injectedScript = nullptr;
Ben Murdoch62ed6312017-06-06 11:06:27 +01001785 m_session->findInjectedScript(contextId, injectedScript);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001786 if (injectedScript) {
Ben Murdoch62ed6312017-06-06 11:06:27 +01001787 String16 breakReason =
Rubin Xu7bc1b612021-02-16 09:38:50 +00001788 exceptionType == v8::debug::kPromiseRejection
Ben Murdochf3b273f2017-01-17 12:11:28 +00001789 ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
1790 : protocol::Debugger::Paused::ReasonEnum::Exception;
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001791 std::unique_ptr<protocol::Runtime::RemoteObject> obj;
Rubin Xu7bc1b612021-02-16 09:38:50 +00001792 injectedScript->wrapObject(exception, kBacktraceObjectGroup,
1793 WrapMode::kNoPreview, &obj);
Ben Murdoch62ed6312017-06-06 11:06:27 +01001794 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001795 if (obj) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001796 std::vector<uint8_t> serialized;
1797 obj->AppendSerialized(&serialized);
1798 breakAuxData = protocol::DictionaryValue::cast(
1799 protocol::Value::parseBinary(serialized.data(), serialized.size()));
Ben Murdoch62ed6312017-06-06 11:06:27 +01001800 breakAuxData->setBoolean("uncaught", isUncaught);
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001801 }
Ben Murdoch62ed6312017-06-06 11:06:27 +01001802 hitReasons.push_back(
1803 std::make_pair(breakReason, std::move(breakAuxData)));
Ben Murdochf3b273f2017-01-17 12:11:28 +00001804 }
1805 }
1806
Rubin Xu7bc1b612021-02-16 09:38:50 +00001807 auto hitBreakpointIds = std::make_unique<Array<String16>>();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001808
Rubin Xu7bc1b612021-02-16 09:38:50 +00001809 for (const auto& id : hitBreakpoints) {
1810 auto it = m_breakpointsOnScriptRun.find(id);
1811 if (it != m_breakpointsOnScriptRun.end()) {
1812 hitReasons.push_back(std::make_pair(
1813 protocol::Debugger::Paused::ReasonEnum::Instrumentation,
1814 std::move(it->second)));
1815 m_breakpointsOnScriptRun.erase(it);
1816 continue;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001817 }
Rubin Xu7bc1b612021-02-16 09:38:50 +00001818 auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
1819 if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
1820 continue;
1821 }
1822 const String16& breakpointId = breakpointIterator->second;
1823 hitBreakpointIds->emplace_back(breakpointId);
1824 BreakpointType type;
1825 parseBreakpointId(breakpointId, &type);
1826 if (type != BreakpointType::kDebugCommand) continue;
1827 hitReasons.push_back(std::make_pair(
1828 protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
Ben Murdochf3b273f2017-01-17 12:11:28 +00001829 }
1830
Ben Murdoch62ed6312017-06-06 11:06:27 +01001831 for (size_t i = 0; i < m_breakReason.size(); ++i) {
1832 hitReasons.push_back(std::move(m_breakReason[i]));
1833 }
1834 clearBreakDetails();
1835
1836 String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
1837 std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1838 if (hitReasons.size() == 1) {
1839 breakReason = hitReasons[0].first;
1840 breakAuxData = std::move(hitReasons[0].second);
1841 } else if (hitReasons.size() > 1) {
1842 breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
1843 std::unique_ptr<protocol::ListValue> reasons =
1844 protocol::ListValue::create();
1845 for (size_t i = 0; i < hitReasons.size(); ++i) {
1846 std::unique_ptr<protocol::DictionaryValue> reason =
1847 protocol::DictionaryValue::create();
1848 reason->setString("reason", hitReasons[i].first);
1849 if (hitReasons[i].second)
1850 reason->setObject("auxData", std::move(hitReasons[i].second));
1851 reasons->pushValue(std::move(reason));
1852 }
1853 breakAuxData = protocol::DictionaryValue::create();
1854 breakAuxData->setArray("reasons", std::move(reasons));
1855 }
1856
Ben Murdochc8c1d9e2017-03-08 14:04:23 +00001857 std::unique_ptr<Array<CallFrame>> protocolCallFrames;
1858 Response response = currentCallFrames(&protocolCallFrames);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001859 if (!response.IsSuccess())
1860 protocolCallFrames = std::make_unique<Array<CallFrame>>();
1861
Ben Murdoch62ed6312017-06-06 11:06:27 +01001862 m_frontend.paused(std::move(protocolCallFrames), breakReason,
1863 std::move(breakAuxData), std::move(hitBreakpointIds),
Rubin Xu7bc1b612021-02-16 09:38:50 +00001864 currentAsyncStackTrace(), currentExternalStackTrace());
Ben Murdochf3b273f2017-01-17 12:11:28 +00001865}
1866
1867void V8DebuggerAgentImpl::didContinue() {
Ben Murdochf3b273f2017-01-17 12:11:28 +00001868 clearBreakDetails();
1869 m_frontend.resumed();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001870 m_frontend.flush();
Ben Murdochf3b273f2017-01-17 12:11:28 +00001871}
1872
1873void V8DebuggerAgentImpl::breakProgram(
1874 const String16& breakReason,
1875 std::unique_ptr<protocol::DictionaryValue> data) {
Rubin Xu7bc1b612021-02-16 09:38:50 +00001876 if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return;
Ben Murdoch62ed6312017-06-06 11:06:27 +01001877 std::vector<BreakReason> currentScheduledReason;
1878 currentScheduledReason.swap(m_breakReason);
1879 pushBreakDetails(breakReason, std::move(data));
Rubin Xu7bc1b612021-02-16 09:38:50 +00001880
1881 int contextGroupId = m_session->contextGroupId();
1882 int sessionId = m_session->sessionId();
1883 V8InspectorImpl* inspector = m_inspector;
1884 m_debugger->breakProgram(contextGroupId);
1885 // Check that session and |this| are still around.
1886 if (!inspector->sessionById(contextGroupId, sessionId)) return;
1887 if (!enabled()) return;
1888
Ben Murdoch62ed6312017-06-06 11:06:27 +01001889 popBreakDetails();
1890 m_breakReason.swap(currentScheduledReason);
Rubin Xu7bc1b612021-02-16 09:38:50 +00001891 if (!m_breakReason.empty()) {
1892 m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1893 }
Ben Murdochf3b273f2017-01-17 12:11:28 +00001894}
1895
Rubin Xu7bc1b612021-02-16 09:38:50 +00001896void V8DebuggerAgentImpl::setBreakpointFor(v8::Local<v8::Function> function,
1897 v8::Local<v8::String> condition,
1898 BreakpointSource source) {
1899 String16 breakpointId = generateBreakpointId(
1900 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1901 : BreakpointType::kMonitorCommand,
1902 function);
1903 if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
1904 m_breakpointIdToDebuggerBreakpointIds.end()) {
Ben Murdochf3b273f2017-01-17 12:11:28 +00001905 return;
Rubin Xu7bc1b612021-02-16 09:38:50 +00001906 }
1907 setBreakpointImpl(breakpointId, function, condition);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001908}
1909
Rubin Xu7bc1b612021-02-16 09:38:50 +00001910void V8DebuggerAgentImpl::removeBreakpointFor(v8::Local<v8::Function> function,
1911 BreakpointSource source) {
1912 String16 breakpointId = generateBreakpointId(
1913 source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
1914 : BreakpointType::kMonitorCommand,
1915 function);
1916 std::vector<V8DebuggerScript*> scripts;
1917 removeBreakpointImpl(breakpointId, scripts);
Ben Murdochf3b273f2017-01-17 12:11:28 +00001918}
1919
1920void V8DebuggerAgentImpl::reset() {
1921 if (!enabled()) return;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001922 m_blackboxedPositions.clear();
Ben Murdoch62ed6312017-06-06 11:06:27 +01001923 resetBlackboxedStateCache();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001924 m_skipList.clear();
Ben Murdoch62ed6312017-06-06 11:06:27 +01001925 m_scripts.clear();
Rubin Xu7bc1b612021-02-16 09:38:50 +00001926 m_cachedScriptIds.clear();
1927 m_cachedScriptSize = 0;
Ben Murdochf3b273f2017-01-17 12:11:28 +00001928}
1929
Rubin Xu7bc1b612021-02-16 09:38:50 +00001930void V8DebuggerAgentImpl::ScriptCollected(const V8DebuggerScript* script) {
1931 DCHECK_NE(m_scripts.find(script->scriptId()), m_scripts.end());
1932 m_cachedScriptIds.push_back(script->scriptId());
1933 // TODO(alph): Properly calculate size when sources are one-byte strings.
1934 m_cachedScriptSize += script->length() * sizeof(uint16_t);
1935
1936 while (m_cachedScriptSize > m_maxScriptCacheSize) {
1937 const String16& scriptId = m_cachedScriptIds.front();
1938 size_t scriptSize = m_scripts[scriptId]->length() * sizeof(uint16_t);
1939 DCHECK_GE(m_cachedScriptSize, scriptSize);
1940 m_cachedScriptSize -= scriptSize;
1941 m_scripts.erase(scriptId);
1942 m_cachedScriptIds.pop_front();
1943 }
1944}
1945
1946Response V8DebuggerAgentImpl::processSkipList(
1947 protocol::Array<protocol::Debugger::LocationRange>* skipList) {
1948 std::unordered_map<String16, std::vector<std::pair<int, int>>> skipListInit;
1949 for (std::unique_ptr<protocol::Debugger::LocationRange>& range : *skipList) {
1950 protocol::Debugger::ScriptPosition* start = range->getStart();
1951 protocol::Debugger::ScriptPosition* end = range->getEnd();
1952 String16 scriptId = range->getScriptId();
1953
1954 auto it = m_scripts.find(scriptId);
1955 if (it == m_scripts.end())
1956 return Response::ServerError("No script with passed id.");
1957
1958 Response res = isValidPosition(start);
1959 if (res.IsError()) return res;
1960
1961 res = isValidPosition(end);
1962 if (res.IsError()) return res;
1963
1964 skipListInit[scriptId].emplace_back(start->getLineNumber(),
1965 start->getColumnNumber());
1966 skipListInit[scriptId].emplace_back(end->getLineNumber(),
1967 end->getColumnNumber());
1968 }
1969
1970 // Verify that the skipList is sorted, and that all ranges
1971 // are properly defined (start comes before end).
1972 for (auto skipListPair : skipListInit) {
1973 Response res = isValidRangeOfPositions(skipListPair.second);
1974 if (res.IsError()) return res;
1975 }
1976
1977 m_skipList = std::move(skipListInit);
1978 return Response::Success();
1979}
Ben Murdochf3b273f2017-01-17 12:11:28 +00001980} // namespace v8_inspector