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#include "HeapIterationScope.h"
29#include "Interpreter.h"
30#include "JSCJSValueInlines.h"
31#include "JSFunction.h"
32#include "JSGlobalObject.h"
33#include "JSCInlines.h"
34#include "Parser.h"
35#include "Protect.h"
36#include "VMEntryScope.h"
37
38namespace {
39
40using namespace JSC;
41
42struct GatherSourceProviders : public MarkedBlock::VoidFunctor {
43 HashSet<SourceProvider*> sourceProviders;
44 JSGlobalObject* m_globalObject;
45
46 GatherSourceProviders(JSGlobalObject* globalObject)
47 : m_globalObject(globalObject) { }
48
49 IterationStatus operator()(JSCell* cell)
50 {
51 JSFunction* function = jsDynamicCast<JSFunction*>(cell);
52 if (!function)
53 return IterationStatus::Continue;
54
55 if (function->scope()->globalObject() != m_globalObject)
56 return IterationStatus::Continue;
57
58 if (!function->executable()->isFunctionExecutable())
59 return IterationStatus::Continue;
60
61 if (function->isHostOrBuiltinFunction())
62 return IterationStatus::Continue;
63
64 sourceProviders.add(
65 jsCast<FunctionExecutable*>(function->executable())->source().provider());
66 return IterationStatus::Continue;
67 }
68};
69
70} // namespace
71
72namespace JSC {
73
74class DebuggerPausedScope {
75public:
76 DebuggerPausedScope(Debugger& debugger)
77 : m_debugger(debugger)
78 {
79 ASSERT(!m_debugger.m_currentDebuggerCallFrame);
80 if (m_debugger.m_currentCallFrame)
81 m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame);
82 }
83
84 ~DebuggerPausedScope()
85 {
86 if (m_debugger.m_currentDebuggerCallFrame) {
87 m_debugger.m_currentDebuggerCallFrame->invalidate();
88 m_debugger.m_currentDebuggerCallFrame = nullptr;
89 }
90 }
91
92private:
93 Debugger& m_debugger;
94};
95
96// This is very similar to TemporaryChange<bool>, but that cannot be used
97// as the m_isPaused field uses only one bit.
98class TemporaryPausedState {
99public:
100 TemporaryPausedState(Debugger& debugger)
101 : m_debugger(debugger)
102 {
103 ASSERT(!m_debugger.m_isPaused);
104 m_debugger.m_isPaused = true;
105 }
106
107 ~TemporaryPausedState()
108 {
109 m_debugger.m_isPaused = false;
110 }
111
112private:
113 Debugger& m_debugger;
114};
115
116Debugger::Debugger(VM& vm)
117 : m_vm(vm)
118 , m_pauseOnExceptionsState(DontPauseOnExceptions)
119 , m_pauseOnNextStatement(false)
120 , m_isPaused(false)
121 , m_breakpointsActivated(true)
122 , m_hasHandlerForExceptionCallback(false)
123 , m_suppressAllPauses(false)
124 , m_steppingMode(SteppingModeDisabled)
125 , m_reasonForPause(NotPaused)
126 , m_pauseOnCallFrame(0)
127 , m_currentCallFrame(0)
128 , m_lastExecutedLine(UINT_MAX)
129 , m_lastExecutedSourceID(noSourceID)
130 , m_topBreakpointID(noBreakpointID)
131 , m_pausingBreakpointID(noBreakpointID)
132{
133}
134
135Debugger::~Debugger()
136{
137 HashSet<JSGlobalObject*>::iterator end = m_globalObjects.end();
138 for (HashSet<JSGlobalObject*>::iterator it = m_globalObjects.begin(); it != end; ++it)
139 (*it)->setDebugger(0);
140}
141
142void Debugger::attach(JSGlobalObject* globalObject)
143{
144 ASSERT(!globalObject->debugger());
145 globalObject->setDebugger(this);
146 m_globalObjects.add(globalObject);
147
148 m_vm.setShouldBuildPCToCodeOriginMapping();
149
150 // Call sourceParsed because it will execute JavaScript in the inspector.
151 GatherSourceProviders gatherSourceProviders(globalObject);
152 {
153 HeapIterationScope iterationScope(m_vm.heap);
154 m_vm.heap.objectSpace().forEachLiveCell(iterationScope, gatherSourceProviders);
155 }
156 for (auto* sourceProvider : gatherSourceProviders.sourceProviders)
157 sourceParsed(globalObject->globalExec(), sourceProvider, -1, String());
158}
159
160void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason)
161{
162 // If we're detaching from the currently executing global object, manually tear down our
163 // stack, since we won't get further debugger callbacks to do so. Also, resume execution,
164 // since there's no point in staying paused once a window closes.
165 if (m_currentCallFrame && m_currentCallFrame->vmEntryGlobalObject() == globalObject) {
166 m_currentCallFrame = 0;
167 m_pauseOnCallFrame = 0;
168 continueProgram();
169 }
170
171 ASSERT(m_globalObjects.contains(globalObject));
172 m_globalObjects.remove(globalObject);
173
174 // If the globalObject is destructing, then its CodeBlocks will also be
175 // destructed. There is no need to do the debugger requests clean up, and
176 // it is not safe to access those CodeBlocks at this time anyway.
177 if (reason != GlobalObjectIsDestructing)
178 clearDebuggerRequests(globalObject);
179
180 globalObject->setDebugger(0);
181}
182
183bool Debugger::isAttached(JSGlobalObject* globalObject)
184{
185 return globalObject->debugger() == this;
186}
187
188class Debugger::SetSteppingModeFunctor {
189public:
190 SetSteppingModeFunctor(Debugger* debugger, SteppingMode mode)
191 : m_debugger(debugger)
192 , m_mode(mode)
193 {
194 }
195
196 bool operator()(CodeBlock* codeBlock)
197 {
198 if (m_debugger == codeBlock->globalObject()->debugger()) {
199 if (m_mode == SteppingModeEnabled)
200 codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
201 else
202 codeBlock->setSteppingMode(CodeBlock::SteppingModeDisabled);
203 }
204 return false;
205 }
206
207private:
208 Debugger* m_debugger;
209 SteppingMode m_mode;
210};
211
212void Debugger::setSteppingMode(SteppingMode mode)
213{
214 if (mode == m_steppingMode)
215 return;
216
217 m_vm.heap.completeAllDFGPlans();
218
219 m_steppingMode = mode;
220 SetSteppingModeFunctor functor(this, mode);
221 m_vm.heap.forEachCodeBlock(functor);
222}
223
224void Debugger::registerCodeBlock(CodeBlock* codeBlock)
225{
226 applyBreakpoints(codeBlock);
227 if (isStepping())
228 codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
229}
230
231void Debugger::setProfilingClient(ProfilingClient* client)
232{
233 ASSERT(!!m_profilingClient != !!client);
234 m_profilingClient = client;
235
236 recompileAllJSFunctions();
237}
238
239double Debugger::willEvaluateScript()
240{
241 return m_profilingClient->willEvaluateScript();
242}
243
244void Debugger::didEvaluateScript(double startTime, ProfilingReason reason)
245{
246 m_profilingClient->didEvaluateScript(startTime, reason);
247}
248
249void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot)
250{
251 ScriptExecutable* executable = codeBlock->ownerScriptExecutable();
252
253 SourceID sourceID = static_cast<SourceID>(executable->sourceID());
254 if (breakpoint.sourceID != sourceID)
255 return;
256
257 unsigned line = breakpoint.line;
258 unsigned column = breakpoint.column;
259
260 unsigned startLine = executable->firstLine();
261 unsigned startColumn = executable->startColumn();
262 unsigned endLine = executable->lastLine();
263 unsigned endColumn = executable->endColumn();
264
265 // Inspector breakpoint line and column values are zero-based but the executable
266 // and CodeBlock line and column values are one-based.
267 line += 1;
268 column = column ? column + 1 : Breakpoint::unspecifiedColumn;
269
270 if (line < startLine || line > endLine)
271 return;
272 if (column != Breakpoint::unspecifiedColumn) {
273 if (line == startLine && column < startColumn)
274 return;
275 if (line == endLine && column > endColumn)
276 return;
277 }
278 if (!codeBlock->hasOpDebugForLineAndColumn(line, column))
279 return;
280
281 if (enabledOrNot == BreakpointEnabled)
282 codeBlock->addBreakpoint(1);
283 else
284 codeBlock->removeBreakpoint(1);
285}
286
287void Debugger::applyBreakpoints(CodeBlock* codeBlock)
288{
289 BreakpointIDToBreakpointMap& breakpoints = m_breakpointIDToBreakpoint;
290 for (auto it = breakpoints.begin(); it != breakpoints.end(); ++it) {
291 Breakpoint& breakpoint = *it->value;
292 toggleBreakpoint(codeBlock, breakpoint, BreakpointEnabled);
293 }
294}
295
296class Debugger::ToggleBreakpointFunctor {
297public:
298 ToggleBreakpointFunctor(Debugger* debugger, Breakpoint& breakpoint, BreakpointState enabledOrNot)
299 : m_debugger(debugger)
300 , m_breakpoint(breakpoint)
301 , m_enabledOrNot(enabledOrNot)
302 {
303 }
304
305 bool operator()(CodeBlock* codeBlock)
306 {
307 if (m_debugger == codeBlock->globalObject()->debugger())
308 m_debugger->toggleBreakpoint(codeBlock, m_breakpoint, m_enabledOrNot);
309 return false;
310 }
311
312private:
313 Debugger* m_debugger;
314 Breakpoint& m_breakpoint;
315 BreakpointState m_enabledOrNot;
316};
317
318void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot)
319{
320 m_vm.heap.completeAllDFGPlans();
321
322 ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot);
323 m_vm.heap.forEachCodeBlock(functor);
324}
325
326void Debugger::recompileAllJSFunctions()
327{
328 m_vm.deleteAllCode();
329}
330
331BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn)
332{
333 SourceID sourceID = breakpoint.sourceID;
334 unsigned line = breakpoint.line;
335 unsigned column = breakpoint.column;
336
337 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
338 if (it == m_sourceIDToBreakpoints.end())
339 it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator;
340 LineToBreakpointsMap::iterator breaksIt = it->value.find(line);
341 if (breaksIt == it->value.end())
342 breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator;
343
344 BreakpointsList& breakpoints = *breaksIt->value;
345 for (Breakpoint* current = breakpoints.head(); current; current = current->next()) {
346 if (current->column == column) {
347 // The breakpoint already exists. We're not allowed to create a new
348 // breakpoint at this location. Rather than returning the breakpointID
349 // of the pre-existing breakpoint, we need to return noBreakpointID
350 // to indicate that we're not creating a new one.
351 return noBreakpointID;
352 }
353 }
354
355 BreakpointID id = ++m_topBreakpointID;
356 RELEASE_ASSERT(id != noBreakpointID);
357
358 breakpoint.id = id;
359 actualLine = line;
360 actualColumn = column;
361
362 Breakpoint* newBreakpoint = new Breakpoint(breakpoint);
363 breakpoints.append(newBreakpoint);
364 m_breakpointIDToBreakpoint.set(id, newBreakpoint);
365
366 toggleBreakpoint(breakpoint, BreakpointEnabled);
367
368 return id;
369}
370
371void Debugger::removeBreakpoint(BreakpointID id)
372{
373 ASSERT(id != noBreakpointID);
374
375 BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id);
376 ASSERT(idIt != m_breakpointIDToBreakpoint.end());
377 Breakpoint* breakpoint = idIt->value;
378
379 SourceID sourceID = breakpoint->sourceID;
380 ASSERT(sourceID);
381 SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
382 ASSERT(it != m_sourceIDToBreakpoints.end());
383 LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint->line);
384 ASSERT(breaksIt != it->value.end());
385
386 toggleBreakpoint(*breakpoint, BreakpointDisabled);
387
388 BreakpointsList& breakpoints = *breaksIt->value;
389#if !ASSERT_DISABLED
390 bool found = false;
391 for (Breakpoint* current = breakpoints.head(); current && !found; current = current->next()) {
392 if (current->id == breakpoint->id)
393 found = true;
394 }
395 ASSERT(found);
396#endif
397
398 m_breakpointIDToBreakpoint.remove(idIt);
399 breakpoints.remove(breakpoint);
400 delete breakpoint;
401
402 if (breakpoints.isEmpty()) {
403 it->value.remove(breaksIt);
404 if (it->value.isEmpty())
405 m_sourceIDToBreakpoints.remove(it);
406 }
407}
408
409bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint)
410{
411 if (!m_breakpointsActivated)
412 return false;
413
414 SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID);
415 if (it == m_sourceIDToBreakpoints.end())
416 return false;
417
418 unsigned line = position.m_line.zeroBasedInt();
419 unsigned column = position.m_column.zeroBasedInt();
420
421 LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line);
422 if (breaksIt == it->value.end())
423 return false;
424
425 bool hit = false;
426 const BreakpointsList& breakpoints = *breaksIt->value;
427 Breakpoint* breakpoint;
428 for (breakpoint = breakpoints.head(); breakpoint; breakpoint = breakpoint->next()) {
429 unsigned breakLine = breakpoint->line;
430 unsigned breakColumn = breakpoint->column;
431 // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0).
432 ASSERT(this == m_currentCallFrame->codeBlock()->globalObject()->debugger());
433 if ((line != m_lastExecutedLine && line == breakLine && !breakColumn)
434 || (line == breakLine && column == breakColumn)) {
435 hit = true;
436 break;
437 }
438 }
439 if (!hit)
440 return false;
441
442 if (hitBreakpoint)
443 *hitBreakpoint = *breakpoint;
444
445 breakpoint->hitCount++;
446 if (breakpoint->ignoreCount >= breakpoint->hitCount)
447 return false;
448
449 if (breakpoint->condition.isEmpty())
450 return true;
451
452 // We cannot stop in the debugger while executing condition code,
453 // so make it looks like the debugger is already paused.
454 TemporaryPausedState pausedState(*this);
455
456 NakedPtr<Exception> exception;
457 DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
458 JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception);
459
460 // We can lose the debugger while executing JavaScript.
461 if (!m_currentCallFrame)
462 return false;
463
464 if (exception) {
465 // An erroneous condition counts as "false".
466 handleExceptionInBreakpointCondition(m_currentCallFrame, exception);
467 return false;
468 }
469
470 return result.toBoolean(m_currentCallFrame);
471}
472
473class Debugger::ClearCodeBlockDebuggerRequestsFunctor {
474public:
475 ClearCodeBlockDebuggerRequestsFunctor(Debugger* debugger)
476 : m_debugger(debugger)
477 {
478 }
479
480 bool operator()(CodeBlock* codeBlock)
481 {
482 if (codeBlock->hasDebuggerRequests() && m_debugger == codeBlock->globalObject()->debugger())
483 codeBlock->clearDebuggerRequests();
484 return false;
485 }
486
487private:
488 Debugger* m_debugger;
489};
490
491void Debugger::clearBreakpoints()
492{
493 m_vm.heap.completeAllDFGPlans();
494
495 m_topBreakpointID = noBreakpointID;
496 m_breakpointIDToBreakpoint.clear();
497 m_sourceIDToBreakpoints.clear();
498
499 ClearCodeBlockDebuggerRequestsFunctor functor(this);
500 m_vm.heap.forEachCodeBlock(functor);
501}
502
503class Debugger::ClearDebuggerRequestsFunctor {
504public:
505 ClearDebuggerRequestsFunctor(JSGlobalObject* globalObject)
506 : m_globalObject(globalObject)
507 {
508 }
509
510 bool operator()(CodeBlock* codeBlock)
511 {
512 if (codeBlock->hasDebuggerRequests() && m_globalObject == codeBlock->globalObject())
513 codeBlock->clearDebuggerRequests();
514 return false;
515 }
516
517private:
518 JSGlobalObject* m_globalObject;
519};
520
521void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
522{
523 m_vm.heap.completeAllDFGPlans();
524
525 ClearDebuggerRequestsFunctor functor(globalObject);
526 m_vm.heap.forEachCodeBlock(functor);
527}
528
529void Debugger::setBreakpointsActivated(bool activated)
530{
531 m_breakpointsActivated = activated;
532}
533
534void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
535{
536 m_pauseOnExceptionsState = pause;
537}
538
539void Debugger::setPauseOnNextStatement(bool pause)
540{
541 m_pauseOnNextStatement = pause;
542 if (pause)
543 setSteppingMode(SteppingModeEnabled);
544}
545
546void Debugger::breakProgram()
547{
548 if (m_isPaused)
549 return;
550
551 if (!m_vm.topCallFrame)
552 return;
553
554 m_pauseOnNextStatement = true;
555 setSteppingMode(SteppingModeEnabled);
556 m_currentCallFrame = m_vm.topCallFrame;
557 pauseIfNeeded(m_currentCallFrame);
558}
559
560void Debugger::continueProgram()
561{
562 if (!m_isPaused)
563 return;
564
565 m_pauseOnNextStatement = false;
566 notifyDoneProcessingDebuggerEvents();
567}
568
569void Debugger::stepIntoStatement()
570{
571 if (!m_isPaused)
572 return;
573
574 m_pauseOnNextStatement = true;
575 setSteppingMode(SteppingModeEnabled);
576 notifyDoneProcessingDebuggerEvents();
577}
578
579void Debugger::stepOverStatement()
580{
581 if (!m_isPaused)
582 return;
583
584 m_pauseOnCallFrame = m_currentCallFrame;
585 notifyDoneProcessingDebuggerEvents();
586}
587
588void Debugger::stepOutOfFunction()
589{
590 if (!m_isPaused)
591 return;
592
593 VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
594 m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrame(topVMEntryFrame) : 0;
595 notifyDoneProcessingDebuggerEvents();
596}
597
598void Debugger::updateCallFrame(CallFrame* callFrame)
599{
600 m_currentCallFrame = callFrame;
601 SourceID sourceID = DebuggerCallFrame::sourceIDForCallFrame(callFrame);
602 if (m_lastExecutedSourceID != sourceID) {
603 m_lastExecutedLine = UINT_MAX;
604 m_lastExecutedSourceID = sourceID;
605 }
606}
607
608void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame)
609{
610 updateCallFrame(callFrame);
611 pauseIfNeeded(callFrame);
612 if (!isStepping())
613 m_currentCallFrame = 0;
614}
615
616void Debugger::pauseIfNeeded(CallFrame* callFrame)
617{
618 if (m_isPaused)
619 return;
620
621 if (m_suppressAllPauses)
622 return;
623
624 JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject();
625 if (!needPauseHandling(vmEntryGlobalObject))
626 return;
627
628 Breakpoint breakpoint;
629 bool didHitBreakpoint = false;
630 bool pauseNow = m_pauseOnNextStatement;
631 pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame);
632
633 DebuggerPausedScope debuggerPausedScope(*this);
634
635 intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame);
636 TextPosition position = DebuggerCallFrame::positionForCallFrame(m_currentCallFrame);
637 pauseNow |= didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint);
638 m_lastExecutedLine = position.m_line.zeroBasedInt();
639 if (!pauseNow)
640 return;
641
642 // Make sure we are not going to pause again on breakpoint actions by
643 // reseting the pause state before executing any breakpoint actions.
644 TemporaryPausedState pausedState(*this);
645 m_pauseOnCallFrame = 0;
646 m_pauseOnNextStatement = false;
647
648 if (didHitBreakpoint) {
649 handleBreakpointHit(vmEntryGlobalObject, breakpoint);
650 // Note that the actions can potentially stop the debugger, so we need to check that
651 // we still have a current call frame when we get back.
652 if (breakpoint.autoContinue || !m_currentCallFrame)
653 return;
654 m_pausingBreakpointID = breakpoint.id;
655 }
656
657 {
658 PauseReasonDeclaration reason(*this, didHitBreakpoint ? PausedForBreakpoint : m_reasonForPause);
659 handlePause(vmEntryGlobalObject, m_reasonForPause);
660 RELEASE_ASSERT(!callFrame->hadException());
661 }
662
663 m_pausingBreakpointID = noBreakpointID;
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 hasCatchHandler)
672{
673 if (m_isPaused)
674 return;
675
676 PauseReasonDeclaration reason(*this, PausedForException);
677 if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasCatchHandler)) {
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 VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
722 m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
723 }
724
725 VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
726 m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
727}
728
729void Debugger::willExecuteProgram(CallFrame* callFrame)
730{
731 if (m_isPaused)
732 return;
733
734 PauseReasonDeclaration reason(*this, PausedAtStartOfProgram);
735 updateCallFrameAndPauseIfNeeded(callFrame);
736}
737
738void Debugger::didExecuteProgram(CallFrame* callFrame)
739{
740 if (m_isPaused)
741 return;
742
743 PauseReasonDeclaration reason(*this, PausedAtEndOfProgram);
744 updateCallFrameAndPauseIfNeeded(callFrame);
745
746 // Treat stepping over the end of a program like stepping out.
747 if (!m_currentCallFrame)
748 return;
749 if (m_currentCallFrame == m_pauseOnCallFrame) {
750 VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
751 m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
752 if (!m_currentCallFrame)
753 return;
754 }
755 VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
756 m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
757}
758
759void Debugger::didReachBreakpoint(CallFrame* callFrame)
760{
761 if (m_isPaused)
762 return;
763
764 PauseReasonDeclaration reason(*this, PausedForDebuggerStatement);
765 m_pauseOnNextStatement = true;
766 setSteppingMode(SteppingModeEnabled);
767 updateCallFrameAndPauseIfNeeded(callFrame);
768}
769
770DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
771{
772 ASSERT(m_currentDebuggerCallFrame);
773 return m_currentDebuggerCallFrame.get();
774}
775
776} // namespace JSC
777