| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1 | // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "src/inspector/v8-debugger-agent-impl.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 9 | #include "../../third_party/inspector_protocol/crdtp/json.h" |
| 10 | #include "include/v8-inspector.h" |
| 11 | #include "src/base/safe_conversions.h" |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 12 | #include "src/debug/debug-interface.h" |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 13 | #include "src/inspector/injected-script.h" |
| 14 | #include "src/inspector/inspected-context.h" |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 15 | #include "src/inspector/protocol/Debugger.h" |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 16 | #include "src/inspector/protocol/Protocol.h" |
| 17 | #include "src/inspector/remote-object-id.h" |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 18 | #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 Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 27 | #include "src/inspector/v8-value-utils.h" |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 28 | |
| 29 | namespace v8_inspector { |
| 30 | |
| 31 | using protocol::Array; |
| 32 | using protocol::Maybe; |
| 33 | using protocol::Debugger::BreakpointId; |
| 34 | using protocol::Debugger::CallFrame; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 35 | using protocol::Debugger::Scope; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 36 | using protocol::Runtime::ExceptionDetails; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 37 | using protocol::Runtime::RemoteObject; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 38 | using protocol::Runtime::ScriptId; |
| 39 | |
| 40 | namespace InstrumentationEnum = |
| 41 | protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 42 | |
| 43 | namespace DebuggerAgentState { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 44 | static const char pauseOnExceptionsState[] = "pauseOnExceptionsState"; |
| 45 | static const char asyncCallStackDepth[] = "asyncCallStackDepth"; |
| 46 | static const char blackboxPattern[] = "blackboxPattern"; |
| 47 | static const char debuggerEnabled[] = "debuggerEnabled"; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 48 | static const char skipAllPauses[] = "skipAllPauses"; |
| 49 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 50 | static const char breakpointsByRegex[] = "breakpointsByRegex"; |
| 51 | static const char breakpointsByUrl[] = "breakpointsByUrl"; |
| 52 | static const char breakpointsByScriptHash[] = "breakpointsByScriptHash"; |
| 53 | static const char breakpointHints[] = "breakpointHints"; |
| 54 | static const char instrumentationBreakpoints[] = "instrumentationBreakpoints"; |
| 55 | |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 56 | } // namespace DebuggerAgentState |
| 57 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 58 | static const char kBacktraceObjectGroup[] = "backtrace"; |
| 59 | static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled"; |
| 60 | static const char kDebuggerNotPaused[] = |
| 61 | "Can only perform operation while paused."; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 62 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 63 | static const size_t kBreakpointHintMaxLength = 128; |
| 64 | static 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). |
| 67 | static 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. |
| 76 | static const size_t kWasmBytecodeMaxLength = (v8::String::kMaxLength / 4) * 3; |
| 77 | static const char kWasmBytecodeExceedsTransferLimit[] = |
| 78 | "WebAssembly bytecode exceeds the transfer limit"; |
| 79 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 80 | namespace { |
| 81 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 82 | enum class BreakpointType { |
| 83 | kByUrl = 1, |
| 84 | kByUrlRegex, |
| 85 | kByScriptHash, |
| 86 | kByScriptId, |
| 87 | kDebugCommand, |
| 88 | kMonitorCommand, |
| 89 | kBreakpointAtEntry, |
| 90 | kInstrumentationBreakpoint |
| 91 | }; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 92 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 93 | String16 generateBreakpointId(BreakpointType type, |
| 94 | const String16& scriptSelector, int lineNumber, |
| 95 | int columnNumber) { |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 96 | String16Builder builder; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 97 | builder.appendNumber(static_cast<int>(type)); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 98 | builder.append(':'); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 99 | builder.appendNumber(lineNumber); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 100 | builder.append(':'); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 101 | builder.appendNumber(columnNumber); |
| 102 | builder.append(':'); |
| 103 | builder.append(scriptSelector); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 104 | return builder.toString(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 105 | } |
| 106 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 107 | String16 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 | |
| 116 | String16 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 | |
| 125 | bool 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 169 | bool positionComparator(const std::pair<int, int>& a, |
| 170 | const std::pair<int, int>& b) { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 171 | if (a.first != b.first) return a.first < b.first; |
| 172 | return a.second < b.second; |
| 173 | } |
| 174 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 175 | String16 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 187 | } |
| 188 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 189 | void 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 | |
| 225 | String16 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 | |
| 239 | String16 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 | |
| 266 | Response 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 | |
| 311 | protocol::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 | |
| 322 | Response 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 | |
| 330 | Response 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 341 | } // namespace |
| 342 | |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 343 | V8DebuggerAgentImpl::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 Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 352 | m_isolate(m_inspector->isolate()) {} |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 353 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 354 | V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 355 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 356 | void V8DebuggerAgentImpl::enableImpl() { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 357 | m_enabled = true; |
| 358 | m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true); |
| 359 | m_debugger->enable(); |
| 360 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 361 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 366 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 367 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 374 | } |
| 375 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 376 | Response 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 383 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 384 | if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId())) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 385 | return Response::ServerError("Script execution is prohibited"); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 386 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 387 | enableImpl(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 388 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 389 | } |
| 390 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 391 | Response V8DebuggerAgentImpl::disable() { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 392 | if (!enabled()) return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 393 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 394 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 400 | m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 401 | v8::debug::NoBreakOnException); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 402 | m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0); |
| 403 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 404 | if (m_breakpointsActive) { |
| 405 | m_debugger->setBreakpointsActive(false); |
| 406 | m_breakpointsActive = false; |
| 407 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 408 | m_blackboxedPositions.clear(); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 409 | m_blackboxPattern.reset(); |
| 410 | resetBlackboxedStateCache(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 411 | m_skipList.clear(); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 412 | m_scripts.clear(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 413 | m_cachedScriptIds.clear(); |
| 414 | m_cachedScriptSize = 0; |
| 415 | for (const auto& it : m_debuggerBreakpointIdToBreakpointId) { |
| 416 | v8::debug::RemoveBreakpoint(m_isolate, it.first); |
| 417 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 418 | m_breakpointIdToDebuggerBreakpointIds.clear(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 419 | m_debuggerBreakpointIdToBreakpointId.clear(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 420 | m_debugger->setAsyncCallStackDepth(this, 0); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 421 | clearBreakDetails(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 422 | m_skipAllPauses = false; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 423 | m_state->setBoolean(DebuggerAgentState::skipAllPauses, false); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 424 | m_state->remove(DebuggerAgentState::blackboxPattern); |
| 425 | m_enabled = false; |
| 426 | m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 427 | m_debugger->disable(); |
| 428 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 429 | } |
| 430 | |
| 431 | void 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 438 | enableImpl(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 439 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 440 | int pauseState = v8::debug::NoBreakOnException; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 441 | m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 442 | setPauseOnExceptionsImpl(pauseState); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 443 | |
| 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 455 | setBlackboxPattern(blackboxPattern); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 456 | } |
| 457 | } |
| 458 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 459 | Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 460 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 469 | } |
| 470 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 471 | Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 472 | m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 473 | m_skipAllPauses = skip; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 474 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 475 | } |
| 476 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 477 | static 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 493 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 494 | } |
| 495 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 496 | Response V8DebuggerAgentImpl::setBreakpointByUrl( |
| 497 | int lineNumber, Maybe<String16> optionalURL, |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 498 | Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash, |
| 499 | Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition, |
| 500 | String16* outBreakpointId, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 501 | std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 502 | *locations = std::make_unique<Array<protocol::Debugger::Location>>(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 503 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 504 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 511 | int columnNumber = 0; |
| 512 | if (optionalColumnNumber.isJust()) { |
| 513 | columnNumber = optionalColumnNumber.fromJust(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 514 | if (columnNumber < 0) |
| 515 | return Response::ServerError("Incorrect column number"); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 516 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 517 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 518 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 529 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 530 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 531 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 558 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 559 | String16 hint; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 560 | for (const auto& script : m_scripts) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 561 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 572 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 573 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 579 | *outBreakpointId = breakpointId; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 580 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 581 | } |
| 582 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 583 | Response V8DebuggerAgentImpl::setBreakpoint( |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 584 | std::unique_ptr<protocol::Debugger::Location> location, |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 585 | Maybe<String16> optionalCondition, String16* outBreakpointId, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 586 | std::unique_ptr<protocol::Debugger::Location>* actualLocation) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 587 | String16 breakpointId = generateBreakpointId( |
| 588 | BreakpointType::kByScriptId, location->getScriptId(), |
| 589 | location->getLineNumber(), location->getColumnNumber(0)); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 590 | if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) != |
| 591 | m_breakpointIdToDebuggerBreakpointIds.end()) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 592 | return Response::ServerError( |
| 593 | "Breakpoint at specified location already exists."); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 594 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 595 | *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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 601 | *outBreakpointId = breakpointId; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 602 | return Response::Success(); |
| 603 | } |
| 604 | |
| 605 | Response 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 | |
| 630 | Response 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 643 | } |
| 644 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 645 | Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 646 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 695 | } |
| 696 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 697 | void V8DebuggerAgentImpl::removeBreakpointImpl( |
| 698 | const String16& breakpointId, |
| 699 | const std::vector<V8DebuggerScript*>& scripts) { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 700 | DCHECK(enabled()); |
| 701 | BreakpointIdToDebuggerBreakpointIdsMap::iterator |
| 702 | debuggerBreakpointIdsIterator = |
| 703 | m_breakpointIdToDebuggerBreakpointIds.find(breakpointId); |
| 704 | if (debuggerBreakpointIdsIterator == |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 705 | m_breakpointIdToDebuggerBreakpointIds.end()) { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 706 | return; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 707 | } |
| 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 714 | } |
| 715 | m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId); |
| 716 | } |
| 717 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 718 | Response V8DebuggerAgentImpl::getPossibleBreakpoints( |
| 719 | std::unique_ptr<protocol::Debugger::Location> start, |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 720 | Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction, |
| 721 | std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>* |
| 722 | locations) { |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 723 | String16 scriptId = start->getScriptId(); |
| 724 | |
| 725 | if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 726 | return Response::ServerError( |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 727 | "start.lineNumber and start.columnNumber should be >= 0"); |
| 728 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 729 | v8::debug::Location v8Start(start->getLineNumber(), |
| 730 | start->getColumnNumber(0)); |
| 731 | v8::debug::Location v8End; |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 732 | if (end.isJust()) { |
| 733 | if (end.fromJust()->getScriptId() != scriptId) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 734 | return Response::ServerError( |
| 735 | "Locations should contain the same scriptId"); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 736 | int line = end.fromJust()->getLineNumber(); |
| 737 | int column = end.fromJust()->getColumnNumber(0); |
| 738 | if (line < 0 || column < 0) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 739 | return Response::ServerError( |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 740 | "end.lineNumber and end.columnNumber should be >= 0"); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 741 | v8End = v8::debug::Location(line, column); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 742 | } |
| 743 | auto it = m_scripts.find(scriptId); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 744 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 759 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 760 | |
| 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 781 | } |
| 782 | |
| 783 | Response V8DebuggerAgentImpl::continueToLocation( |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 784 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 791 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 792 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 804 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 805 | Response 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 811 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 812 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 832 | } |
| 833 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 834 | bool 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 838 | 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 848 | auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 849 | if (itBlackboxedPositions == m_blackboxedPositions.end()) return false; |
| 850 | |
| 851 | const std::vector<std::pair<int, int>>& ranges = |
| 852 | itBlackboxedPositions->second; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 853 | auto itStartRange = std::lower_bound( |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 854 | ranges.begin(), ranges.end(), |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 855 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 861 | // 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 864 | return itStartRange == itEndRange && |
| 865 | std::distance(ranges.begin(), itStartRange) % 2; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 866 | } |
| 867 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 868 | bool 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 | |
| 895 | bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const { |
| 896 | return enabled() && (isOOMBreak || !m_skipAllPauses); |
| 897 | } |
| 898 | |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 899 | std::unique_ptr<protocol::Debugger::Location> |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 900 | V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId, |
| 901 | const String16& scriptId, |
| 902 | const String16& condition, |
| 903 | int lineNumber, int columnNumber) { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 904 | v8::HandleScope handles(m_isolate); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 905 | DCHECK(enabled()); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 906 | |
| 907 | ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 908 | if (scriptIterator == m_scripts.end()) return nullptr; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 909 | V8DebuggerScript* script = scriptIterator->second.get(); |
| 910 | if (lineNumber < script->startLine() || script->endLine() < lineNumber) { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 911 | return nullptr; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 912 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 913 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 914 | 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 919 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 920 | { |
| 921 | v8::Context::Scope contextScope(inspected->context()); |
| 922 | if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) { |
| 923 | return nullptr; |
| 924 | } |
| 925 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 926 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 927 | m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 928 | m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back( |
| 929 | debuggerBreakpointId); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 930 | |
| 931 | return protocol::Debugger::Location::create() |
| 932 | .setScriptId(scriptId) |
| 933 | .setLineNumber(location.GetLineNumber()) |
| 934 | .setColumnNumber(location.GetColumnNumber()) |
| 935 | .build(); |
| 936 | } |
| 937 | |
| 938 | void 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 949 | } |
| 950 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 951 | Response V8DebuggerAgentImpl::searchInContent( |
| 952 | const String16& scriptId, const String16& query, |
| 953 | Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 954 | std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) { |
| 955 | v8::HandleScope handles(m_isolate); |
| 956 | ScriptsMap::iterator it = m_scripts.find(scriptId); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 957 | if (it == m_scripts.end()) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 958 | return Response::ServerError("No script for id: " + scriptId.utf8()); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 959 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 960 | *results = std::make_unique<protocol::Array<protocol::Debugger::SearchMatch>>( |
| 961 | searchInTextByLinesImpl(m_session, it->second->source(0), query, |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 962 | optionalCaseSensitive.fromMaybe(false), |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 963 | optionalIsRegex.fromMaybe(false))); |
| 964 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 965 | } |
| 966 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 967 | Response V8DebuggerAgentImpl::setScriptSource( |
| 968 | const String16& scriptId, const String16& newContent, Maybe<bool> dryRun, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 969 | Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames, |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 970 | Maybe<bool>* stackChanged, |
| 971 | Maybe<protocol::Runtime::StackTrace>* asyncStackTrace, |
| 972 | Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 973 | Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 974 | if (!enabled()) return Response::ServerError(kDebuggerNotEnabled); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 975 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 976 | ScriptsMap::iterator it = m_scripts.find(scriptId); |
| 977 | if (it == m_scripts.end()) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 978 | return Response::ServerError("No script with given id found"); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 979 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 980 | int contextId = it->second->executionContextId(); |
| 981 | InspectedContext* inspected = m_inspector->getContext(contextId); |
| 982 | if (!inspected) { |
| 983 | return Response::InternalError(); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 984 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 985 | v8::HandleScope handleScope(m_isolate); |
| 986 | v8::Local<v8::Context> context = inspected->context(); |
| 987 | v8::Context::Scope contextScope(context); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 988 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 989 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1005 | std::unique_ptr<Array<CallFrame>> callFrames; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1006 | Response response = currentCallFrames(&callFrames); |
| 1007 | if (!response.IsSuccess()) return response; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1008 | *newCallFrames = std::move(callFrames); |
| 1009 | *asyncStackTrace = currentAsyncStackTrace(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1010 | *asyncStackTraceId = currentExternalStackTrace(); |
| 1011 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1012 | } |
| 1013 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1014 | Response V8DebuggerAgentImpl::restartFrame( |
| 1015 | const String16& callFrameId, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1016 | std::unique_ptr<Array<CallFrame>>* newCallFrames, |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1017 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1021 | Response response = scope.initialize(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1022 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1029 | return Response::InternalError(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1030 | } |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1031 | response = currentCallFrames(newCallFrames); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1032 | if (!response.IsSuccess()) return response; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1033 | *asyncStackTrace = currentAsyncStackTrace(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1034 | *asyncStackTraceId = currentExternalStackTrace(); |
| 1035 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1036 | } |
| 1037 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1038 | Response V8DebuggerAgentImpl::getScriptSource( |
| 1039 | const String16& scriptId, String16* scriptSource, |
| 1040 | Maybe<protocol::Binary>* bytecode) { |
| 1041 | if (!enabled()) return Response::ServerError(kDebuggerNotEnabled); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1042 | ScriptsMap::iterator it = m_scripts.find(scriptId); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1043 | if (it == m_scripts.end()) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1044 | 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 | |
| 1056 | Response 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1071 | } |
| 1072 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1073 | void 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 | |
| 1079 | void V8DebuggerAgentImpl::popBreakDetails() { |
| 1080 | if (m_breakReason.empty()) return; |
| 1081 | m_breakReason.pop_back(); |
| 1082 | } |
| 1083 | |
| 1084 | void V8DebuggerAgentImpl::clearBreakDetails() { |
| 1085 | std::vector<BreakReason> emptyBreakReason; |
| 1086 | m_breakReason.swap(emptyBreakReason); |
| 1087 | } |
| 1088 | |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1089 | void V8DebuggerAgentImpl::schedulePauseOnNextStatement( |
| 1090 | const String16& breakReason, |
| 1091 | std::unique_ptr<protocol::DictionaryValue> data) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1092 | if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return; |
| 1093 | if (m_breakReason.empty()) { |
| 1094 | m_debugger->setPauseOnNextCall(true, m_session->contextGroupId()); |
| 1095 | } |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1096 | pushBreakDetails(breakReason, std::move(data)); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1097 | } |
| 1098 | |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1099 | void V8DebuggerAgentImpl::cancelPauseOnNextStatement() { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1100 | if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return; |
| 1101 | if (m_breakReason.size() == 1) { |
| 1102 | m_debugger->setPauseOnNextCall(false, m_session->contextGroupId()); |
| 1103 | } |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1104 | popBreakDetails(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1105 | } |
| 1106 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1107 | Response V8DebuggerAgentImpl::pause() { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1108 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1119 | } |
| 1120 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1121 | Response V8DebuggerAgentImpl::resume(Maybe<bool> terminateOnResume) { |
| 1122 | if (!isPaused()) return Response::ServerError(kDebuggerNotPaused); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1123 | m_session->releaseObjectGroup(kBacktraceObjectGroup); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1124 | m_debugger->continueProgram(m_session->contextGroupId(), |
| 1125 | terminateOnResume.fromMaybe(false)); |
| 1126 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1127 | } |
| 1128 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1129 | Response 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1140 | m_session->releaseObjectGroup(kBacktraceObjectGroup); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1141 | m_debugger->stepOverStatement(m_session->contextGroupId()); |
| 1142 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1143 | } |
| 1144 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1145 | Response 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1157 | m_session->releaseObjectGroup(kBacktraceObjectGroup); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1158 | m_debugger->stepIntoStatement(m_session->contextGroupId(), |
| 1159 | inBreakOnAsyncCall.fromMaybe(false)); |
| 1160 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1161 | } |
| 1162 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1163 | Response V8DebuggerAgentImpl::stepOut() { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1164 | if (!isPaused()) return Response::ServerError(kDebuggerNotPaused); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1165 | m_session->releaseObjectGroup(kBacktraceObjectGroup); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1166 | m_debugger->stepOutOfFunction(m_session->contextGroupId()); |
| 1167 | return Response::Success(); |
| 1168 | } |
| 1169 | |
| 1170 | Response V8DebuggerAgentImpl::pauseOnAsyncCall( |
| 1171 | std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) { |
| 1172 | // Deprecated, just return OK. |
| 1173 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1174 | } |
| 1175 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1176 | Response V8DebuggerAgentImpl::setPauseOnExceptions( |
| 1177 | const String16& stringPauseState) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1178 | if (!enabled()) return Response::ServerError(kDebuggerNotEnabled); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1179 | v8::debug::ExceptionBreakState pauseState; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1180 | if (stringPauseState == "none") { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1181 | pauseState = v8::debug::NoBreakOnException; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1182 | } else if (stringPauseState == "all") { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1183 | pauseState = v8::debug::BreakOnAnyException; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1184 | } else if (stringPauseState == "uncaught") { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1185 | pauseState = v8::debug::BreakOnUncaughtException; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1186 | } else { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1187 | return Response::ServerError("Unknown pause on exceptions mode: " + |
| 1188 | stringPauseState.utf8()); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1189 | } |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1190 | setPauseOnExceptionsImpl(pauseState); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1191 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1192 | } |
| 1193 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1194 | void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1195 | // 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1197 | m_debugger->setPauseOnExceptionsState( |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1198 | static_cast<v8::debug::ExceptionBreakState>(pauseState)); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1199 | m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1200 | } |
| 1201 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1202 | Response 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 Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1206 | Maybe<bool> throwOnSideEffect, Maybe<double> timeout, |
| 1207 | std::unique_ptr<RemoteObject>* result, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1208 | Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1209 | if (!isPaused()) return Response::ServerError(kDebuggerNotPaused); |
| 1210 | InjectedScript::CallFrameScope scope(m_session, callFrameId); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1211 | Response response = scope.initialize(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1212 | if (!response.IsSuccess()) return response; |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1213 | if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1214 | if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); |
| 1215 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1216 | 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 | |
| 1244 | Response 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1280 | |
| 1281 | // Re-initialize after running client's code, as it could have destroyed |
| 1282 | // context or session. |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1283 | response = scope.initialize(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1284 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1291 | } |
| 1292 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1293 | Response V8DebuggerAgentImpl::setVariableValue( |
| 1294 | int scopeNumber, const String16& variableName, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1295 | std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument, |
| 1296 | const String16& callFrameId) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1297 | if (!enabled()) return Response::ServerError(kDebuggerNotEnabled); |
| 1298 | if (!isPaused()) return Response::ServerError(kDebuggerNotPaused); |
| 1299 | InjectedScript::CallFrameScope scope(m_session, callFrameId); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1300 | Response response = scope.initialize(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1301 | if (!response.IsSuccess()) return response; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1302 | v8::Local<v8::Value> newValue; |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1303 | response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(), |
| 1304 | &newValue); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1305 | if (!response.IsSuccess()) return response; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1306 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1307 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1324 | return Response::InternalError(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1325 | } |
| 1326 | return Response::Success(); |
| 1327 | } |
| 1328 | |
| 1329 | Response 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1351 | } |
| 1352 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1353 | Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1354 | if (!enabled() && !m_session->runtimeAgent()->enabled()) { |
| 1355 | return Response::ServerError(kDebuggerNotEnabled); |
| 1356 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1357 | m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth); |
| 1358 | m_debugger->setAsyncCallStackDepth(this, depth); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1359 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1360 | } |
| 1361 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1362 | Response V8DebuggerAgentImpl::setBlackboxPatterns( |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1363 | std::unique_ptr<protocol::Array<String16>> patterns) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1364 | if (patterns->empty()) { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1365 | m_blackboxPattern = nullptr; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1366 | resetBlackboxedStateCache(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1367 | m_state->remove(DebuggerAgentState::blackboxPattern); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1368 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1369 | } |
| 1370 | |
| 1371 | String16Builder patternBuilder; |
| 1372 | patternBuilder.append('('); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1373 | for (size_t i = 0; i < patterns->size() - 1; ++i) { |
| 1374 | patternBuilder.append((*patterns)[i]); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1375 | patternBuilder.append("|"); |
| 1376 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1377 | patternBuilder.append(patterns->back()); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1378 | patternBuilder.append(')'); |
| 1379 | String16 pattern = patternBuilder.toString(); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1380 | Response response = setBlackboxPattern(pattern); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1381 | if (!response.IsSuccess()) return response; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1382 | resetBlackboxedStateCache(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1383 | m_state->setString(DebuggerAgentState::blackboxPattern, pattern); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1384 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1385 | } |
| 1386 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1387 | Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1388 | std::unique_ptr<V8Regex> regex(new V8Regex( |
| 1389 | m_inspector, pattern, true /** caseSensitive */, false /** multiline */)); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1390 | if (!regex->isValid()) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1391 | return Response::ServerError("Pattern parser error: " + |
| 1392 | regex->errorMessage().utf8()); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1393 | m_blackboxPattern = std::move(regex); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1394 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1395 | } |
| 1396 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1397 | void V8DebuggerAgentImpl::resetBlackboxedStateCache() { |
| 1398 | for (const auto& it : m_scripts) { |
| 1399 | it.second->resetBlackboxedStateCache(); |
| 1400 | } |
| 1401 | } |
| 1402 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1403 | Response V8DebuggerAgentImpl::setBlackboxedRanges( |
| 1404 | const String16& scriptId, |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1405 | std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>> |
| 1406 | inPositions) { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1407 | auto it = m_scripts.find(scriptId); |
| 1408 | if (it == m_scripts.end()) |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1409 | return Response::ServerError("No script with passed id."); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1410 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1411 | if (inPositions->empty()) { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1412 | m_blackboxedPositions.erase(scriptId); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1413 | it->second->resetBlackboxedStateCache(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1414 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1415 | } |
| 1416 | |
| 1417 | std::vector<std::pair<int, int>> positions; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1418 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1424 | positions.push_back( |
| 1425 | std::make_pair(position->getLineNumber(), position->getColumnNumber())); |
| 1426 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1427 | Response res = isValidRangeOfPositions(positions); |
| 1428 | if (res.IsError()) return res; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1429 | |
| 1430 | m_blackboxedPositions[scriptId] = positions; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1431 | it->second->resetBlackboxedStateCache(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1432 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1433 | } |
| 1434 | |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1435 | Response V8DebuggerAgentImpl::currentCallFrames( |
| 1436 | std::unique_ptr<Array<CallFrame>>* result) { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1437 | if (!isPaused()) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1438 | *result = std::make_unique<Array<CallFrame>>(); |
| 1439 | return Response::Success(); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1440 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1441 | v8::HandleScope handles(m_isolate); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1442 | *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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1447 | InjectedScript* injectedScript = nullptr; |
| 1448 | if (contextId) m_session->findInjectedScript(contextId, injectedScript); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1449 | String16 callFrameId = RemoteCallFrameId::serialize( |
| 1450 | m_inspector->isolateId(), contextId, frameOrdinal); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1451 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1452 | v8::debug::Location loc = iterator->GetSourceLocation(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1453 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1454 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1461 | if (injectedScript) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1462 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1468 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1469 | } |
| 1470 | if (!protocolReceiver) { |
| 1471 | protocolReceiver = RemoteObject::create() |
| 1472 | .setType(RemoteObject::TypeEnum::Undefined) |
| 1473 | .build(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1474 | } |
| 1475 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1476 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1490 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1491 | |
| 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1521 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1522 | return Response::Success(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1523 | } |
| 1524 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1525 | std::unique_ptr<protocol::Runtime::StackTrace> |
| 1526 | V8DebuggerAgentImpl::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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1532 | } |
| 1533 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1534 | std::unique_ptr<protocol::Runtime::StackTraceId> |
| 1535 | V8DebuggerAgentImpl::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 | |
| 1544 | bool V8DebuggerAgentImpl::isPaused() const { |
| 1545 | return m_debugger->isPausedInContextGroup(m_session->contextGroupId()); |
| 1546 | } |
| 1547 | |
| 1548 | static 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 | |
| 1557 | static 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 | |
| 1574 | static 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1589 | |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1590 | void V8DebuggerAgentImpl::didParseSource( |
| 1591 | std::unique_ptr<V8DebuggerScript> script, bool success) { |
| 1592 | v8::HandleScope handles(m_isolate); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1593 | if (!success) { |
| 1594 | DCHECK(!script->isSourceLoadedLazily()); |
| 1595 | String16 scriptSource = script->source(0); |
| 1596 | script->setSourceURL(findSourceURL(scriptSource, false)); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1597 | script->setSourceMappingURL(findSourceMapURL(scriptSource, false)); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1598 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1599 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1600 | int contextId = script->executionContextId(); |
| 1601 | int contextGroupId = m_inspector->contextGroupId(contextId); |
| 1602 | InspectedContext* inspected = |
| 1603 | m_inspector->getContext(contextGroupId, contextId); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1604 | std::unique_ptr<protocol::DictionaryValue> executionContextAuxData; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1605 | if (inspected) { |
| 1606 | // Script reused between different groups/sessions can have a stale |
| 1607 | // execution context id. |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1608 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1612 | executionContextAuxData = protocol::DictionaryValue::cast( |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1613 | protocol::Value::parseBinary(cbor.data(), cbor.size())); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1614 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1615 | bool isLiveEdit = script->isLiveEdit(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1616 | bool hasSourceURLComment = script->hasSourceURLComment(); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1617 | bool isModule = script->isModule(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1618 | String16 scriptId = script->scriptId(); |
| 1619 | String16 scriptURL = script->sourceURL(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1620 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1627 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1628 | m_scripts[scriptId] = std::move(script); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1629 | // 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1632 | |
| 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1643 | Maybe<protocol::DictionaryValue> executionContextAuxDataParam( |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1644 | std::move(executionContextAuxData)); |
| 1645 | const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1646 | const bool* hasSourceURLParam = |
| 1647 | hasSourceURLComment ? &hasSourceURLComment : nullptr; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1648 | const bool* isModuleParam = isModule ? &isModule : nullptr; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1649 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1657 | m_frontend.scriptFailedToParse( |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1658 | scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(), |
| 1659 | scriptRef->endLine(), scriptRef->endColumn(), contextId, |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1660 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1665 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1666 | |
| 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1733 | } |
| 1734 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1735 | void 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 | |
| 1768 | void 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1773 | v8::HandleScope handles(m_isolate); |
| 1774 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1775 | std::vector<BreakReason> hitReasons; |
| 1776 | |
| 1777 | if (isOOMBreak) { |
| 1778 | hitReasons.push_back( |
| 1779 | std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr)); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1780 | } else if (isAssert) { |
| 1781 | hitReasons.push_back(std::make_pair( |
| 1782 | protocol::Debugger::Paused::ReasonEnum::Assert, nullptr)); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1783 | } else if (!exception.IsEmpty()) { |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1784 | InjectedScript* injectedScript = nullptr; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1785 | m_session->findInjectedScript(contextId, injectedScript); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1786 | if (injectedScript) { |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1787 | String16 breakReason = |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1788 | exceptionType == v8::debug::kPromiseRejection |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1789 | ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection |
| 1790 | : protocol::Debugger::Paused::ReasonEnum::Exception; |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1791 | std::unique_ptr<protocol::Runtime::RemoteObject> obj; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1792 | injectedScript->wrapObject(exception, kBacktraceObjectGroup, |
| 1793 | WrapMode::kNoPreview, &obj); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1794 | std::unique_ptr<protocol::DictionaryValue> breakAuxData; |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1795 | if (obj) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1796 | std::vector<uint8_t> serialized; |
| 1797 | obj->AppendSerialized(&serialized); |
| 1798 | breakAuxData = protocol::DictionaryValue::cast( |
| 1799 | protocol::Value::parseBinary(serialized.data(), serialized.size())); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1800 | breakAuxData->setBoolean("uncaught", isUncaught); |
| Ben Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1801 | } |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1802 | hitReasons.push_back( |
| 1803 | std::make_pair(breakReason, std::move(breakAuxData))); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1804 | } |
| 1805 | } |
| 1806 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1807 | auto hitBreakpointIds = std::make_unique<Array<String16>>(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1808 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1809 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1817 | } |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1818 | 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1829 | } |
| 1830 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1831 | 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 Murdoch | c8c1d9e | 2017-03-08 14:04:23 +0000 | [diff] [blame] | 1857 | std::unique_ptr<Array<CallFrame>> protocolCallFrames; |
| 1858 | Response response = currentCallFrames(&protocolCallFrames); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1859 | if (!response.IsSuccess()) |
| 1860 | protocolCallFrames = std::make_unique<Array<CallFrame>>(); |
| 1861 | |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1862 | m_frontend.paused(std::move(protocolCallFrames), breakReason, |
| 1863 | std::move(breakAuxData), std::move(hitBreakpointIds), |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1864 | currentAsyncStackTrace(), currentExternalStackTrace()); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1865 | } |
| 1866 | |
| 1867 | void V8DebuggerAgentImpl::didContinue() { |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1868 | clearBreakDetails(); |
| 1869 | m_frontend.resumed(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1870 | m_frontend.flush(); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1871 | } |
| 1872 | |
| 1873 | void V8DebuggerAgentImpl::breakProgram( |
| 1874 | const String16& breakReason, |
| 1875 | std::unique_ptr<protocol::DictionaryValue> data) { |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1876 | if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return; |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1877 | std::vector<BreakReason> currentScheduledReason; |
| 1878 | currentScheduledReason.swap(m_breakReason); |
| 1879 | pushBreakDetails(breakReason, std::move(data)); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1880 | |
| 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 Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1889 | popBreakDetails(); |
| 1890 | m_breakReason.swap(currentScheduledReason); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1891 | if (!m_breakReason.empty()) { |
| 1892 | m_debugger->setPauseOnNextCall(true, m_session->contextGroupId()); |
| 1893 | } |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1894 | } |
| 1895 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1896 | void 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1905 | return; |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1906 | } |
| 1907 | setBreakpointImpl(breakpointId, function, condition); |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1908 | } |
| 1909 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1910 | void 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1918 | } |
| 1919 | |
| 1920 | void V8DebuggerAgentImpl::reset() { |
| 1921 | if (!enabled()) return; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1922 | m_blackboxedPositions.clear(); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1923 | resetBlackboxedStateCache(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1924 | m_skipList.clear(); |
| Ben Murdoch | 62ed631 | 2017-06-06 11:06:27 +0100 | [diff] [blame] | 1925 | m_scripts.clear(); |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1926 | m_cachedScriptIds.clear(); |
| 1927 | m_cachedScriptSize = 0; |
| Ben Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1928 | } |
| 1929 | |
| Rubin Xu | 7bc1b61 | 2021-02-16 09:38:50 +0000 | [diff] [blame^] | 1930 | void 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 | |
| 1946 | Response 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 Murdoch | f3b273f | 2017-01-17 12:11:28 +0000 | [diff] [blame] | 1980 | } // namespace v8_inspector |