1/* 2 * Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved. 3 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) 4 * Copyright (C) 2001 Peter Kelly (pmk@post.com) 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * 20 */ 21 22#include "config.h" 23#include "Debugger.h" 24 25#include "CodeBlock.h" 26#include "DebuggerCallFrame.h" 27#include "Error.h" 28 29#include "HeapIterationScope.h" 30#include "Interpreter.h" 31#include "JSCJSValueInlines.h" 32#include "JSFunction.h" 33#include "JSGlobalObject.h" 34#include "JSCInlines.h" 35#include "Parser.h" 36#include "Protect.h" 37#include "VMEntryScope.h" 38 39namespace { 40 41using namespace JSC; 42 43class Recompiler : public MarkedBlock::VoidFunctor { 44public: 45 Recompiler(JSC::Debugger*); 46 ~Recompiler(); 47 void operator()(JSCell*); 48 49private: 50 typedef HashSet<FunctionExecutable*> FunctionExecutableSet; 51 typedef HashMap<SourceProvider*, ExecState*> SourceProviderMap; 52 53 JSC::Debugger* m_debugger; 54 FunctionExecutableSet m_functionExecutables; 55 SourceProviderMap m_sourceProviders; 56}; 57 58inline Recompiler::Recompiler(JSC::Debugger* debugger) 59 : m_debugger(debugger) 60{ 61} 62 63inline Recompiler::~Recompiler() 64{ 65 // Call sourceParsed() after reparsing all functions because it will execute 66 // JavaScript in the inspector. 67 SourceProviderMap::const_iterator end = m_sourceProviders.end(); 68 for (SourceProviderMap::const_iterator iter = m_sourceProviders.begin(); iter != end; ++iter) 69 m_debugger->sourceParsed(iter->value, iter->key, -1, String()); 70} 71 72inline void Recompiler::operator()(JSCell* cell) 73{ 74 if (!cell->inherits(JSFunction::info())) 75 return; 76 77 JSFunction* function = jsCast<JSFunction*>(cell); 78 if (function->executable()->isHostFunction()) 79 return; 80 81 FunctionExecutable* executable = function->jsExecutable(); 82 83 // Check if the function is already in the set - if so, 84 // we've already retranslated it, nothing to do here. 85 if (!m_functionExecutables.add(executable).isNewEntry) 86 return; 87 88 ExecState* exec = function->scope()->globalObject()->JSGlobalObject::globalExec(); 89 executable->clearCodeIfNotCompiling(); 90 executable->clearUnlinkedCodeForRecompilationIfNotCompiling(); 91 if (m_debugger == function->scope()->globalObject()->debugger()) 92 m_sourceProviders.add(executable->source().provider(), exec); 93} 94 95} // namespace 96 97namespace JSC { 98 99class DebuggerCallFrameScope { 100public: 101 DebuggerCallFrameScope(Debugger& debugger) 102 : m_debugger(debugger) 103 { 104 ASSERT(!m_debugger.m_currentDebuggerCallFrame); 105 if (m_debugger.m_currentCallFrame) 106 m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame); 107 } 108 109 ~DebuggerCallFrameScope() 110 { 111 if (m_debugger.m_currentDebuggerCallFrame) { 112 m_debugger.m_currentDebuggerCallFrame->invalidate(); 113 m_debugger.m_currentDebuggerCallFrame = 0; 114 } 115 } 116 117private: 118 Debugger& m_debugger; 119}; 120 121// This is very similar to TemporaryChange<bool>, but that cannot be used 122// as the m_isPaused field uses only one bit. 123class TemporaryPausedState { 124public: 125 TemporaryPausedState(Debugger& debugger) 126 : m_debugger(debugger) 127 { 128 ASSERT(!m_debugger.m_isPaused); 129 m_debugger.m_isPaused = true; 130 } 131 132 ~TemporaryPausedState() 133 { 134 m_debugger.m_isPaused = false; 135 } 136 137private: 138 Debugger& m_debugger; 139}; 140 141template<typename Functor> 142void Debugger::forEachCodeBlock(Functor& functor) 143{ 144 m_vm->waitForCompilationsToComplete(); 145 m_vm->heap.forEachCodeBlock(functor); 146} 147 148Debugger::Debugger(bool isInWorkerThread) 149 : m_vm(nullptr) 150 , m_pauseOnExceptionsState(DontPauseOnExceptions) 151 , m_pauseOnNextStatement(false) 152 , m_isPaused(false) 153 , m_breakpointsActivated(true) 154 , m_hasHandlerForExceptionCallback(false) 155 , m_isInWorkerThread(isInWorkerThread) 156 , m_steppingMode(SteppingModeDisabled) 157 , m_reasonForPause(NotPaused) 158 , m_pauseOnCallFrame(0) 159 , m_currentCallFrame(0) 160 , m_lastExecutedLine(UINT_MAX) 161 , m_lastExecutedSourceID(noSourceID) 162 , m_topBreakpointID(noBreakpointID) 163{ 164} 165 166Debugger::~Debugger() 167{ 168 HashSet<JSGlobalObject*>::iterator end = m_globalObjects.end(); 169 for (HashSet<JSGlobalObject*>::iterator it = m_globalObjects.begin(); it != end; ++it) 170 (*it)->setDebugger(0); 171} 172 173void Debugger::attach(JSGlobalObject* globalObject) 174{ 175 ASSERT(!globalObject->debugger()); 176 if (!m_vm) 177 m_vm = &globalObject->vm(); 178 else 179 ASSERT(m_vm == &globalObject->vm()); 180 globalObject->setDebugger(this); 181 m_globalObjects.add(globalObject); 182} 183 184void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason) 185{ 186 // If we're detaching from the currently executing global object, manually tear down our 187 // stack, since we won't get further debugger callbacks to do so. Also, resume execution, 188 // since there's no point in staying paused once a window closes. 189 if (m_currentCallFrame && m_currentCallFrame->vmEntryGlobalObject() == globalObject) { 190 m_currentCallFrame = 0; 191 m_pauseOnCallFrame = 0; 192 continueProgram(); 193 } 194 195 ASSERT(m_globalObjects.contains(globalObject)); 196 m_globalObjects.remove(globalObject); 197 198 // If the globalObject is destructing, then its CodeBlocks will also be 199 // destructed. There is no need to do the debugger requests clean up, and 200 // it is not safe to access those CodeBlocks at this time anyway. 201 if (reason != GlobalObjectIsDestructing) 202 clearDebuggerRequests(globalObject); 203 204 globalObject->setDebugger(0); 205 if (!m_globalObjects.size()) 206 m_vm = nullptr; 207} 208 209class Debugger::SetSteppingModeFunctor { 210public: 211 SetSteppingModeFunctor(Debugger* debugger, SteppingMode mode) 212 : m_debugger(debugger) 213 , m_mode(mode) 214 { 215 } 216 217 bool operator()(CodeBlock* codeBlock) 218 { 219 if (m_debugger == codeBlock->globalObject()->debugger()) { 220 if (m_mode == SteppingModeEnabled) 221 codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled); 222 else 223 codeBlock->setSteppingMode(CodeBlock::SteppingModeDisabled); 224 } 225 return false; 226 } 227 228private: 229 Debugger* m_debugger; 230 SteppingMode m_mode; 231}; 232 233void Debugger::setSteppingMode(SteppingMode mode) 234{ 235 if (mode == m_steppingMode || !m_vm) 236 return; 237 238 m_vm->waitForCompilationsToComplete(); 239 240 m_steppingMode = mode; 241 SetSteppingModeFunctor functor(this, mode); 242 m_vm->heap.forEachCodeBlock(functor); 243} 244 245void Debugger::registerCodeBlock(CodeBlock* codeBlock) 246{ 247 // FIXME: We should never have to jettison a code block (due to pending breakpoints 248 // or stepping mode) that is being registered. operationOptimize() should have 249 // prevented the optimizing of such code blocks in the first place. Find a way to 250 // express this with greater clarity in the code. See <https://webkit.org/b131771>. 251 applyBreakpoints(codeBlock); 252 if (isStepping()) 253 codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled); 254} 255 256void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot) 257{ 258 ScriptExecutable* executable = codeBlock->ownerExecutable(); 259 260 SourceID sourceID = static_cast<SourceID>(executable->sourceID()); 261 if (breakpoint.sourceID != sourceID) 262 return; 263 264 unsigned line = breakpoint.line; 265 unsigned column = breakpoint.column; 266 267 unsigned startLine = executable->lineNo(); 268 unsigned startColumn = executable->startColumn(); 269 unsigned endLine = executable->lastLine(); 270 unsigned endColumn = executable->endColumn(); 271 272 // Inspector breakpoint line and column values are zero-based but the executable 273 // and CodeBlock line and column values are one-based. 274 line += 1; 275 column = column ? column + 1 : Breakpoint::unspecifiedColumn; 276 277 if (line < startLine || line > endLine) 278 return; 279 if (column != Breakpoint::unspecifiedColumn) { 280 if (line == startLine && column < startColumn) 281 return; 282 if (line == endLine && column > endColumn) 283 return; 284 } 285 if (!codeBlock->hasOpDebugForLineAndColumn(line, column)) 286 return; 287 288 if (enabledOrNot == BreakpointEnabled) 289 codeBlock->addBreakpoint(1); 290 else 291 codeBlock->removeBreakpoint(1); 292} 293 294void Debugger::applyBreakpoints(CodeBlock* codeBlock) 295{ 296 BreakpointIDToBreakpointMap& breakpoints = m_breakpointIDToBreakpoint; 297 for (auto it = breakpoints.begin(); it != breakpoints.end(); ++it) { 298 Breakpoint& breakpoint = *it->value; 299 toggleBreakpoint(codeBlock, breakpoint, BreakpointEnabled); 300 } 301} 302 303class Debugger::ToggleBreakpointFunctor { 304public: 305 ToggleBreakpointFunctor(Debugger* debugger, Breakpoint& breakpoint, BreakpointState enabledOrNot) 306 : m_debugger(debugger) 307 , m_breakpoint(breakpoint) 308 , m_enabledOrNot(enabledOrNot) 309 { 310 } 311 312 bool operator()(CodeBlock* codeBlock) 313 { 314 if (m_debugger == codeBlock->globalObject()->debugger()) 315 m_debugger->toggleBreakpoint(codeBlock, m_breakpoint, m_enabledOrNot); 316 return false; 317 } 318 319private: 320 Debugger* m_debugger; 321 Breakpoint& m_breakpoint; 322 BreakpointState m_enabledOrNot; 323}; 324 325void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot) 326{ 327 if (!m_vm) 328 return; 329 ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot); 330 forEachCodeBlock(functor); 331} 332 333void Debugger::recompileAllJSFunctions(VM* vm) 334{ 335 // If JavaScript is running, it's not safe to recompile, since we'll end 336 // up throwing away code that is live on the stack. 337 if (vm->entryScope) { 338 vm->entryScope->setRecompilationNeeded(true); 339 return; 340 } 341 342 vm->waitForCompilationsToComplete(); 343 344 Recompiler recompiler(this); 345 HeapIterationScope iterationScope(vm->heap); 346 vm->heap.objectSpace().forEachLiveCell(iterationScope, recompiler); 347} 348 349BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn) 350{ 351 SourceID sourceID = breakpoint.sourceID; 352 unsigned line = breakpoint.line; 353 unsigned column = breakpoint.column; 354 355 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID); 356 if (it == m_sourceIDToBreakpoints.end()) 357 it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator; 358 LineToBreakpointsMap::iterator breaksIt = it->value.find(line); 359 if (breaksIt == it->value.end()) 360 breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator; 361 362 BreakpointsList& breakpoints = *breaksIt->value; 363 for (Breakpoint* current = breakpoints.head(); current; current = current->next()) { 364 if (current->column == column) { 365 // The breakpoint already exists. We're not allowed to create a new 366 // breakpoint at this location. Rather than returning the breakpointID 367 // of the pre-existing breakpoint, we need to return noBreakpointID 368 // to indicate that we're not creating a new one. 369 return noBreakpointID; 370 } 371 } 372 373 BreakpointID id = ++m_topBreakpointID; 374 RELEASE_ASSERT(id != noBreakpointID); 375 376 breakpoint.id = id; 377 actualLine = line; 378 actualColumn = column; 379 380 Breakpoint* newBreakpoint = new Breakpoint(breakpoint); 381 breakpoints.append(newBreakpoint); 382 m_breakpointIDToBreakpoint.set(id, newBreakpoint); 383 384 toggleBreakpoint(breakpoint, BreakpointEnabled); 385 386 return id; 387} 388 389void Debugger::removeBreakpoint(BreakpointID id) 390{ 391 ASSERT(id != noBreakpointID); 392 393 BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id); 394 ASSERT(idIt != m_breakpointIDToBreakpoint.end()); 395 Breakpoint* breakpoint = idIt->value; 396 397 SourceID sourceID = breakpoint->sourceID; 398 ASSERT(sourceID); 399 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID); 400 ASSERT(it != m_sourceIDToBreakpoints.end()); 401 LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint->line); 402 ASSERT(breaksIt != it->value.end()); 403 404 toggleBreakpoint(*breakpoint, BreakpointDisabled); 405 406 BreakpointsList& breakpoints = *breaksIt->value; 407#if !ASSERT_DISABLED 408 bool found = false; 409 for (Breakpoint* current = breakpoints.head(); current && !found; current = current->next()) { 410 if (current->id == breakpoint->id) 411 found = true; 412 } 413 ASSERT(found); 414#endif 415 416 m_breakpointIDToBreakpoint.remove(idIt); 417 breakpoints.remove(breakpoint); 418 delete breakpoint; 419 420 if (breakpoints.isEmpty()) { 421 it->value.remove(breaksIt); 422 if (it->value.isEmpty()) 423 m_sourceIDToBreakpoints.remove(it); 424 } 425} 426 427bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint) 428{ 429 if (!m_breakpointsActivated) 430 return false; 431 432 SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID); 433 if (it == m_sourceIDToBreakpoints.end()) 434 return false; 435 436 unsigned line = position.m_line.zeroBasedInt(); 437 unsigned column = position.m_column.zeroBasedInt(); 438 439 LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line); 440 if (breaksIt == it->value.end()) 441 return false; 442 443 bool hit = false; 444 const BreakpointsList& breakpoints = *breaksIt->value; 445 Breakpoint* breakpoint; 446 for (breakpoint = breakpoints.head(); breakpoint; breakpoint = breakpoint->next()) { 447 unsigned breakLine = breakpoint->line; 448 unsigned breakColumn = breakpoint->column; 449 // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0). 450 ASSERT(this == m_currentCallFrame->codeBlock()->globalObject()->debugger()); 451 if ((line != m_lastExecutedLine && line == breakLine && !breakColumn) 452 || (line == breakLine && column == breakColumn)) { 453 hit = true; 454 break; 455 } 456 } 457 if (!hit) 458 return false; 459 460 if (hitBreakpoint) 461 *hitBreakpoint = *breakpoint; 462 463 if (breakpoint->condition.isEmpty()) 464 return true; 465 466 // We cannot stop in the debugger while executing condition code, 467 // so make it looks like the debugger is already paused. 468 TemporaryPausedState pausedState(*this); 469 470 JSValue exception; 471 DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame(); 472 JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception); 473 474 // We can lose the debugger while executing JavaScript. 475 if (!m_currentCallFrame) 476 return false; 477 478 if (exception) { 479 // An erroneous condition counts as "false". 480 handleExceptionInBreakpointCondition(m_currentCallFrame, exception); 481 return false; 482 } 483 484 return result.toBoolean(m_currentCallFrame); 485} 486 487class Debugger::ClearCodeBlockDebuggerRequestsFunctor { 488public: 489 ClearCodeBlockDebuggerRequestsFunctor(Debugger* debugger) 490 : m_debugger(debugger) 491 { 492 } 493 494 bool operator()(CodeBlock* codeBlock) 495 { 496 if (codeBlock->hasDebuggerRequests() && m_debugger == codeBlock->globalObject()->debugger()) 497 codeBlock->clearDebuggerRequests(); 498 return false; 499 } 500 501private: 502 Debugger* m_debugger; 503}; 504 505void Debugger::clearBreakpoints() 506{ 507 m_topBreakpointID = noBreakpointID; 508 m_breakpointIDToBreakpoint.clear(); 509 m_sourceIDToBreakpoints.clear(); 510 511 if (!m_vm) 512 return; 513 ClearCodeBlockDebuggerRequestsFunctor functor(this); 514 forEachCodeBlock(functor); 515} 516 517class Debugger::ClearDebuggerRequestsFunctor { 518public: 519 ClearDebuggerRequestsFunctor(JSGlobalObject* globalObject) 520 : m_globalObject(globalObject) 521 { 522 } 523 524 bool operator()(CodeBlock* codeBlock) 525 { 526 if (codeBlock->hasDebuggerRequests() && m_globalObject == codeBlock->globalObject()) 527 codeBlock->clearDebuggerRequests(); 528 return false; 529 } 530 531private: 532 JSGlobalObject* m_globalObject; 533}; 534 535void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject) 536{ 537 ASSERT(m_vm); 538 ClearDebuggerRequestsFunctor functor(globalObject); 539 forEachCodeBlock(functor); 540} 541 542void Debugger::setBreakpointsActivated(bool activated) 543{ 544 m_breakpointsActivated = activated; 545} 546 547void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause) 548{ 549 m_pauseOnExceptionsState = pause; 550} 551 552void Debugger::setPauseOnNextStatement(bool pause) 553{ 554 m_pauseOnNextStatement = pause; 555 if (pause) 556 setSteppingMode(SteppingModeEnabled); 557} 558 559void Debugger::breakProgram() 560{ 561 if (m_isPaused) 562 return; 563 564 m_pauseOnNextStatement = true; 565 setSteppingMode(SteppingModeEnabled); 566 m_currentCallFrame = m_vm->topCallFrame; 567 ASSERT(m_currentCallFrame); 568 pauseIfNeeded(m_currentCallFrame); 569} 570 571void Debugger::continueProgram() 572{ 573 if (!m_isPaused) 574 return; 575 576 m_pauseOnNextStatement = false; 577 notifyDoneProcessingDebuggerEvents(); 578} 579 580void Debugger::stepIntoStatement() 581{ 582 if (!m_isPaused) 583 return; 584 585 m_pauseOnNextStatement = true; 586 setSteppingMode(SteppingModeEnabled); 587 notifyDoneProcessingDebuggerEvents(); 588} 589 590void Debugger::stepOverStatement() 591{ 592 if (!m_isPaused) 593 return; 594 595 m_pauseOnCallFrame = m_currentCallFrame; 596 notifyDoneProcessingDebuggerEvents(); 597} 598 599void Debugger::stepOutOfFunction() 600{ 601 if (!m_isPaused) 602 return; 603 604 m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrameSkippingVMEntrySentinel() : 0; 605 notifyDoneProcessingDebuggerEvents(); 606} 607 608void Debugger::updateCallFrame(CallFrame* callFrame) 609{ 610 m_currentCallFrame = callFrame; 611 SourceID sourceID = DebuggerCallFrame::sourceIDForCallFrame(callFrame); 612 if (m_lastExecutedSourceID != sourceID) { 613 m_lastExecutedLine = UINT_MAX; 614 m_lastExecutedSourceID = sourceID; 615 } 616} 617 618void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame) 619{ 620 updateCallFrame(callFrame); 621 pauseIfNeeded(callFrame); 622 if (!isStepping()) 623 m_currentCallFrame = 0; 624} 625 626void Debugger::pauseIfNeeded(CallFrame* callFrame) 627{ 628 if (m_isPaused) 629 return; 630 631 JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject(); 632 if (!needPauseHandling(vmEntryGlobalObject)) 633 return; 634 635 Breakpoint breakpoint; 636 bool didHitBreakpoint = false; 637 bool pauseNow = m_pauseOnNextStatement; 638 pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame); 639 640 DebuggerCallFrameScope debuggerCallFrameScope(*this); 641 642 intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame); 643 TextPosition position = DebuggerCallFrame::positionForCallFrame(m_currentCallFrame); 644 pauseNow |= didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint); 645 m_lastExecutedLine = position.m_line.zeroBasedInt(); 646 if (!pauseNow) 647 return; 648 649 // Make sure we are not going to pause again on breakpoint actions by 650 // reseting the pause state before executing any breakpoint actions. 651 TemporaryPausedState pausedState(*this); 652 m_pauseOnCallFrame = 0; 653 m_pauseOnNextStatement = false; 654 655 if (didHitBreakpoint) { 656 handleBreakpointHit(breakpoint); 657 // Note that the actions can potentially stop the debugger, so we need to check that 658 // we still have a current call frame when we get back. 659 if (breakpoint.autoContinue || !m_currentCallFrame) 660 return; 661 } 662 663 handlePause(m_reasonForPause, vmEntryGlobalObject); 664 665 if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) { 666 setSteppingMode(SteppingModeDisabled); 667 m_currentCallFrame = nullptr; 668 } 669} 670 671void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasHandler) 672{ 673 if (m_isPaused) 674 return; 675 676 PauseReasonDeclaration reason(*this, PausedForException); 677 if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) { 678 m_pauseOnNextStatement = true; 679 setSteppingMode(SteppingModeEnabled); 680 } 681 682 m_hasHandlerForExceptionCallback = true; 683 m_currentException = exception; 684 updateCallFrameAndPauseIfNeeded(callFrame); 685 m_currentException = JSValue(); 686 m_hasHandlerForExceptionCallback = false; 687} 688 689void Debugger::atStatement(CallFrame* callFrame) 690{ 691 if (m_isPaused) 692 return; 693 694 PauseReasonDeclaration reason(*this, PausedAtStatement); 695 updateCallFrameAndPauseIfNeeded(callFrame); 696} 697 698void Debugger::callEvent(CallFrame* callFrame) 699{ 700 if (m_isPaused) 701 return; 702 703 PauseReasonDeclaration reason(*this, PausedAfterCall); 704 updateCallFrameAndPauseIfNeeded(callFrame); 705} 706 707void Debugger::returnEvent(CallFrame* callFrame) 708{ 709 if (m_isPaused) 710 return; 711 712 PauseReasonDeclaration reason(*this, PausedBeforeReturn); 713 updateCallFrameAndPauseIfNeeded(callFrame); 714 715 // detach may have been called during pauseIfNeeded 716 if (!m_currentCallFrame) 717 return; 718 719 // Treat stepping over a return statement like stepping out. 720 if (m_currentCallFrame == m_pauseOnCallFrame) 721 m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 722 723 m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 724} 725 726void Debugger::willExecuteProgram(CallFrame* callFrame) 727{ 728 if (m_isPaused) 729 return; 730 731 PauseReasonDeclaration reason(*this, PausedAtStartOfProgram); 732 // FIXME: This check for whether we're debugging a worker thread is a workaround 733 // for https://bugs.webkit.org/show_bug.cgi?id=102637. Remove it when we rework 734 // the debugger implementation to not require callbacks. 735 if (!m_isInWorkerThread) 736 updateCallFrameAndPauseIfNeeded(callFrame); 737 else if (isStepping()) 738 updateCallFrame(callFrame); 739} 740 741void Debugger::didExecuteProgram(CallFrame* callFrame) 742{ 743 if (m_isPaused) 744 return; 745 746 PauseReasonDeclaration reason(*this, PausedAtEndOfProgram); 747 updateCallFrameAndPauseIfNeeded(callFrame); 748 749 // Treat stepping over the end of a program like stepping out. 750 if (!m_currentCallFrame) 751 return; 752 if (m_currentCallFrame == m_pauseOnCallFrame) { 753 m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 754 if (!m_currentCallFrame) 755 return; 756 } 757 m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); 758} 759 760void Debugger::didReachBreakpoint(CallFrame* callFrame) 761{ 762 if (m_isPaused) 763 return; 764 765 PauseReasonDeclaration reason(*this, PausedForBreakpoint); 766 m_pauseOnNextStatement = true; 767 setSteppingMode(SteppingModeEnabled); 768 updateCallFrameAndPauseIfNeeded(callFrame); 769} 770 771DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const 772{ 773 ASSERT(m_currentDebuggerCallFrame); 774 return m_currentDebuggerCallFrame.get(); 775} 776 777} // namespace JSC 778