]> git.saurik.com Git - apple/javascriptcore.git/blob - debugger/Debugger.cpp
168d7a3f43793a247fa98fbe3da21947013ca7f0
[apple/javascriptcore.git] / debugger / Debugger.cpp
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
39 namespace {
40
41 using namespace JSC;
42
43 class Recompiler : public MarkedBlock::VoidFunctor {
44 public:
45 Recompiler(JSC::Debugger*);
46 ~Recompiler();
47 void operator()(JSCell*);
48
49 private:
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
58 inline Recompiler::Recompiler(JSC::Debugger* debugger)
59 : m_debugger(debugger)
60 {
61 }
62
63 inline 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
72 inline 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
97 namespace JSC {
98
99 class DebuggerCallFrameScope {
100 public:
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
117 private:
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.
123 class TemporaryPausedState {
124 public:
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
137 private:
138 Debugger& m_debugger;
139 };
140
141 template<typename Functor>
142 void Debugger::forEachCodeBlock(Functor& functor)
143 {
144 m_vm->waitForCompilationsToComplete();
145 m_vm->heap.forEachCodeBlock(functor);
146 }
147
148 Debugger::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
166 Debugger::~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
173 void 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
184 void 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
209 class Debugger::SetSteppingModeFunctor {
210 public:
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
228 private:
229 Debugger* m_debugger;
230 SteppingMode m_mode;
231 };
232
233 void 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
245 void 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
256 void 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
294 void 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
303 class Debugger::ToggleBreakpointFunctor {
304 public:
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
319 private:
320 Debugger* m_debugger;
321 Breakpoint& m_breakpoint;
322 BreakpointState m_enabledOrNot;
323 };
324
325 void 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
333 void 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
349 BreakpointID 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
389 void 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
427 bool 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
487 class Debugger::ClearCodeBlockDebuggerRequestsFunctor {
488 public:
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
501 private:
502 Debugger* m_debugger;
503 };
504
505 void 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
517 class Debugger::ClearDebuggerRequestsFunctor {
518 public:
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
531 private:
532 JSGlobalObject* m_globalObject;
533 };
534
535 void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
536 {
537 ASSERT(m_vm);
538 ClearDebuggerRequestsFunctor functor(globalObject);
539 forEachCodeBlock(functor);
540 }
541
542 void Debugger::setBreakpointsActivated(bool activated)
543 {
544 m_breakpointsActivated = activated;
545 }
546
547 void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
548 {
549 m_pauseOnExceptionsState = pause;
550 }
551
552 void Debugger::setPauseOnNextStatement(bool pause)
553 {
554 m_pauseOnNextStatement = pause;
555 if (pause)
556 setSteppingMode(SteppingModeEnabled);
557 }
558
559 void 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
571 void Debugger::continueProgram()
572 {
573 if (!m_isPaused)
574 return;
575
576 m_pauseOnNextStatement = false;
577 notifyDoneProcessingDebuggerEvents();
578 }
579
580 void Debugger::stepIntoStatement()
581 {
582 if (!m_isPaused)
583 return;
584
585 m_pauseOnNextStatement = true;
586 setSteppingMode(SteppingModeEnabled);
587 notifyDoneProcessingDebuggerEvents();
588 }
589
590 void Debugger::stepOverStatement()
591 {
592 if (!m_isPaused)
593 return;
594
595 m_pauseOnCallFrame = m_currentCallFrame;
596 notifyDoneProcessingDebuggerEvents();
597 }
598
599 void Debugger::stepOutOfFunction()
600 {
601 if (!m_isPaused)
602 return;
603
604 m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrameSkippingVMEntrySentinel() : 0;
605 notifyDoneProcessingDebuggerEvents();
606 }
607
608 void 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
618 void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame)
619 {
620 updateCallFrame(callFrame);
621 pauseIfNeeded(callFrame);
622 if (!isStepping())
623 m_currentCallFrame = 0;
624 }
625
626 void 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
671 void 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
689 void Debugger::atStatement(CallFrame* callFrame)
690 {
691 if (m_isPaused)
692 return;
693
694 PauseReasonDeclaration reason(*this, PausedAtStatement);
695 updateCallFrameAndPauseIfNeeded(callFrame);
696 }
697
698 void Debugger::callEvent(CallFrame* callFrame)
699 {
700 if (m_isPaused)
701 return;
702
703 PauseReasonDeclaration reason(*this, PausedAfterCall);
704 updateCallFrameAndPauseIfNeeded(callFrame);
705 }
706
707 void 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
726 void 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
741 void 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
760 void 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
771 DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
772 {
773 ASSERT(m_currentDebuggerCallFrame);
774 return m_currentDebuggerCallFrame.get();
775 }
776
777 } // namespace JSC