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)
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.
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.
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
25 #include "CodeBlock.h"
26 #include "DebuggerCallFrame.h"
28 #include "HeapIterationScope.h"
29 #include "Interpreter.h"
30 #include "JSCJSValueInlines.h"
31 #include "JSFunction.h"
32 #include "JSGlobalObject.h"
33 #include "JSCInlines.h"
36 #include "VMEntryScope.h"
42 class Recompiler
: public MarkedBlock::VoidFunctor
{
44 Recompiler(JSC::Debugger
*);
46 IterationStatus
operator()(JSCell
*);
49 typedef HashSet
<FunctionExecutable
*> FunctionExecutableSet
;
50 typedef HashMap
<SourceProvider
*, ExecState
*> SourceProviderMap
;
54 JSC::Debugger
* m_debugger
;
55 FunctionExecutableSet m_functionExecutables
;
56 SourceProviderMap m_sourceProviders
;
59 inline Recompiler::Recompiler(JSC::Debugger
* debugger
)
60 : m_debugger(debugger
)
64 inline Recompiler::~Recompiler()
66 // Call sourceParsed() after reparsing all functions because it will execute
67 // JavaScript in the inspector.
68 SourceProviderMap::const_iterator end
= m_sourceProviders
.end();
69 for (SourceProviderMap::const_iterator iter
= m_sourceProviders
.begin(); iter
!= end
; ++iter
)
70 m_debugger
->sourceParsed(iter
->value
, iter
->key
, -1, String());
73 inline void Recompiler::visit(JSCell
* cell
)
75 if (!cell
->inherits(JSFunction::info()))
78 JSFunction
* function
= jsCast
<JSFunction
*>(cell
);
79 if (function
->executable()->isHostFunction())
82 FunctionExecutable
* executable
= function
->jsExecutable();
84 // Check if the function is already in the set - if so,
85 // we've already retranslated it, nothing to do here.
86 if (!m_functionExecutables
.add(executable
).isNewEntry
)
89 ExecState
* exec
= function
->scope()->globalObject()->JSGlobalObject::globalExec();
90 executable
->clearCode();
91 executable
->clearUnlinkedCodeForRecompilation();
92 if (m_debugger
== function
->scope()->globalObject()->debugger())
93 m_sourceProviders
.add(executable
->source().provider(), exec
);
96 inline IterationStatus
Recompiler::operator()(JSCell
* cell
)
99 return IterationStatus::Continue
;
106 class DebuggerPausedScope
{
108 DebuggerPausedScope(Debugger
& debugger
)
109 : m_debugger(debugger
)
111 ASSERT(!m_debugger
.m_currentDebuggerCallFrame
);
112 if (m_debugger
.m_currentCallFrame
)
113 m_debugger
.m_currentDebuggerCallFrame
= DebuggerCallFrame::create(debugger
.m_currentCallFrame
);
116 ~DebuggerPausedScope()
118 if (m_debugger
.m_currentDebuggerCallFrame
) {
119 m_debugger
.m_currentDebuggerCallFrame
->invalidate();
120 m_debugger
.m_currentDebuggerCallFrame
= nullptr;
125 Debugger
& m_debugger
;
128 // This is very similar to TemporaryChange<bool>, but that cannot be used
129 // as the m_isPaused field uses only one bit.
130 class TemporaryPausedState
{
132 TemporaryPausedState(Debugger
& debugger
)
133 : m_debugger(debugger
)
135 ASSERT(!m_debugger
.m_isPaused
);
136 m_debugger
.m_isPaused
= true;
139 ~TemporaryPausedState()
141 m_debugger
.m_isPaused
= false;
145 Debugger
& m_debugger
;
148 template<typename Functor
>
149 void Debugger::forEachCodeBlock(Functor
& functor
)
151 m_vm
->prepareToDiscardCode();
152 m_vm
->heap
.forEachCodeBlock(functor
);
155 Debugger::Debugger(bool isInWorkerThread
)
157 , m_pauseOnExceptionsState(DontPauseOnExceptions
)
158 , m_pauseOnNextStatement(false)
160 , m_breakpointsActivated(true)
161 , m_hasHandlerForExceptionCallback(false)
162 , m_isInWorkerThread(isInWorkerThread
)
163 , m_steppingMode(SteppingModeDisabled
)
164 , m_reasonForPause(NotPaused
)
165 , m_pauseOnCallFrame(0)
166 , m_currentCallFrame(0)
167 , m_lastExecutedLine(UINT_MAX
)
168 , m_lastExecutedSourceID(noSourceID
)
169 , m_topBreakpointID(noBreakpointID
)
170 , m_pausingBreakpointID(noBreakpointID
)
174 Debugger::~Debugger()
176 HashSet
<JSGlobalObject
*>::iterator end
= m_globalObjects
.end();
177 for (HashSet
<JSGlobalObject
*>::iterator it
= m_globalObjects
.begin(); it
!= end
; ++it
)
178 (*it
)->setDebugger(0);
181 void Debugger::attach(JSGlobalObject
* globalObject
)
183 ASSERT(!globalObject
->debugger());
185 m_vm
= &globalObject
->vm();
187 ASSERT(m_vm
== &globalObject
->vm());
188 globalObject
->setDebugger(this);
189 m_globalObjects
.add(globalObject
);
192 void Debugger::detach(JSGlobalObject
* globalObject
, ReasonForDetach reason
)
194 // If we're detaching from the currently executing global object, manually tear down our
195 // stack, since we won't get further debugger callbacks to do so. Also, resume execution,
196 // since there's no point in staying paused once a window closes.
197 if (m_currentCallFrame
&& m_currentCallFrame
->vmEntryGlobalObject() == globalObject
) {
198 m_currentCallFrame
= 0;
199 m_pauseOnCallFrame
= 0;
203 ASSERT(m_globalObjects
.contains(globalObject
));
204 m_globalObjects
.remove(globalObject
);
206 // If the globalObject is destructing, then its CodeBlocks will also be
207 // destructed. There is no need to do the debugger requests clean up, and
208 // it is not safe to access those CodeBlocks at this time anyway.
209 if (reason
!= GlobalObjectIsDestructing
)
210 clearDebuggerRequests(globalObject
);
212 globalObject
->setDebugger(0);
213 if (!m_globalObjects
.size())
217 bool Debugger::isAttached(JSGlobalObject
* globalObject
)
219 return globalObject
->debugger() == this;
222 class Debugger::SetSteppingModeFunctor
{
224 SetSteppingModeFunctor(Debugger
* debugger
, SteppingMode mode
)
225 : m_debugger(debugger
)
230 bool operator()(CodeBlock
* codeBlock
)
232 if (m_debugger
== codeBlock
->globalObject()->debugger()) {
233 if (m_mode
== SteppingModeEnabled
)
234 codeBlock
->setSteppingMode(CodeBlock::SteppingModeEnabled
);
236 codeBlock
->setSteppingMode(CodeBlock::SteppingModeDisabled
);
242 Debugger
* m_debugger
;
246 void Debugger::setSteppingMode(SteppingMode mode
)
248 if (mode
== m_steppingMode
|| !m_vm
)
251 m_vm
->prepareToDiscardCode();
253 m_steppingMode
= mode
;
254 SetSteppingModeFunctor
functor(this, mode
);
255 m_vm
->heap
.forEachCodeBlock(functor
);
258 void Debugger::registerCodeBlock(CodeBlock
* codeBlock
)
260 // FIXME: We should never have to jettison a code block (due to pending breakpoints
261 // or stepping mode) that is being registered. operationOptimize() should have
262 // prevented the optimizing of such code blocks in the first place. Find a way to
263 // express this with greater clarity in the code. See <https://webkit.org/b131771>.
264 applyBreakpoints(codeBlock
);
266 codeBlock
->setSteppingMode(CodeBlock::SteppingModeEnabled
);
269 void Debugger::toggleBreakpoint(CodeBlock
* codeBlock
, Breakpoint
& breakpoint
, BreakpointState enabledOrNot
)
271 ScriptExecutable
* executable
= codeBlock
->ownerExecutable();
273 SourceID sourceID
= static_cast<SourceID
>(executable
->sourceID());
274 if (breakpoint
.sourceID
!= sourceID
)
277 unsigned line
= breakpoint
.line
;
278 unsigned column
= breakpoint
.column
;
280 unsigned startLine
= executable
->firstLine();
281 unsigned startColumn
= executable
->startColumn();
282 unsigned endLine
= executable
->lastLine();
283 unsigned endColumn
= executable
->endColumn();
285 // Inspector breakpoint line and column values are zero-based but the executable
286 // and CodeBlock line and column values are one-based.
288 column
= column
? column
+ 1 : Breakpoint::unspecifiedColumn
;
290 if (line
< startLine
|| line
> endLine
)
292 if (column
!= Breakpoint::unspecifiedColumn
) {
293 if (line
== startLine
&& column
< startColumn
)
295 if (line
== endLine
&& column
> endColumn
)
298 if (!codeBlock
->hasOpDebugForLineAndColumn(line
, column
))
301 if (enabledOrNot
== BreakpointEnabled
)
302 codeBlock
->addBreakpoint(1);
304 codeBlock
->removeBreakpoint(1);
307 void Debugger::applyBreakpoints(CodeBlock
* codeBlock
)
309 BreakpointIDToBreakpointMap
& breakpoints
= m_breakpointIDToBreakpoint
;
310 for (auto it
= breakpoints
.begin(); it
!= breakpoints
.end(); ++it
) {
311 Breakpoint
& breakpoint
= *it
->value
;
312 toggleBreakpoint(codeBlock
, breakpoint
, BreakpointEnabled
);
316 class Debugger::ToggleBreakpointFunctor
{
318 ToggleBreakpointFunctor(Debugger
* debugger
, Breakpoint
& breakpoint
, BreakpointState enabledOrNot
)
319 : m_debugger(debugger
)
320 , m_breakpoint(breakpoint
)
321 , m_enabledOrNot(enabledOrNot
)
325 bool operator()(CodeBlock
* codeBlock
)
327 if (m_debugger
== codeBlock
->globalObject()->debugger())
328 m_debugger
->toggleBreakpoint(codeBlock
, m_breakpoint
, m_enabledOrNot
);
333 Debugger
* m_debugger
;
334 Breakpoint
& m_breakpoint
;
335 BreakpointState m_enabledOrNot
;
338 void Debugger::toggleBreakpoint(Breakpoint
& breakpoint
, Debugger::BreakpointState enabledOrNot
)
342 ToggleBreakpointFunctor
functor(this, breakpoint
, enabledOrNot
);
343 forEachCodeBlock(functor
);
346 void Debugger::recompileAllJSFunctions(VM
* vm
)
348 // If JavaScript is running, it's not safe to recompile, since we'll end
349 // up throwing away code that is live on the stack.
350 if (vm
->entryScope
) {
351 auto listener
= [] (VM
& vm
, JSGlobalObject
* globalObject
)
353 if (Debugger
* debugger
= globalObject
->debugger())
354 debugger
->recompileAllJSFunctions(&vm
);
357 vm
->entryScope
->setEntryScopeDidPopListener(this, listener
);
361 vm
->prepareToDiscardCode();
363 Recompiler
recompiler(this);
364 HeapIterationScope
iterationScope(vm
->heap
);
365 vm
->heap
.objectSpace().forEachLiveCell(iterationScope
, recompiler
);
368 BreakpointID
Debugger::setBreakpoint(Breakpoint breakpoint
, unsigned& actualLine
, unsigned& actualColumn
)
370 SourceID sourceID
= breakpoint
.sourceID
;
371 unsigned line
= breakpoint
.line
;
372 unsigned column
= breakpoint
.column
;
374 SourceIDToBreakpointsMap::iterator it
= m_sourceIDToBreakpoints
.find(sourceID
);
375 if (it
== m_sourceIDToBreakpoints
.end())
376 it
= m_sourceIDToBreakpoints
.set(sourceID
, LineToBreakpointsMap()).iterator
;
377 LineToBreakpointsMap::iterator breaksIt
= it
->value
.find(line
);
378 if (breaksIt
== it
->value
.end())
379 breaksIt
= it
->value
.set(line
, adoptRef(new BreakpointsList
)).iterator
;
381 BreakpointsList
& breakpoints
= *breaksIt
->value
;
382 for (Breakpoint
* current
= breakpoints
.head(); current
; current
= current
->next()) {
383 if (current
->column
== column
) {
384 // The breakpoint already exists. We're not allowed to create a new
385 // breakpoint at this location. Rather than returning the breakpointID
386 // of the pre-existing breakpoint, we need to return noBreakpointID
387 // to indicate that we're not creating a new one.
388 return noBreakpointID
;
392 BreakpointID id
= ++m_topBreakpointID
;
393 RELEASE_ASSERT(id
!= noBreakpointID
);
397 actualColumn
= column
;
399 Breakpoint
* newBreakpoint
= new Breakpoint(breakpoint
);
400 breakpoints
.append(newBreakpoint
);
401 m_breakpointIDToBreakpoint
.set(id
, newBreakpoint
);
403 toggleBreakpoint(breakpoint
, BreakpointEnabled
);
408 void Debugger::removeBreakpoint(BreakpointID id
)
410 ASSERT(id
!= noBreakpointID
);
412 BreakpointIDToBreakpointMap::iterator idIt
= m_breakpointIDToBreakpoint
.find(id
);
413 ASSERT(idIt
!= m_breakpointIDToBreakpoint
.end());
414 Breakpoint
* breakpoint
= idIt
->value
;
416 SourceID sourceID
= breakpoint
->sourceID
;
418 SourceIDToBreakpointsMap::iterator it
= m_sourceIDToBreakpoints
.find(sourceID
);
419 ASSERT(it
!= m_sourceIDToBreakpoints
.end());
420 LineToBreakpointsMap::iterator breaksIt
= it
->value
.find(breakpoint
->line
);
421 ASSERT(breaksIt
!= it
->value
.end());
423 toggleBreakpoint(*breakpoint
, BreakpointDisabled
);
425 BreakpointsList
& breakpoints
= *breaksIt
->value
;
428 for (Breakpoint
* current
= breakpoints
.head(); current
&& !found
; current
= current
->next()) {
429 if (current
->id
== breakpoint
->id
)
435 m_breakpointIDToBreakpoint
.remove(idIt
);
436 breakpoints
.remove(breakpoint
);
439 if (breakpoints
.isEmpty()) {
440 it
->value
.remove(breaksIt
);
441 if (it
->value
.isEmpty())
442 m_sourceIDToBreakpoints
.remove(it
);
446 bool Debugger::hasBreakpoint(SourceID sourceID
, const TextPosition
& position
, Breakpoint
*hitBreakpoint
)
448 if (!m_breakpointsActivated
)
451 SourceIDToBreakpointsMap::const_iterator it
= m_sourceIDToBreakpoints
.find(sourceID
);
452 if (it
== m_sourceIDToBreakpoints
.end())
455 unsigned line
= position
.m_line
.zeroBasedInt();
456 unsigned column
= position
.m_column
.zeroBasedInt();
458 LineToBreakpointsMap::const_iterator breaksIt
= it
->value
.find(line
);
459 if (breaksIt
== it
->value
.end())
463 const BreakpointsList
& breakpoints
= *breaksIt
->value
;
464 Breakpoint
* breakpoint
;
465 for (breakpoint
= breakpoints
.head(); breakpoint
; breakpoint
= breakpoint
->next()) {
466 unsigned breakLine
= breakpoint
->line
;
467 unsigned breakColumn
= breakpoint
->column
;
468 // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0).
469 ASSERT(this == m_currentCallFrame
->codeBlock()->globalObject()->debugger());
470 if ((line
!= m_lastExecutedLine
&& line
== breakLine
&& !breakColumn
)
471 || (line
== breakLine
&& column
== breakColumn
)) {
480 *hitBreakpoint
= *breakpoint
;
482 if (breakpoint
->condition
.isEmpty())
485 // We cannot stop in the debugger while executing condition code,
486 // so make it looks like the debugger is already paused.
487 TemporaryPausedState
pausedState(*this);
489 NakedPtr
<Exception
> exception
;
490 DebuggerCallFrame
* debuggerCallFrame
= currentDebuggerCallFrame();
491 JSValue result
= debuggerCallFrame
->evaluate(breakpoint
->condition
, exception
);
493 // We can lose the debugger while executing JavaScript.
494 if (!m_currentCallFrame
)
498 // An erroneous condition counts as "false".
499 handleExceptionInBreakpointCondition(m_currentCallFrame
, exception
);
503 return result
.toBoolean(m_currentCallFrame
);
506 class Debugger::ClearCodeBlockDebuggerRequestsFunctor
{
508 ClearCodeBlockDebuggerRequestsFunctor(Debugger
* debugger
)
509 : m_debugger(debugger
)
513 bool operator()(CodeBlock
* codeBlock
)
515 if (codeBlock
->hasDebuggerRequests() && m_debugger
== codeBlock
->globalObject()->debugger())
516 codeBlock
->clearDebuggerRequests();
521 Debugger
* m_debugger
;
524 void Debugger::clearBreakpoints()
526 m_topBreakpointID
= noBreakpointID
;
527 m_breakpointIDToBreakpoint
.clear();
528 m_sourceIDToBreakpoints
.clear();
532 ClearCodeBlockDebuggerRequestsFunctor
functor(this);
533 forEachCodeBlock(functor
);
536 class Debugger::ClearDebuggerRequestsFunctor
{
538 ClearDebuggerRequestsFunctor(JSGlobalObject
* globalObject
)
539 : m_globalObject(globalObject
)
543 bool operator()(CodeBlock
* codeBlock
)
545 if (codeBlock
->hasDebuggerRequests() && m_globalObject
== codeBlock
->globalObject())
546 codeBlock
->clearDebuggerRequests();
551 JSGlobalObject
* m_globalObject
;
554 void Debugger::clearDebuggerRequests(JSGlobalObject
* globalObject
)
557 ClearDebuggerRequestsFunctor
functor(globalObject
);
558 forEachCodeBlock(functor
);
561 void Debugger::setBreakpointsActivated(bool activated
)
563 m_breakpointsActivated
= activated
;
566 void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause
)
568 m_pauseOnExceptionsState
= pause
;
571 void Debugger::setPauseOnNextStatement(bool pause
)
573 m_pauseOnNextStatement
= pause
;
575 setSteppingMode(SteppingModeEnabled
);
578 void Debugger::breakProgram()
583 m_pauseOnNextStatement
= true;
584 setSteppingMode(SteppingModeEnabled
);
585 m_currentCallFrame
= m_vm
->topCallFrame
;
586 ASSERT(m_currentCallFrame
);
587 pauseIfNeeded(m_currentCallFrame
);
590 void Debugger::continueProgram()
595 m_pauseOnNextStatement
= false;
596 notifyDoneProcessingDebuggerEvents();
599 void Debugger::stepIntoStatement()
604 m_pauseOnNextStatement
= true;
605 setSteppingMode(SteppingModeEnabled
);
606 notifyDoneProcessingDebuggerEvents();
609 void Debugger::stepOverStatement()
614 m_pauseOnCallFrame
= m_currentCallFrame
;
615 notifyDoneProcessingDebuggerEvents();
618 void Debugger::stepOutOfFunction()
623 VMEntryFrame
* topVMEntryFrame
= m_vm
->topVMEntryFrame
;
624 m_pauseOnCallFrame
= m_currentCallFrame
? m_currentCallFrame
->callerFrame(topVMEntryFrame
) : 0;
625 notifyDoneProcessingDebuggerEvents();
628 void Debugger::updateCallFrame(CallFrame
* callFrame
)
630 m_currentCallFrame
= callFrame
;
631 SourceID sourceID
= DebuggerCallFrame::sourceIDForCallFrame(callFrame
);
632 if (m_lastExecutedSourceID
!= sourceID
) {
633 m_lastExecutedLine
= UINT_MAX
;
634 m_lastExecutedSourceID
= sourceID
;
638 void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame
* callFrame
)
640 updateCallFrame(callFrame
);
641 pauseIfNeeded(callFrame
);
643 m_currentCallFrame
= 0;
646 void Debugger::pauseIfNeeded(CallFrame
* callFrame
)
651 JSGlobalObject
* vmEntryGlobalObject
= callFrame
->vmEntryGlobalObject();
652 if (!needPauseHandling(vmEntryGlobalObject
))
655 Breakpoint breakpoint
;
656 bool didHitBreakpoint
= false;
657 bool pauseNow
= m_pauseOnNextStatement
;
658 pauseNow
|= (m_pauseOnCallFrame
== m_currentCallFrame
);
660 DebuggerPausedScope
debuggerPausedScope(*this);
662 intptr_t sourceID
= DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame
);
663 TextPosition position
= DebuggerCallFrame::positionForCallFrame(m_currentCallFrame
);
664 pauseNow
|= didHitBreakpoint
= hasBreakpoint(sourceID
, position
, &breakpoint
);
665 m_lastExecutedLine
= position
.m_line
.zeroBasedInt();
669 // Make sure we are not going to pause again on breakpoint actions by
670 // reseting the pause state before executing any breakpoint actions.
671 TemporaryPausedState
pausedState(*this);
672 m_pauseOnCallFrame
= 0;
673 m_pauseOnNextStatement
= false;
675 if (didHitBreakpoint
) {
676 handleBreakpointHit(vmEntryGlobalObject
, breakpoint
);
677 // Note that the actions can potentially stop the debugger, so we need to check that
678 // we still have a current call frame when we get back.
679 if (breakpoint
.autoContinue
|| !m_currentCallFrame
)
681 m_pausingBreakpointID
= breakpoint
.id
;
685 PauseReasonDeclaration
reason(*this, didHitBreakpoint
? PausedForBreakpoint
: m_reasonForPause
);
686 handlePause(vmEntryGlobalObject
, m_reasonForPause
);
689 m_pausingBreakpointID
= noBreakpointID
;
691 if (!m_pauseOnNextStatement
&& !m_pauseOnCallFrame
) {
692 setSteppingMode(SteppingModeDisabled
);
693 m_currentCallFrame
= nullptr;
697 void Debugger::exception(CallFrame
* callFrame
, JSValue exception
, bool hasCatchHandler
)
702 PauseReasonDeclaration
reason(*this, PausedForException
);
703 if (m_pauseOnExceptionsState
== PauseOnAllExceptions
|| (m_pauseOnExceptionsState
== PauseOnUncaughtExceptions
&& !hasCatchHandler
)) {
704 m_pauseOnNextStatement
= true;
705 setSteppingMode(SteppingModeEnabled
);
708 m_hasHandlerForExceptionCallback
= true;
709 m_currentException
= exception
;
710 updateCallFrameAndPauseIfNeeded(callFrame
);
711 m_currentException
= JSValue();
712 m_hasHandlerForExceptionCallback
= false;
715 void Debugger::atStatement(CallFrame
* callFrame
)
720 PauseReasonDeclaration
reason(*this, PausedAtStatement
);
721 updateCallFrameAndPauseIfNeeded(callFrame
);
724 void Debugger::callEvent(CallFrame
* callFrame
)
729 PauseReasonDeclaration
reason(*this, PausedAfterCall
);
730 updateCallFrameAndPauseIfNeeded(callFrame
);
733 void Debugger::returnEvent(CallFrame
* callFrame
)
738 PauseReasonDeclaration
reason(*this, PausedBeforeReturn
);
739 updateCallFrameAndPauseIfNeeded(callFrame
);
741 // detach may have been called during pauseIfNeeded
742 if (!m_currentCallFrame
)
745 // Treat stepping over a return statement like stepping out.
746 if (m_currentCallFrame
== m_pauseOnCallFrame
) {
747 VMEntryFrame
* topVMEntryFrame
= m_vm
->topVMEntryFrame
;
748 m_pauseOnCallFrame
= m_currentCallFrame
->callerFrame(topVMEntryFrame
);
751 VMEntryFrame
* topVMEntryFrame
= m_vm
->topVMEntryFrame
;
752 m_currentCallFrame
= m_currentCallFrame
->callerFrame(topVMEntryFrame
);
755 void Debugger::willExecuteProgram(CallFrame
* callFrame
)
760 PauseReasonDeclaration
reason(*this, PausedAtStartOfProgram
);
761 // FIXME: This check for whether we're debugging a worker thread is a workaround
762 // for https://bugs.webkit.org/show_bug.cgi?id=102637. Remove it when we rework
763 // the debugger implementation to not require callbacks.
764 if (!m_isInWorkerThread
)
765 updateCallFrameAndPauseIfNeeded(callFrame
);
766 else if (isStepping())
767 updateCallFrame(callFrame
);
770 void Debugger::didExecuteProgram(CallFrame
* callFrame
)
775 PauseReasonDeclaration
reason(*this, PausedAtEndOfProgram
);
776 updateCallFrameAndPauseIfNeeded(callFrame
);
778 // Treat stepping over the end of a program like stepping out.
779 if (!m_currentCallFrame
)
781 if (m_currentCallFrame
== m_pauseOnCallFrame
) {
782 VMEntryFrame
* topVMEntryFrame
= m_vm
->topVMEntryFrame
;
783 m_pauseOnCallFrame
= m_currentCallFrame
->callerFrame(topVMEntryFrame
);
784 if (!m_currentCallFrame
)
787 VMEntryFrame
* topVMEntryFrame
= m_vm
->topVMEntryFrame
;
788 m_currentCallFrame
= m_currentCallFrame
->callerFrame(topVMEntryFrame
);
791 void Debugger::didReachBreakpoint(CallFrame
* callFrame
)
796 PauseReasonDeclaration
reason(*this, PausedForDebuggerStatement
);
797 m_pauseOnNextStatement
= true;
798 setSteppingMode(SteppingModeEnabled
);
799 updateCallFrameAndPauseIfNeeded(callFrame
);
802 DebuggerCallFrame
* Debugger::currentDebuggerCallFrame() const
804 ASSERT(m_currentDebuggerCallFrame
);
805 return m_currentDebuggerCallFrame
.get();