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