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"
29 #include "HeapIterationScope.h"
30 #include "Interpreter.h"
31 #include "JSCJSValueInlines.h"
32 #include "JSFunction.h"
33 #include "JSGlobalObject.h"
34 #include "JSCInlines.h"
37 #include "VMEntryScope.h"
43 class Recompiler
: public MarkedBlock::VoidFunctor
{
45 Recompiler(JSC::Debugger
*);
47 void operator()(JSCell
*);
50 typedef HashSet
<FunctionExecutable
*> FunctionExecutableSet
;
51 typedef HashMap
<SourceProvider
*, ExecState
*> SourceProviderMap
;
53 JSC::Debugger
* m_debugger
;
54 FunctionExecutableSet m_functionExecutables
;
55 SourceProviderMap m_sourceProviders
;
58 inline Recompiler::Recompiler(JSC::Debugger
* debugger
)
59 : m_debugger(debugger
)
63 inline Recompiler::~Recompiler()
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());
72 inline void Recompiler::operator()(JSCell
* cell
)
74 if (!cell
->inherits(JSFunction::info()))
77 JSFunction
* function
= jsCast
<JSFunction
*>(cell
);
78 if (function
->executable()->isHostFunction())
81 FunctionExecutable
* executable
= function
->jsExecutable();
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
)
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
);
99 class DebuggerCallFrameScope
{
101 DebuggerCallFrameScope(Debugger
& debugger
)
102 : m_debugger(debugger
)
104 ASSERT(!m_debugger
.m_currentDebuggerCallFrame
);
105 if (m_debugger
.m_currentCallFrame
)
106 m_debugger
.m_currentDebuggerCallFrame
= DebuggerCallFrame::create(debugger
.m_currentCallFrame
);
109 ~DebuggerCallFrameScope()
111 if (m_debugger
.m_currentDebuggerCallFrame
) {
112 m_debugger
.m_currentDebuggerCallFrame
->invalidate();
113 m_debugger
.m_currentDebuggerCallFrame
= 0;
118 Debugger
& m_debugger
;
121 // This is very similar to TemporaryChange<bool>, but that cannot be used
122 // as the m_isPaused field uses only one bit.
123 class TemporaryPausedState
{
125 TemporaryPausedState(Debugger
& debugger
)
126 : m_debugger(debugger
)
128 ASSERT(!m_debugger
.m_isPaused
);
129 m_debugger
.m_isPaused
= true;
132 ~TemporaryPausedState()
134 m_debugger
.m_isPaused
= false;
138 Debugger
& m_debugger
;
141 template<typename Functor
>
142 void Debugger::forEachCodeBlock(Functor
& functor
)
144 m_vm
->waitForCompilationsToComplete();
145 m_vm
->heap
.forEachCodeBlock(functor
);
148 Debugger::Debugger(bool isInWorkerThread
)
150 , m_pauseOnExceptionsState(DontPauseOnExceptions
)
151 , m_pauseOnNextStatement(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
)
166 Debugger::~Debugger()
168 HashSet
<JSGlobalObject
*>::iterator end
= m_globalObjects
.end();
169 for (HashSet
<JSGlobalObject
*>::iterator it
= m_globalObjects
.begin(); it
!= end
; ++it
)
170 (*it
)->setDebugger(0);
173 void Debugger::attach(JSGlobalObject
* globalObject
)
175 ASSERT(!globalObject
->debugger());
177 m_vm
= &globalObject
->vm();
179 ASSERT(m_vm
== &globalObject
->vm());
180 globalObject
->setDebugger(this);
181 m_globalObjects
.add(globalObject
);
184 void Debugger::detach(JSGlobalObject
* globalObject
, ReasonForDetach reason
)
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;
195 ASSERT(m_globalObjects
.contains(globalObject
));
196 m_globalObjects
.remove(globalObject
);
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
);
204 globalObject
->setDebugger(0);
205 if (!m_globalObjects
.size())
209 class Debugger::SetSteppingModeFunctor
{
211 SetSteppingModeFunctor(Debugger
* debugger
, SteppingMode mode
)
212 : m_debugger(debugger
)
217 bool operator()(CodeBlock
* codeBlock
)
219 if (m_debugger
== codeBlock
->globalObject()->debugger()) {
220 if (m_mode
== SteppingModeEnabled
)
221 codeBlock
->setSteppingMode(CodeBlock::SteppingModeEnabled
);
223 codeBlock
->setSteppingMode(CodeBlock::SteppingModeDisabled
);
229 Debugger
* m_debugger
;
233 void Debugger::setSteppingMode(SteppingMode mode
)
235 if (mode
== m_steppingMode
|| !m_vm
)
238 m_vm
->waitForCompilationsToComplete();
240 m_steppingMode
= mode
;
241 SetSteppingModeFunctor
functor(this, mode
);
242 m_vm
->heap
.forEachCodeBlock(functor
);
245 void Debugger::registerCodeBlock(CodeBlock
* codeBlock
)
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
);
253 codeBlock
->setSteppingMode(CodeBlock::SteppingModeEnabled
);
256 void Debugger::toggleBreakpoint(CodeBlock
* codeBlock
, Breakpoint
& breakpoint
, BreakpointState enabledOrNot
)
258 ScriptExecutable
* executable
= codeBlock
->ownerExecutable();
260 SourceID sourceID
= static_cast<SourceID
>(executable
->sourceID());
261 if (breakpoint
.sourceID
!= sourceID
)
264 unsigned line
= breakpoint
.line
;
265 unsigned column
= breakpoint
.column
;
267 unsigned startLine
= executable
->lineNo();
268 unsigned startColumn
= executable
->startColumn();
269 unsigned endLine
= executable
->lastLine();
270 unsigned endColumn
= executable
->endColumn();
272 // Inspector breakpoint line and column values are zero-based but the executable
273 // and CodeBlock line and column values are one-based.
275 column
= column
? column
+ 1 : Breakpoint::unspecifiedColumn
;
277 if (line
< startLine
|| line
> endLine
)
279 if (column
!= Breakpoint::unspecifiedColumn
) {
280 if (line
== startLine
&& column
< startColumn
)
282 if (line
== endLine
&& column
> endColumn
)
285 if (!codeBlock
->hasOpDebugForLineAndColumn(line
, column
))
288 if (enabledOrNot
== BreakpointEnabled
)
289 codeBlock
->addBreakpoint(1);
291 codeBlock
->removeBreakpoint(1);
294 void Debugger::applyBreakpoints(CodeBlock
* codeBlock
)
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
);
303 class Debugger::ToggleBreakpointFunctor
{
305 ToggleBreakpointFunctor(Debugger
* debugger
, Breakpoint
& breakpoint
, BreakpointState enabledOrNot
)
306 : m_debugger(debugger
)
307 , m_breakpoint(breakpoint
)
308 , m_enabledOrNot(enabledOrNot
)
312 bool operator()(CodeBlock
* codeBlock
)
314 if (m_debugger
== codeBlock
->globalObject()->debugger())
315 m_debugger
->toggleBreakpoint(codeBlock
, m_breakpoint
, m_enabledOrNot
);
320 Debugger
* m_debugger
;
321 Breakpoint
& m_breakpoint
;
322 BreakpointState m_enabledOrNot
;
325 void Debugger::toggleBreakpoint(Breakpoint
& breakpoint
, Debugger::BreakpointState enabledOrNot
)
329 ToggleBreakpointFunctor
functor(this, breakpoint
, enabledOrNot
);
330 forEachCodeBlock(functor
);
333 void Debugger::recompileAllJSFunctions(VM
* vm
)
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);
342 vm
->waitForCompilationsToComplete();
344 Recompiler
recompiler(this);
345 HeapIterationScope
iterationScope(vm
->heap
);
346 vm
->heap
.objectSpace().forEachLiveCell(iterationScope
, recompiler
);
349 BreakpointID
Debugger::setBreakpoint(Breakpoint breakpoint
, unsigned& actualLine
, unsigned& actualColumn
)
351 SourceID sourceID
= breakpoint
.sourceID
;
352 unsigned line
= breakpoint
.line
;
353 unsigned column
= breakpoint
.column
;
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
;
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
;
373 BreakpointID id
= ++m_topBreakpointID
;
374 RELEASE_ASSERT(id
!= noBreakpointID
);
378 actualColumn
= column
;
380 Breakpoint
* newBreakpoint
= new Breakpoint(breakpoint
);
381 breakpoints
.append(newBreakpoint
);
382 m_breakpointIDToBreakpoint
.set(id
, newBreakpoint
);
384 toggleBreakpoint(breakpoint
, BreakpointEnabled
);
389 void Debugger::removeBreakpoint(BreakpointID id
)
391 ASSERT(id
!= noBreakpointID
);
393 BreakpointIDToBreakpointMap::iterator idIt
= m_breakpointIDToBreakpoint
.find(id
);
394 ASSERT(idIt
!= m_breakpointIDToBreakpoint
.end());
395 Breakpoint
* breakpoint
= idIt
->value
;
397 SourceID sourceID
= breakpoint
->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());
404 toggleBreakpoint(*breakpoint
, BreakpointDisabled
);
406 BreakpointsList
& breakpoints
= *breaksIt
->value
;
409 for (Breakpoint
* current
= breakpoints
.head(); current
&& !found
; current
= current
->next()) {
410 if (current
->id
== breakpoint
->id
)
416 m_breakpointIDToBreakpoint
.remove(idIt
);
417 breakpoints
.remove(breakpoint
);
420 if (breakpoints
.isEmpty()) {
421 it
->value
.remove(breaksIt
);
422 if (it
->value
.isEmpty())
423 m_sourceIDToBreakpoints
.remove(it
);
427 bool Debugger::hasBreakpoint(SourceID sourceID
, const TextPosition
& position
, Breakpoint
*hitBreakpoint
)
429 if (!m_breakpointsActivated
)
432 SourceIDToBreakpointsMap::const_iterator it
= m_sourceIDToBreakpoints
.find(sourceID
);
433 if (it
== m_sourceIDToBreakpoints
.end())
436 unsigned line
= position
.m_line
.zeroBasedInt();
437 unsigned column
= position
.m_column
.zeroBasedInt();
439 LineToBreakpointsMap::const_iterator breaksIt
= it
->value
.find(line
);
440 if (breaksIt
== it
->value
.end())
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
)) {
461 *hitBreakpoint
= *breakpoint
;
463 if (breakpoint
->condition
.isEmpty())
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);
471 DebuggerCallFrame
* debuggerCallFrame
= currentDebuggerCallFrame();
472 JSValue result
= debuggerCallFrame
->evaluate(breakpoint
->condition
, exception
);
474 // We can lose the debugger while executing JavaScript.
475 if (!m_currentCallFrame
)
479 // An erroneous condition counts as "false".
480 handleExceptionInBreakpointCondition(m_currentCallFrame
, exception
);
484 return result
.toBoolean(m_currentCallFrame
);
487 class Debugger::ClearCodeBlockDebuggerRequestsFunctor
{
489 ClearCodeBlockDebuggerRequestsFunctor(Debugger
* debugger
)
490 : m_debugger(debugger
)
494 bool operator()(CodeBlock
* codeBlock
)
496 if (codeBlock
->hasDebuggerRequests() && m_debugger
== codeBlock
->globalObject()->debugger())
497 codeBlock
->clearDebuggerRequests();
502 Debugger
* m_debugger
;
505 void Debugger::clearBreakpoints()
507 m_topBreakpointID
= noBreakpointID
;
508 m_breakpointIDToBreakpoint
.clear();
509 m_sourceIDToBreakpoints
.clear();
513 ClearCodeBlockDebuggerRequestsFunctor
functor(this);
514 forEachCodeBlock(functor
);
517 class Debugger::ClearDebuggerRequestsFunctor
{
519 ClearDebuggerRequestsFunctor(JSGlobalObject
* globalObject
)
520 : m_globalObject(globalObject
)
524 bool operator()(CodeBlock
* codeBlock
)
526 if (codeBlock
->hasDebuggerRequests() && m_globalObject
== codeBlock
->globalObject())
527 codeBlock
->clearDebuggerRequests();
532 JSGlobalObject
* m_globalObject
;
535 void Debugger::clearDebuggerRequests(JSGlobalObject
* globalObject
)
538 ClearDebuggerRequestsFunctor
functor(globalObject
);
539 forEachCodeBlock(functor
);
542 void Debugger::setBreakpointsActivated(bool activated
)
544 m_breakpointsActivated
= activated
;
547 void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause
)
549 m_pauseOnExceptionsState
= pause
;
552 void Debugger::setPauseOnNextStatement(bool pause
)
554 m_pauseOnNextStatement
= pause
;
556 setSteppingMode(SteppingModeEnabled
);
559 void Debugger::breakProgram()
564 m_pauseOnNextStatement
= true;
565 setSteppingMode(SteppingModeEnabled
);
566 m_currentCallFrame
= m_vm
->topCallFrame
;
567 ASSERT(m_currentCallFrame
);
568 pauseIfNeeded(m_currentCallFrame
);
571 void Debugger::continueProgram()
576 m_pauseOnNextStatement
= false;
577 notifyDoneProcessingDebuggerEvents();
580 void Debugger::stepIntoStatement()
585 m_pauseOnNextStatement
= true;
586 setSteppingMode(SteppingModeEnabled
);
587 notifyDoneProcessingDebuggerEvents();
590 void Debugger::stepOverStatement()
595 m_pauseOnCallFrame
= m_currentCallFrame
;
596 notifyDoneProcessingDebuggerEvents();
599 void Debugger::stepOutOfFunction()
604 m_pauseOnCallFrame
= m_currentCallFrame
? m_currentCallFrame
->callerFrameSkippingVMEntrySentinel() : 0;
605 notifyDoneProcessingDebuggerEvents();
608 void Debugger::updateCallFrame(CallFrame
* callFrame
)
610 m_currentCallFrame
= callFrame
;
611 SourceID sourceID
= DebuggerCallFrame::sourceIDForCallFrame(callFrame
);
612 if (m_lastExecutedSourceID
!= sourceID
) {
613 m_lastExecutedLine
= UINT_MAX
;
614 m_lastExecutedSourceID
= sourceID
;
618 void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame
* callFrame
)
620 updateCallFrame(callFrame
);
621 pauseIfNeeded(callFrame
);
623 m_currentCallFrame
= 0;
626 void Debugger::pauseIfNeeded(CallFrame
* callFrame
)
631 JSGlobalObject
* vmEntryGlobalObject
= callFrame
->vmEntryGlobalObject();
632 if (!needPauseHandling(vmEntryGlobalObject
))
635 Breakpoint breakpoint
;
636 bool didHitBreakpoint
= false;
637 bool pauseNow
= m_pauseOnNextStatement
;
638 pauseNow
|= (m_pauseOnCallFrame
== m_currentCallFrame
);
640 DebuggerCallFrameScope
debuggerCallFrameScope(*this);
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();
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;
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
)
663 handlePause(m_reasonForPause
, vmEntryGlobalObject
);
665 if (!m_pauseOnNextStatement
&& !m_pauseOnCallFrame
) {
666 setSteppingMode(SteppingModeDisabled
);
667 m_currentCallFrame
= nullptr;
671 void Debugger::exception(CallFrame
* callFrame
, JSValue exception
, bool hasHandler
)
676 PauseReasonDeclaration
reason(*this, PausedForException
);
677 if (m_pauseOnExceptionsState
== PauseOnAllExceptions
|| (m_pauseOnExceptionsState
== PauseOnUncaughtExceptions
&& !hasHandler
)) {
678 m_pauseOnNextStatement
= true;
679 setSteppingMode(SteppingModeEnabled
);
682 m_hasHandlerForExceptionCallback
= true;
683 m_currentException
= exception
;
684 updateCallFrameAndPauseIfNeeded(callFrame
);
685 m_currentException
= JSValue();
686 m_hasHandlerForExceptionCallback
= false;
689 void Debugger::atStatement(CallFrame
* callFrame
)
694 PauseReasonDeclaration
reason(*this, PausedAtStatement
);
695 updateCallFrameAndPauseIfNeeded(callFrame
);
698 void Debugger::callEvent(CallFrame
* callFrame
)
703 PauseReasonDeclaration
reason(*this, PausedAfterCall
);
704 updateCallFrameAndPauseIfNeeded(callFrame
);
707 void Debugger::returnEvent(CallFrame
* callFrame
)
712 PauseReasonDeclaration
reason(*this, PausedBeforeReturn
);
713 updateCallFrameAndPauseIfNeeded(callFrame
);
715 // detach may have been called during pauseIfNeeded
716 if (!m_currentCallFrame
)
719 // Treat stepping over a return statement like stepping out.
720 if (m_currentCallFrame
== m_pauseOnCallFrame
)
721 m_pauseOnCallFrame
= m_currentCallFrame
->callerFrameSkippingVMEntrySentinel();
723 m_currentCallFrame
= m_currentCallFrame
->callerFrameSkippingVMEntrySentinel();
726 void Debugger::willExecuteProgram(CallFrame
* callFrame
)
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
);
741 void Debugger::didExecuteProgram(CallFrame
* callFrame
)
746 PauseReasonDeclaration
reason(*this, PausedAtEndOfProgram
);
747 updateCallFrameAndPauseIfNeeded(callFrame
);
749 // Treat stepping over the end of a program like stepping out.
750 if (!m_currentCallFrame
)
752 if (m_currentCallFrame
== m_pauseOnCallFrame
) {
753 m_pauseOnCallFrame
= m_currentCallFrame
->callerFrameSkippingVMEntrySentinel();
754 if (!m_currentCallFrame
)
757 m_currentCallFrame
= m_currentCallFrame
->callerFrameSkippingVMEntrySentinel();
760 void Debugger::didReachBreakpoint(CallFrame
* callFrame
)
765 PauseReasonDeclaration
reason(*this, PausedForBreakpoint
);
766 m_pauseOnNextStatement
= true;
767 setSteppingMode(SteppingModeEnabled
);
768 updateCallFrameAndPauseIfNeeded(callFrame
);
771 DebuggerCallFrame
* Debugger::currentDebuggerCallFrame() const
773 ASSERT(m_currentDebuggerCallFrame
);
774 return m_currentDebuggerCallFrame
.get();