Changeset 266074 in webkit for trunk/Source/JavaScriptCore/debugger
- Timestamp:
- Aug 24, 2020, 10:34:12 AM (5 years ago)
- Location:
- trunk/Source/JavaScriptCore/debugger
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/debugger/Breakpoint.h
r250655 r266074 27 27 28 28 #include "DebuggerPrimitives.h" 29 #include <wtf/DoublyLinkedList.h> 29 #include <wtf/FastMalloc.h> 30 #include <wtf/Noncopyable.h> 31 #include <wtf/Ref.h> 30 32 #include <wtf/RefCounted.h> 33 #include <wtf/Vector.h> 31 34 #include <wtf/text/WTFString.h> 32 35 33 36 namespace JSC { 34 37 35 struct Breakpoint : public DoublyLinkedListNode<Breakpoint> { 36 Breakpoint() 37 { 38 } 38 class Debugger; 39 class JSGlobalObject; 39 40 40 Breakpoint(SourceID sourceID, unsigned line, unsigned column, const String& condition, bool autoContinue, unsigned ignoreCount) 41 : sourceID(sourceID) 42 , line(line) 43 , column(column) 44 , condition(condition) 45 , autoContinue(autoContinue) 46 , ignoreCount(ignoreCount) 47 { 48 } 41 class Breakpoint : public RefCounted<Breakpoint> { 42 WTF_MAKE_NONCOPYABLE(Breakpoint); 43 WTF_MAKE_FAST_ALLOCATED; 44 public: 45 struct Action { 46 enum class Type : uint8_t { 47 Log, 48 Evaluate, 49 Sound, 50 Probe, 51 }; 49 52 50 BreakpointID id { noBreakpointID }; 51 SourceID sourceID { noSourceID }; 52 unsigned line { 0 }; 53 unsigned column { 0 }; 54 String condition; 55 bool autoContinue { false }; 56 unsigned ignoreCount { 0 }; 57 unsigned hitCount { 0 }; 58 bool resolved { false }; 53 Action(Type); 54 55 Type type; 56 String data; 57 BreakpointActionID id { noBreakpointActionID }; 58 }; 59 60 using ActionsVector = Vector<Action>; 61 62 static Ref<Breakpoint> create(BreakpointID, const String& condition = nullString(), ActionsVector&& = { }, bool autoContinue = false, size_t ignoreCount = 0); 63 64 BreakpointID id() const { return m_id; } 65 66 SourceID sourceID() const { return m_sourceID; } 67 unsigned lineNumber() const { return m_lineNumber; } 68 unsigned columnNumber() const { return m_columnNumber; } 69 70 const String& condition() const { return m_condition; } 71 const ActionsVector& actions() const { return m_actions; } 72 bool isAutoContinue() const { return m_autoContinue; } 73 74 void resetHitCount() { m_hitCount = 0; } 75 76 // Associates this breakpoint with a position in a specific source code. 77 bool link(SourceID, unsigned lineNumber, unsigned columnNumber); 78 bool isLinked() const { return m_sourceID != noSourceID; } 79 80 // Adjust the previously associated position to the next pause opportunity. 81 bool resolve(unsigned lineNumber, unsigned columnNumber); 82 bool isResolved() const { return m_resolved; } 83 84 bool shouldPause(Debugger&, JSGlobalObject*); 59 85 60 86 private: 61 Breakpoint* m_prev; 62 Breakpoint* m_next; 87 Breakpoint(BreakpointID, String condition = nullString(), ActionsVector&& = { }, bool autoContinue = false, size_t ignoreCount = 0); 63 88 64 friend class WTF::DoublyLinkedListNode<Breakpoint>; 89 BreakpointID m_id { noBreakpointID }; 90 91 SourceID m_sourceID { noSourceID }; 92 93 // FIXME: <https://webkit.org/b/162771> Web Inspector: Adopt TextPosition in Inspector to avoid oneBasedInt/zeroBasedInt ambiguity 94 unsigned m_lineNumber { 0 }; 95 unsigned m_columnNumber { 0 }; 96 97 bool m_resolved { false }; 98 99 String m_condition; 100 ActionsVector m_actions; 101 bool m_autoContinue { false }; 102 size_t m_ignoreCount { 0 }; 103 size_t m_hitCount { 0 }; 65 104 }; 66 105 67 class BreakpointsList : public DoublyLinkedList<Breakpoint>, 68 public RefCounted<BreakpointsList> { 69 public: 70 ~BreakpointsList() 71 { 72 Breakpoint* breakpoint; 73 while ((breakpoint = removeHead())) 74 delete breakpoint; 75 ASSERT(isEmpty()); 76 } 77 }; 106 using BreakpointsVector = Vector<Ref<JSC::Breakpoint>>; 78 107 79 108 } // namespace JSC -
trunk/Source/JavaScriptCore/debugger/Debugger.cpp
r261895 r266074 25 25 #include "CodeBlock.h" 26 26 #include "DebuggerCallFrame.h" 27 #include "DebuggerScope.h" 27 28 #include "HeapIterationScope.h" 28 29 #include "JSCInlines.h" 29 30 #include "MarkedSpaceInlines.h" 30 31 #include "VMEntryScope.h" 32 #include <wtf/HashMap.h> 33 #include <wtf/HashSet.h> 34 #include <wtf/RefPtr.h> 35 #include <wtf/Vector.h> 36 #include <wtf/text/TextPosition.h> 31 37 32 38 namespace JSC { … … 90 96 , m_lastExecutedLine(UINT_MAX) 91 97 , m_lastExecutedSourceID(noSourceID) 92 , m_topBreakpointID(noBreakpointID)93 98 , m_pausingBreakpointID(noBreakpointID) 94 99 { … … 206 211 } 207 212 213 void Debugger::addObserver(Observer& observer) 214 { 215 bool wasEmpty = m_observers.isEmpty(); 216 217 m_observers.add(&observer); 218 219 if (wasEmpty) 220 attachDebugger(); 221 } 222 223 void Debugger::removeObserver(Observer& observer, bool isBeingDestroyed) 224 { 225 m_observers.remove(&observer); 226 227 if (m_observers.isEmpty()) 228 detachDebugger(isBeingDestroyed); 229 } 230 231 bool Debugger::canDispatchFunctionToObservers() const 232 { 233 return !m_dispatchingFunctionToObservers && !m_observers.isEmpty(); 234 } 235 236 void Debugger::dispatchFunctionToObservers(Function<void(Observer&)> func) 237 { 238 if (!canDispatchFunctionToObservers()) 239 return; 240 241 SetForScope<bool> change(m_dispatchingFunctionToObservers, true); 242 243 for (auto* observer : copyToVector(m_observers)) 244 func(*observer); 245 } 246 208 247 void Debugger::setProfilingClient(ProfilingClient* client) 209 248 { … … 212 251 } 213 252 253 void Debugger::sourceParsed(JSGlobalObject* globalObject, SourceProvider* sourceProvider, int errorLine, const String& errorMessage) 254 { 255 // Preemptively check whether we can dispatch so that we don't do any unnecessary allocations. 256 if (!canDispatchFunctionToObservers()) 257 return; 258 259 if (errorLine != -1) { 260 auto sourceURL = sourceProvider->sourceURL(); 261 auto data = sourceProvider->source().toString(); 262 auto firstLine = sourceProvider->startPosition().m_line.oneBasedInt(); 263 dispatchFunctionToObservers([&] (Observer& observer) { 264 observer.failedToParseSource(sourceURL, data, firstLine, errorLine, errorMessage); 265 }); 266 return; 267 } 268 269 JSC::SourceID sourceID = sourceProvider->asID(); 270 271 // FIXME: <https://webkit.org/b/162773> Web Inspector: Simplify Debugger::Script to use SourceProvider 272 Debugger::Script script; 273 script.sourceProvider = sourceProvider; 274 script.url = sourceProvider->sourceURL(); 275 script.source = sourceProvider->source().toString(); 276 script.startLine = sourceProvider->startPosition().m_line.zeroBasedInt(); 277 script.startColumn = sourceProvider->startPosition().m_column.zeroBasedInt(); 278 script.isContentScript = isContentScript(globalObject); 279 script.sourceURL = sourceProvider->sourceURLDirective(); 280 script.sourceMappingURL = sourceProvider->sourceMappingURLDirective(); 281 282 int sourceLength = script.source.length(); 283 int lineCount = 1; 284 int lastLineStart = 0; 285 for (int i = 0; i < sourceLength; ++i) { 286 if (script.source[i] == '\n') { 287 lineCount += 1; 288 lastLineStart = i + 1; 289 } 290 } 291 292 script.endLine = script.startLine + lineCount - 1; 293 if (lineCount == 1) 294 script.endColumn = script.startColumn + sourceLength; 295 else 296 script.endColumn = sourceLength - lastLineStart; 297 298 dispatchFunctionToObservers([&] (Observer& observer) { 299 observer.didParseSource(sourceID, script); 300 }); 301 } 302 214 303 Seconds Debugger::willEvaluateScript() 215 304 { … … 224 313 void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot) 225 314 { 226 ASSERT(breakpoint. resolved);315 ASSERT(breakpoint.isResolved()); 227 316 228 317 ScriptExecutable* executable = codeBlock->ownerExecutable(); 229 318 230 319 SourceID sourceID = static_cast<SourceID>(executable->sourceID()); 231 if (breakpoint.sourceID != sourceID)320 if (breakpoint.sourceID() != sourceID) 232 321 return; 233 322 … … 239 328 // Inspector breakpoint line and column values are zero-based but the executable 240 329 // and CodeBlock line and column values are one-based. 241 unsigned line = breakpoint.line + 1;330 unsigned line = breakpoint.lineNumber() + 1; 242 331 Optional<unsigned> column; 243 if (breakpoint.column )244 column = breakpoint.column + 1;332 if (breakpoint.columnNumber()) 333 column = breakpoint.columnNumber() + 1; 245 334 246 335 if (line < startLine || line > endLine) … … 264 353 void Debugger::applyBreakpoints(CodeBlock* codeBlock) 265 354 { 266 BreakpointIDToBreakpointMap& breakpoints = m_breakpointIDToBreakpoint; 267 for (auto* breakpoint : breakpoints.values()) 268 toggleBreakpoint(codeBlock, *breakpoint, BreakpointEnabled); 355 for (auto& breakpoint : m_breakpoints) 356 toggleBreakpoint(codeBlock, breakpoint, BreakpointEnabled); 269 357 } 270 358 … … 315 403 } 316 404 317 voidDebugger::resolveBreakpoint(Breakpoint& breakpoint, SourceProvider* sourceProvider)318 { 319 RELEASE_ASSERT(!breakpoint. resolved);320 ASSERT(breakpoint. sourceID != noSourceID);405 bool Debugger::resolveBreakpoint(Breakpoint& breakpoint, SourceProvider* sourceProvider) 406 { 407 RELEASE_ASSERT(!breakpoint.isResolved()); 408 ASSERT(breakpoint.isLinked()); 321 409 322 410 // FIXME: <https://webkit.org/b/162771> Web Inspector: Adopt TextPosition in Inspector to avoid oneBasedInt/zeroBasedInt ambiguity 323 411 // Inspector breakpoint line and column values are zero-based but the executable 324 412 // and CodeBlock line values are one-based while column is zero-based. 325 int line = breakpoint.line + 1;326 int column = breakpoint.column ;413 int line = breakpoint.lineNumber() + 1; 414 int column = breakpoint.columnNumber(); 327 415 328 416 // Account for a <script>'s start position on the first line only. 329 417 int providerStartLine = sourceProvider->startPosition().m_line.oneBasedInt(); // One based to match the already adjusted line. 330 418 int providerStartColumn = sourceProvider->startPosition().m_column.zeroBasedInt(); // Zero based so column zero is zero. 331 if (line == providerStartLine && breakpoint.column ) {419 if (line == providerStartLine && breakpoint.columnNumber()) { 332 420 ASSERT(providerStartColumn <= column); 333 421 if (providerStartColumn) … … 335 423 } 336 424 337 DebuggerParseData& parseData = debuggerParseData(breakpoint.sourceID , sourceProvider);425 DebuggerParseData& parseData = debuggerParseData(breakpoint.sourceID(), sourceProvider); 338 426 Optional<JSTextPosition> resolvedPosition = parseData.pausePositions.breakpointLocationForLineColumn(line, column); 339 427 if (!resolvedPosition) 340 return ;428 return false; 341 429 342 430 int resolvedLine = resolvedPosition->line; … … 344 432 345 433 // Re-account for a <script>'s start position on the first line only. 346 if (resolvedLine == providerStartLine && breakpoint.column ) {434 if (resolvedLine == providerStartLine && breakpoint.columnNumber()) { 347 435 if (providerStartColumn) 348 436 resolvedColumn += providerStartColumn; 349 437 } 350 438 351 breakpoint.line = resolvedLine - 1; 352 breakpoint.column = resolvedColumn; 353 breakpoint.resolved = true; 354 } 355 356 BreakpointID Debugger::setBreakpoint(Breakpoint& breakpoint, bool& existing) 357 { 358 ASSERT(breakpoint.resolved); 359 ASSERT(breakpoint.sourceID != noSourceID); 360 361 SourceID sourceID = breakpoint.sourceID; 362 unsigned line = breakpoint.line; 363 unsigned column = breakpoint.column; 364 365 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(breakpoint.sourceID); 366 if (it == m_sourceIDToBreakpoints.end()) 367 it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator; 368 369 LineToBreakpointsMap::iterator breaksIt = it->value.find(line); 370 if (breaksIt == it->value.end()) 371 breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator; 372 373 BreakpointsList& breakpoints = *breaksIt->value; 374 for (Breakpoint* current = breakpoints.head(); current; current = current->next()) { 375 if (current->column == column) { 439 return breakpoint.resolve(resolvedLine - 1, resolvedColumn); 440 } 441 442 bool Debugger::setBreakpoint(Breakpoint& breakpoint) 443 { 444 ASSERT(breakpoint.isResolved()); 445 446 auto& breakpointsForLine = m_breakpointsForSourceID.ensure(breakpoint.sourceID(), [] { 447 return LineToBreakpointsMap(); 448 }).iterator->value; 449 450 auto& breakpoints = breakpointsForLine.ensure(breakpoint.lineNumber(), [] { 451 return BreakpointsVector(); 452 }).iterator->value; 453 454 for (auto& existingBreakpoint : breakpoints) { 455 if (existingBreakpoint->columnNumber() == breakpoint.columnNumber()) { 456 ASSERT(existingBreakpoint->id() != breakpoint.id()); 376 457 // Found existing breakpoint. Do not create a duplicate at this location. 377 existing = true; 378 return current->id; 458 return false; 379 459 } 380 460 } 381 461 382 existing = false; 383 BreakpointID id = ++m_topBreakpointID; 384 RELEASE_ASSERT(id != noBreakpointID); 385 386 breakpoint.id = id; 387 388 Breakpoint* newBreakpoint = new Breakpoint(breakpoint); 389 breakpoints.append(newBreakpoint); 390 m_breakpointIDToBreakpoint.set(id, newBreakpoint); 391 392 toggleBreakpoint(*newBreakpoint, BreakpointEnabled); 393 394 return id; 395 } 396 397 void Debugger::removeBreakpoint(BreakpointID id) 398 { 399 ASSERT(id != noBreakpointID); 400 401 BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id); 402 ASSERT(idIt != m_breakpointIDToBreakpoint.end()); 403 Breakpoint* breakpoint = idIt->value; 404 405 SourceID sourceID = breakpoint->sourceID; 406 ASSERT(sourceID); 407 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID); 408 ASSERT(it != m_sourceIDToBreakpoints.end()); 409 LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint->line); 410 ASSERT(breaksIt != it->value.end()); 411 412 toggleBreakpoint(*breakpoint, BreakpointDisabled); 413 414 BreakpointsList& breakpoints = *breaksIt->value; 462 breakpoints.append(makeRef(breakpoint)); 463 464 m_breakpoints.add(makeRef(breakpoint)); 465 466 toggleBreakpoint(breakpoint, BreakpointEnabled); 467 468 return true; 469 } 470 471 bool Debugger::removeBreakpoint(Breakpoint& breakpoint) 472 { 473 ASSERT(breakpoint.isResolved()); 474 475 auto breakpointsForLineIterator = m_breakpointsForSourceID.find(breakpoint.sourceID()); 476 if (breakpointsForLineIterator == m_breakpointsForSourceID.end()) 477 return false; 478 479 auto breakpointsIterator = breakpointsForLineIterator->value.find(breakpoint.lineNumber()); 480 if (breakpointsIterator == breakpointsForLineIterator->value.end()) 481 return false; 482 483 toggleBreakpoint(breakpoint, BreakpointDisabled); 484 485 auto& breakpoints = breakpointsIterator->value; 486 415 487 #if ASSERT_ENABLED 416 488 bool found = false; 417 for (Breakpoint* current = breakpoints.head(); current && !found; current = current->next()) { 418 if (current->id == breakpoint->id) 489 for (auto& existingBreakpoint : breakpoints) { 490 if (existingBreakpoint->columnNumber() == breakpoint.columnNumber()) { 491 ASSERT(existingBreakpoint->id() == breakpoint.id()); 492 ASSERT(!found); 419 493 found = true; 420 }421 ASSERT(found);494 } 495 } 422 496 #endif // ASSERT_ENABLED 423 497 424 m_breakpointIDToBreakpoint.remove(idIt); 425 breakpoints.remove(breakpoint); 426 delete breakpoint; 498 auto removed = m_breakpoints.remove(breakpoint); 499 removed |= !breakpoints.removeAllMatching([&] (const Ref<Breakpoint>& existingBreakpoint) { 500 return &breakpoint == existingBreakpoint.ptr(); 501 }); 427 502 428 503 if (breakpoints.isEmpty()) { 429 it->value.remove(breaksIt); 430 if (it->value.isEmpty()) 431 m_sourceIDToBreakpoints.remove(it); 432 } 433 } 434 435 bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint) 504 breakpointsForLineIterator->value.remove(breakpointsIterator); 505 if (breakpointsForLineIterator->value.isEmpty()) 506 m_breakpointsForSourceID.remove(breakpointsForLineIterator); 507 } 508 509 return removed; 510 } 511 512 RefPtr<Breakpoint> Debugger::didHitBreakpoint(JSGlobalObject* globalObject, SourceID sourceID, const TextPosition& position) 436 513 { 437 514 if (!m_breakpointsActivated) 438 return false;439 440 SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID);441 if ( it == m_sourceIDToBreakpoints.end())442 return false;515 return nullptr; 516 517 auto breakpointsForLineIterator = m_breakpointsForSourceID.find(sourceID); 518 if (breakpointsForLineIterator == m_breakpointsForSourceID.end()) 519 return nullptr; 443 520 444 521 unsigned line = position.m_line.zeroBasedInt(); 445 522 unsigned column = position.m_column.zeroBasedInt(); 446 523 447 LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line); 448 if (breaksIt == it->value.end()) 449 return false; 450 451 bool hit = false; 452 const BreakpointsList& breakpoints = *breaksIt->value; 453 Breakpoint* breakpoint; 454 for (breakpoint = breakpoints.head(); breakpoint; breakpoint = breakpoint->next()) { 455 unsigned breakLine = breakpoint->line; 456 unsigned breakColumn = breakpoint->column; 524 auto breakpointsIterator = breakpointsForLineIterator->value.find(line); 525 if (breakpointsIterator == breakpointsForLineIterator->value.end()) 526 return nullptr; 527 528 for (auto& breakpoint : breakpointsIterator->value) { 529 unsigned breakLine = breakpoint->lineNumber(); 530 unsigned breakColumn = breakpoint->columnNumber(); 531 457 532 // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0). 458 533 ASSERT(this == m_currentCallFrame->codeBlock()->globalObject()->debugger()); 459 if ((line != m_lastExecutedLine && line == breakLine && !breakColumn) 460 || (line == breakLine && column == breakColumn)) {461 hit = true;534 if ((line != m_lastExecutedLine && line == breakLine && !breakColumn) || (line == breakLine && column == breakColumn)) { 535 if (breakpoint->shouldPause(*this, globalObject)) 536 return breakpoint.copyRef(); 462 537 break; 463 538 } 464 539 } 465 if (!hit) 466 return false; 467 468 if (hitBreakpoint) 469 *hitBreakpoint = *breakpoint; 470 471 breakpoint->hitCount++; 472 if (breakpoint->ignoreCount >= breakpoint->hitCount) 473 return false; 474 475 if (breakpoint->condition.isEmpty()) 476 return true; 477 478 // We cannot stop in the debugger while executing condition code, 479 // so make it looks like the debugger is already paused. 480 TemporaryPausedState pausedState(*this); 481 482 NakedPtr<Exception> exception; 483 DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame(); 484 JSObject* scopeExtensionObject = nullptr; 485 JSValue result = debuggerCallFrame.evaluateWithScopeExtension(breakpoint->condition, scopeExtensionObject, exception); 486 487 // We can lose the debugger while executing JavaScript. 488 if (!m_currentCallFrame) 489 return false; 490 491 JSGlobalObject* globalObject = m_currentCallFrame->lexicalGlobalObject(m_vm); 492 if (exception) { 493 // An erroneous condition counts as "false". 494 handleExceptionInBreakpointCondition(globalObject, exception); 495 return false; 496 } 497 498 return result.toBoolean(globalObject); 540 541 return nullptr; 499 542 } 500 543 … … 520 563 m_vm.heap.completeAllJITPlans(); 521 564 522 m_ topBreakpointID = noBreakpointID;523 m_breakpoint IDToBreakpoint.clear();524 m_s ourceIDToBreakpoints.clear();565 m_breakpointsForSourceID.clear(); 566 m_breakpoints.clear(); 567 m_specialBreakpoint = nullptr; 525 568 526 569 ClearCodeBlockDebuggerRequestsFunctor functor(this); 527 570 m_vm.heap.forEachCodeBlock(functor); 571 } 572 573 bool Debugger::evaluateBreakpointCondition(Breakpoint& breakpoint, JSGlobalObject* globalObject) 574 { 575 const String& condition = breakpoint.condition(); 576 if (condition.isEmpty()) 577 return true; 578 579 // We cannot stop in the debugger while executing condition code, 580 // so make it looks like the debugger is already paused. 581 TemporaryPausedState pausedState(*this); 582 583 NakedPtr<Exception> exception; 584 DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame(); 585 JSObject* scopeExtensionObject = nullptr; 586 JSValue result = debuggerCallFrame.evaluateWithScopeExtension(condition, scopeExtensionObject, exception); 587 588 // We can lose the debugger while executing JavaScript. 589 if (!m_currentCallFrame) 590 return false; 591 592 if (exception) { 593 reportException(globalObject, exception); 594 return false; 595 } 596 597 return result.toBoolean(globalObject); 598 } 599 600 void Debugger::evaluateBreakpointActions(Breakpoint& breakpoint, JSGlobalObject* globalObject) 601 { 602 ASSERT(m_isPaused); 603 ASSERT(isAttached(globalObject)); 604 605 m_currentProbeBatchId++; 606 607 for (auto& action : breakpoint.actions()) { 608 auto& debuggerCallFrame = currentDebuggerCallFrame(); 609 610 switch (action.type) { 611 case Breakpoint::Action::Type::Log: 612 dispatchFunctionToObservers([&] (Observer& observer) { 613 observer.breakpointActionLog(debuggerCallFrame.globalObject(), action.data); 614 }); 615 break; 616 617 case Breakpoint::Action::Type::Evaluate: { 618 NakedPtr<Exception> exception; 619 JSObject* scopeExtensionObject = nullptr; 620 debuggerCallFrame.evaluateWithScopeExtension(action.data, scopeExtensionObject, exception); 621 if (exception) 622 reportException(debuggerCallFrame.globalObject(), exception); 623 break; 624 } 625 626 case Breakpoint::Action::Type::Sound: 627 dispatchFunctionToObservers([&] (Observer& observer) { 628 observer.breakpointActionSound(action.id); 629 }); 630 break; 631 632 case Breakpoint::Action::Type::Probe: { 633 NakedPtr<Exception> exception; 634 JSObject* scopeExtensionObject = nullptr; 635 JSValue result = debuggerCallFrame.evaluateWithScopeExtension(action.data, scopeExtensionObject, exception); 636 JSC::JSGlobalObject* debuggerGlobalObject = debuggerCallFrame.globalObject(); 637 if (exception) 638 reportException(debuggerGlobalObject, exception); 639 640 dispatchFunctionToObservers([&] (Observer& observer) { 641 observer.breakpointActionProbe(debuggerGlobalObject, action.id, m_currentProbeBatchId, m_nextProbeSampleId++, exception ? exception->value() : result); 642 }); 643 break; 644 } 645 } 646 647 if (!isAttached(globalObject)) 648 return; 649 } 528 650 } 529 651 … … 572 694 } 573 695 574 void Debugger::setPauseOnNextStatement(bool pause) 575 { 576 m_pauseAtNextOpportunity = pause; 577 if (pause) 578 setSteppingMode(SteppingModeEnabled); 696 void Debugger::schedulePauseAtNextOpportunity() 697 { 698 m_pauseAtNextOpportunity = true; 699 700 setSteppingMode(SteppingModeEnabled); 701 } 702 703 void Debugger::cancelPauseAtNextOpportunity() 704 { 705 m_pauseAtNextOpportunity = false; 706 } 707 708 bool Debugger::schedulePauseForSpecialBreakpoint(Breakpoint& breakpoint) 709 { 710 if (m_specialBreakpoint) 711 return false; 712 713 m_specialBreakpoint = makeRef(breakpoint); 714 setSteppingMode(SteppingModeEnabled); 715 return true; 716 } 717 718 bool Debugger::cancelPauseForSpecialBreakpoint(Breakpoint& breakpoint) 719 { 720 if (&breakpoint != m_specialBreakpoint) 721 return false; 722 723 m_specialBreakpoint = nullptr; 724 return true; 579 725 } 580 726 … … 600 746 return; 601 747 602 notifyDoneProcessingDebuggerEvents();748 m_doneProcessingDebuggerEvents = true; 603 749 } 604 750 … … 611 757 m_pauseOnStepNext = true; 612 758 setSteppingMode(SteppingModeEnabled); 613 notifyDoneProcessingDebuggerEvents();759 m_doneProcessingDebuggerEvents = true; 614 760 } 615 761 … … 621 767 m_pauseAtNextOpportunity = true; 622 768 setSteppingMode(SteppingModeEnabled); 623 notifyDoneProcessingDebuggerEvents();769 m_doneProcessingDebuggerEvents = true; 624 770 } 625 771 … … 631 777 m_pauseOnCallFrame = m_currentCallFrame; 632 778 setSteppingMode(SteppingModeEnabled); 633 notifyDoneProcessingDebuggerEvents();779 m_doneProcessingDebuggerEvents = true; 634 780 } 635 781 … … 643 789 m_pauseOnStepOut = true; 644 790 setSteppingMode(SteppingModeEnabled); 645 notifyDoneProcessingDebuggerEvents();791 m_doneProcessingDebuggerEvents = true; 646 792 } 647 793 … … 702 848 bool didPauseForStep = pauseNow; 703 849 704 Breakpoint breakpoint;705 850 TextPosition position = DebuggerCallFrame::positionForCallFrame(vm, m_currentCallFrame); 706 bool didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint); 707 pauseNow |= didHitBreakpoint; 851 852 auto breakpoint = didHitBreakpoint(globalObject, sourceID, position); 853 if (breakpoint) 854 pauseNow = true; 855 856 // Special breakpoints are only given one opportunity to pause. 857 auto specialBreakpoint = WTFMove(m_specialBreakpoint); 858 if (specialBreakpoint && specialBreakpoint->shouldPause(*this, globalObject)) 859 pauseNow = true; 860 708 861 m_lastExecutedLine = position.m_line.zeroBasedInt(); 709 862 if (!pauseNow) … … 717 870 TemporaryPausedState pausedState(*this); 718 871 719 if (didHitBreakpoint) { 720 handleBreakpointHit(globalObject, breakpoint); 872 if (breakpoint || specialBreakpoint) { 721 873 // Note that the actions can potentially stop the debugger, so we need to check that 722 874 // we still have a current call frame when we get back. 723 if (!m_currentCallFrame) 724 return; 725 726 if (breakpoint.autoContinue) { 875 876 bool autoContinue = false; 877 878 if (breakpoint) { 879 evaluateBreakpointActions(*breakpoint, globalObject); 880 881 if (!m_currentCallFrame) 882 return; 883 884 if (breakpoint->isAutoContinue()) 885 autoContinue = true; 886 } 887 888 if (specialBreakpoint) { 889 evaluateBreakpointActions(*specialBreakpoint, globalObject); 890 891 if (!m_currentCallFrame) 892 return; 893 894 if (specialBreakpoint->isAutoContinue()) 895 autoContinue = true; 896 } 897 898 if (autoContinue) { 727 899 if (!didPauseForStep) 728 900 return; 729 didHitBreakpoint = false; 730 } else 731 m_pausingBreakpointID = breakpoint.id; 901 902 breakpoint = nullptr; 903 specialBreakpoint = nullptr; 904 } else if (breakpoint) 905 m_pausingBreakpointID = breakpoint->id(); 732 906 } 733 907 734 908 if (blackboxTypeIterator != m_blackboxedScripts.end() && blackboxTypeIterator->value == BlackboxType::Deferred) { 735 909 m_afterBlackboxedScript = true; 736 s etPauseOnNextStatement(true);910 schedulePauseAtNextOpportunity(); 737 911 return; 738 912 } … … 742 916 if (afterBlackboxedScript) 743 917 reason = PausedAfterBlackboxedScript; 744 else if ( didHitBreakpoint)918 else if (breakpoint) 745 919 reason = PausedForBreakpoint; 746 920 PauseReasonDeclaration rauseReasonDeclaration(*this, reason); … … 752 926 m_pausingBreakpointID = noBreakpointID; 753 927 754 if (!m_pauseAtNextOpportunity && !m_pauseOnCallFrame ) {928 if (!m_pauseAtNextOpportunity && !m_pauseOnCallFrame && !m_specialBreakpoint) { 755 929 setSteppingMode(SteppingModeDisabled); 756 930 m_currentCallFrame = nullptr; 757 931 } 932 } 933 934 void Debugger::handlePause(JSGlobalObject* globalObject, ReasonForPause) 935 { 936 dispatchFunctionToObservers([&] (Observer& observer) { 937 ASSERT(isPaused()); 938 observer.didPause(globalObject, currentDebuggerCallFrame(), exceptionOrCaughtValue(globalObject)); 939 }); 940 941 didPause(globalObject); 942 943 m_doneProcessingDebuggerEvents = false; 944 runEventLoopWhilePaused(); 945 946 didContinue(globalObject); 947 948 dispatchFunctionToObservers([&] (Observer& observer) { 949 observer.didContinue(); 950 }); 951 } 952 953 JSC::JSValue Debugger::exceptionOrCaughtValue(JSC::JSGlobalObject* globalObject) 954 { 955 if (reasonForPause() == PausedForException) 956 return currentException(); 957 958 for (RefPtr<DebuggerCallFrame> frame = ¤tDebuggerCallFrame(); frame; frame = frame->callerFrame()) { 959 DebuggerScope& scope = *frame->scope(); 960 if (scope.isCatchScope()) 961 return scope.caughtValue(globalObject); 962 } 963 964 return { }; 758 965 } 759 966 … … 808 1015 } 809 1016 810 // Only pause at the next expression with step-in, step-next, and step-out.811 bool shouldAttemptPause = m_pauseAtNextOpportunity || m_pauseOnStepNext || m_pauseOnStepOut ;1017 // Only pause at the next expression for step-in, step-next, step-out, or special breakpoints. 1018 bool shouldAttemptPause = m_pauseAtNextOpportunity || m_pauseOnStepNext || m_pauseOnStepOut || m_specialBreakpoint; 812 1019 813 1020 PauseReasonDeclaration reason(*this, PausedAtExpression); … … 918 1125 m_pauseOnStepOut = false; 919 1126 m_afterBlackboxedScript = false; 1127 m_specialBreakpoint = nullptr; 920 1128 } 921 1129 … … 934 1142 } 935 1143 1144 void Debugger::willRunMicrotask() 1145 { 1146 dispatchFunctionToObservers([&] (Observer& observer) { 1147 observer.willRunMicrotask(); 1148 }); 1149 } 1150 1151 void Debugger::didRunMicrotask() 1152 { 1153 dispatchFunctionToObservers([&] (Observer& observer) { 1154 observer.didRunMicrotask(); 1155 }); 1156 } 1157 936 1158 DebuggerCallFrame& Debugger::currentDebuggerCallFrame() 937 1159 { -
trunk/Source/JavaScriptCore/debugger/Debugger.h
r260113 r266074 28 28 #include "DebuggerPrimitives.h" 29 29 #include "JSCJSValue.h" 30 #include <wtf/HashMap.h> 31 #include <wtf/HashSet.h> 32 #include <wtf/RefPtr.h> 33 #include <wtf/text/TextPosition.h> 30 #include <wtf/Forward.h> 34 31 35 32 namespace JSC { … … 42 39 class VM; 43 40 44 class JS_EXPORT_PRIVATEDebugger {41 class Debugger { 45 42 WTF_MAKE_FAST_ALLOCATED; 46 43 public: 47 Debugger(VM&);48 virtual ~Debugger();44 JS_EXPORT_PRIVATE Debugger(VM&); 45 JS_EXPORT_PRIVATE virtual ~Debugger(); 49 46 50 47 VM& vm() { return m_vm; } 51 52 JSC::DebuggerCallFrame& currentDebuggerCallFrame();53 bool hasHandlerForExceptionCallback() const54 {55 ASSERT(m_reasonForPause == PausedForException);56 return m_hasHandlerForExceptionCallback;57 }58 JSValue currentException()59 {60 ASSERT(m_reasonForPause == PausedForException);61 return m_currentException;62 }63 48 64 49 bool needsExceptionCallbacks() const { return m_breakpointsActivated && m_pauseOnExceptionsState != DontPauseOnExceptions; } … … 69 54 GlobalObjectIsDestructing 70 55 }; 71 void attach(JSGlobalObject*);72 void detach(JSGlobalObject*, ReasonForDetach);73 bool isAttached(JSGlobalObject*);74 75 voidresolveBreakpoint(Breakpoint&, SourceProvider*);76 BreakpointID setBreakpoint(Breakpoint&, bool& existing);77 void removeBreakpoint(BreakpointID);56 JS_EXPORT_PRIVATE void attach(JSGlobalObject*); 57 JS_EXPORT_PRIVATE void detach(JSGlobalObject*, ReasonForDetach); 58 JS_EXPORT_PRIVATE bool isAttached(JSGlobalObject*); 59 60 bool resolveBreakpoint(Breakpoint&, SourceProvider*); 61 bool setBreakpoint(Breakpoint&); 62 bool removeBreakpoint(Breakpoint&); 78 63 void clearBreakpoints(); 79 64 … … 81 66 void deactivateBreakpoints() { setBreakpointsActivated(false); } 82 67 bool breakpointsActive() const { return m_breakpointsActivated; } 68 69 // Breakpoint "delegate" functionality. 70 bool evaluateBreakpointCondition(Breakpoint&, JSGlobalObject*); 71 void evaluateBreakpointActions(Breakpoint&, JSGlobalObject*); 83 72 84 73 enum PauseOnExceptionsState { … … 88 77 }; 89 78 PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; } 90 void setPauseOnExceptionsState(PauseOnExceptionsState);79 JS_EXPORT_PRIVATE void setPauseOnExceptionsState(PauseOnExceptionsState); 91 80 92 81 void setPauseOnDebuggerStatements(bool enabled) { m_pauseOnDebuggerStatements = enabled; } … … 106 95 BreakpointID pausingBreakpointID() const { return m_pausingBreakpointID; } 107 96 108 void setPauseOnNextStatement(bool); 97 void schedulePauseAtNextOpportunity(); 98 void cancelPauseAtNextOpportunity(); 99 bool schedulePauseForSpecialBreakpoint(Breakpoint&); 100 bool cancelPauseForSpecialBreakpoint(Breakpoint&); 109 101 void breakProgram(); 110 102 void continueProgram(); … … 124 116 void setSuppressAllPauses(bool suppress) { m_suppressAllPauses = suppress; } 125 117 126 virtual void sourceParsed(JSGlobalObject*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0; 127 virtual void willRunMicrotask() { } 128 virtual void didRunMicrotask() { } 118 JS_EXPORT_PRIVATE virtual void sourceParsed(JSGlobalObject*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage); 129 119 130 120 void exception(JSGlobalObject*, CallFrame*, JSValue exceptionValue, bool hasCatchHandler); … … 138 128 void didReachDebuggerStatement(CallFrame*); 139 129 140 virtual void recompileAllJSFunctions(); 130 void willRunMicrotask(); 131 void didRunMicrotask(); 141 132 142 133 void registerCodeBlock(CodeBlock*); 134 135 // FIXME: <https://webkit.org/b/162773> Web Inspector: Simplify Debugger::Script to use SourceProvider 136 struct Script { 137 String url; 138 String source; 139 String sourceURL; 140 String sourceMappingURL; 141 RefPtr<SourceProvider> sourceProvider; 142 int startLine { 0 }; 143 int startColumn { 0 }; 144 int endLine { 0 }; 145 int endColumn { 0 }; 146 bool isContentScript { false }; 147 }; 148 149 class Observer { 150 public: 151 virtual ~Observer() { } 152 153 virtual void didParseSource(SourceID, const Debugger::Script&) { } 154 virtual void failedToParseSource(const String& /* url */, const String& /* data */, int /* firstLine */, int /* errorLine */, const String& /* errorMessage */) { } 155 156 virtual void willRunMicrotask() { } 157 virtual void didRunMicrotask() { } 158 159 virtual void didPause(JSGlobalObject*, DebuggerCallFrame&, JSValue /* exceptionOrCaughtValue */) { } 160 virtual void didContinue() { } 161 162 virtual void breakpointActionLog(JSGlobalObject*, const String& /* data */) { } 163 virtual void breakpointActionSound(BreakpointActionID) { } 164 virtual void breakpointActionProbe(JSGlobalObject*, BreakpointActionID, unsigned /* batchId */, unsigned /* sampleId */, JSValue /* result */) { } 165 }; 166 167 JS_EXPORT_PRIVATE void addObserver(Observer&); 168 JS_EXPORT_PRIVATE void removeObserver(Observer&, bool isBeingDestroyed); 143 169 144 170 class ProfilingClient { … … 157 183 158 184 protected: 159 virtual void handleBreakpointHit(JSGlobalObject*, const Breakpoint&) { } 160 virtual void handleExceptionInBreakpointCondition(JSGlobalObject*, Exception*) const { } 161 virtual void handlePause(JSGlobalObject*, ReasonForPause) { } 162 virtual void notifyDoneProcessingDebuggerEvents() { } 185 JS_EXPORT_PRIVATE JSC::DebuggerCallFrame& currentDebuggerCallFrame(); 186 187 bool hasHandlerForExceptionCallback() const 188 { 189 ASSERT(m_reasonForPause == PausedForException); 190 return m_hasHandlerForExceptionCallback; 191 } 192 193 JSValue currentException() 194 { 195 ASSERT(m_reasonForPause == PausedForException); 196 return m_currentException; 197 } 198 199 virtual void attachDebugger() { } 200 virtual void detachDebugger(bool /* isBeingDestroyed */) { } 201 JS_EXPORT_PRIVATE virtual void recompileAllJSFunctions(); 202 203 virtual void didPause(JSGlobalObject*) { } 204 JS_EXPORT_PRIVATE virtual void handlePause(JSGlobalObject*, ReasonForPause); 205 virtual void didContinue(JSGlobalObject*) { } 206 virtual void runEventLoopWhilePaused() { } 207 208 virtual bool isContentScript(JSGlobalObject*) const { return false; } 209 210 // NOTE: Currently all exceptions are reported at the API boundary through reportAPIException. 211 // Until a time comes where an exception can be caused outside of the API (e.g. setTimeout 212 // or some other async operation in a pure JSContext) we can ignore exceptions reported here. 213 virtual void reportException(JSGlobalObject*, Exception*) const { } 214 215 bool doneProcessingDebuggerEvents() const { return m_doneProcessingDebuggerEvents; } 163 216 164 217 private: 165 typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap; 166 167 typedef HashMap<unsigned, RefPtr<BreakpointsList>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap; 168 typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap; 218 JSValue exceptionOrCaughtValue(JSGlobalObject*); 169 219 170 220 class ClearCodeBlockDebuggerRequestsFunctor; … … 189 239 }; 190 240 191 bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);241 RefPtr<Breakpoint> didHitBreakpoint(JSGlobalObject*, SourceID, const TextPosition&); 192 242 193 243 DebuggerParseData& debuggerParseData(SourceID, SourceProvider*); … … 216 266 BreakpointEnabled 217 267 }; 218 void setBreakpointsActivated(bool);268 JS_EXPORT_PRIVATE void setBreakpointsActivated(bool); 219 269 void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState); 220 270 void applyBreakpoints(CodeBlock*); … … 223 273 void clearDebuggerRequests(JSGlobalObject*); 224 274 void clearParsedData(); 275 276 bool canDispatchFunctionToObservers() const; 277 void dispatchFunctionToObservers(Function<void(Observer&)>); 225 278 226 279 VM& m_vm; … … 249 302 bool m_afterBlackboxedScript { false }; 250 303 251 BreakpointID m_topBreakpointID; 304 using LineToBreakpointsMap = HashMap<unsigned, BreakpointsVector, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>>; 305 HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> m_breakpointsForSourceID; 306 HashSet<Ref<Breakpoint>> m_breakpoints; 307 RefPtr<Breakpoint> m_specialBreakpoint; 252 308 BreakpointID m_pausingBreakpointID; 253 BreakpointIDToBreakpointMap m_breakpointIDToBreakpoint; 254 SourceIDToBreakpointsMap m_sourceIDToBreakpoints; 309 310 unsigned m_nextProbeSampleId { 1 }; 311 unsigned m_currentProbeBatchId { 0 }; 255 312 256 313 RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame; 257 314 315 HashSet<Observer*> m_observers; 316 bool m_dispatchingFunctionToObservers { false }; 317 258 318 ProfilingClient* m_profilingClient { nullptr }; 319 320 bool m_doneProcessingDebuggerEvents { true }; 259 321 260 322 friend class DebuggerPausedScope; -
trunk/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
r250005 r266074 30 30 namespace JSC { 31 31 32 typedef size_t SourceID;33 staticconstexpr SourceID noSourceID = 0;32 using SourceID = size_t; 33 constexpr SourceID noSourceID = 0; 34 34 35 typedef size_t BreakpointID; 36 static constexpr BreakpointID noBreakpointID = 0; 35 using BreakpointID = size_t; 36 constexpr BreakpointID noBreakpointID = 0; 37 38 using BreakpointActionID = int; 39 constexpr BreakpointActionID noBreakpointActionID = 0; 37 40 38 41 } // namespace JSC
Note:
See TracChangeset
for help on using the changeset viewer.