]> git.saurik.com Git - apple/javascriptcore.git/blame - debugger/Debugger.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / debugger / Debugger.cpp
CommitLineData
9dae56ea 1/*
81345200 2 * Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved.
9dae56ea
A
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
81345200
A
25#include "CodeBlock.h"
26#include "DebuggerCallFrame.h"
f9bf01c6 27#include "Error.h"
81345200 28#include "HeapIterationScope.h"
9dae56ea 29#include "Interpreter.h"
81345200 30#include "JSCJSValueInlines.h"
f9bf01c6
A
31#include "JSFunction.h"
32#include "JSGlobalObject.h"
81345200 33#include "JSCInlines.h"
9dae56ea 34#include "Parser.h"
f9bf01c6 35#include "Protect.h"
81345200 36#include "VMEntryScope.h"
9dae56ea 37
14957cd0
A
38namespace {
39
40using namespace JSC;
41
6fe7ccc8 42class Recompiler : public MarkedBlock::VoidFunctor {
14957cd0 43public:
6fe7ccc8 44 Recompiler(JSC::Debugger*);
14957cd0 45 ~Recompiler();
ed1e77d3 46 IterationStatus operator()(JSCell*);
14957cd0
A
47
48private:
49 typedef HashSet<FunctionExecutable*> FunctionExecutableSet;
50 typedef HashMap<SourceProvider*, ExecState*> SourceProviderMap;
51
ed1e77d3
A
52 void visit(JSCell*);
53
6fe7ccc8 54 JSC::Debugger* m_debugger;
14957cd0
A
55 FunctionExecutableSet m_functionExecutables;
56 SourceProviderMap m_sourceProviders;
57};
58
6fe7ccc8 59inline Recompiler::Recompiler(JSC::Debugger* debugger)
14957cd0
A
60 : m_debugger(debugger)
61{
62}
63
64inline Recompiler::~Recompiler()
65{
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)
93a37866 70 m_debugger->sourceParsed(iter->value, iter->key, -1, String());
14957cd0
A
71}
72
ed1e77d3 73inline void Recompiler::visit(JSCell* cell)
14957cd0 74{
81345200 75 if (!cell->inherits(JSFunction::info()))
14957cd0
A
76 return;
77
6fe7ccc8 78 JSFunction* function = jsCast<JSFunction*>(cell);
14957cd0
A
79 if (function->executable()->isHostFunction())
80 return;
81
82 FunctionExecutable* executable = function->jsExecutable();
83
84 // Check if the function is already in the set - if so,
85 // we've already retranslated it, nothing to do here.
6fe7ccc8 86 if (!m_functionExecutables.add(executable).isNewEntry)
14957cd0
A
87 return;
88
93a37866 89 ExecState* exec = function->scope()->globalObject()->JSGlobalObject::globalExec();
ed1e77d3
A
90 executable->clearCode();
91 executable->clearUnlinkedCodeForRecompilation();
93a37866 92 if (m_debugger == function->scope()->globalObject()->debugger())
14957cd0
A
93 m_sourceProviders.add(executable->source().provider(), exec);
94}
95
ed1e77d3
A
96inline IterationStatus Recompiler::operator()(JSCell* cell)
97{
98 visit(cell);
99 return IterationStatus::Continue;
100}
101
14957cd0
A
102} // namespace
103
9dae56ea
A
104namespace JSC {
105
ed1e77d3 106class DebuggerPausedScope {
81345200 107public:
ed1e77d3 108 DebuggerPausedScope(Debugger& debugger)
81345200
A
109 : m_debugger(debugger)
110 {
111 ASSERT(!m_debugger.m_currentDebuggerCallFrame);
112 if (m_debugger.m_currentCallFrame)
113 m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame);
114 }
115
ed1e77d3 116 ~DebuggerPausedScope()
81345200
A
117 {
118 if (m_debugger.m_currentDebuggerCallFrame) {
119 m_debugger.m_currentDebuggerCallFrame->invalidate();
ed1e77d3 120 m_debugger.m_currentDebuggerCallFrame = nullptr;
81345200
A
121 }
122 }
123
124private:
125 Debugger& m_debugger;
126};
127
128// This is very similar to TemporaryChange<bool>, but that cannot be used
129// as the m_isPaused field uses only one bit.
130class TemporaryPausedState {
131public:
132 TemporaryPausedState(Debugger& debugger)
133 : m_debugger(debugger)
134 {
135 ASSERT(!m_debugger.m_isPaused);
136 m_debugger.m_isPaused = true;
137 }
138
139 ~TemporaryPausedState()
140 {
141 m_debugger.m_isPaused = false;
142 }
143
144private:
145 Debugger& m_debugger;
146};
147
148template<typename Functor>
149void Debugger::forEachCodeBlock(Functor& functor)
150{
ed1e77d3 151 m_vm->prepareToDiscardCode();
81345200
A
152 m_vm->heap.forEachCodeBlock(functor);
153}
154
155Debugger::Debugger(bool isInWorkerThread)
156 : m_vm(nullptr)
157 , m_pauseOnExceptionsState(DontPauseOnExceptions)
158 , m_pauseOnNextStatement(false)
159 , m_isPaused(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)
ed1e77d3 170 , m_pausingBreakpointID(noBreakpointID)
81345200
A
171{
172}
173
9dae56ea
A
174Debugger::~Debugger()
175{
176 HashSet<JSGlobalObject*>::iterator end = m_globalObjects.end();
177 for (HashSet<JSGlobalObject*>::iterator it = m_globalObjects.begin(); it != end; ++it)
178 (*it)->setDebugger(0);
179}
180
181void Debugger::attach(JSGlobalObject* globalObject)
182{
183 ASSERT(!globalObject->debugger());
81345200
A
184 if (!m_vm)
185 m_vm = &globalObject->vm();
186 else
187 ASSERT(m_vm == &globalObject->vm());
9dae56ea
A
188 globalObject->setDebugger(this);
189 m_globalObjects.add(globalObject);
190}
191
81345200 192void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason)
9dae56ea 193{
81345200
A
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;
200 continueProgram();
201 }
202
9dae56ea
A
203 ASSERT(m_globalObjects.contains(globalObject));
204 m_globalObjects.remove(globalObject);
81345200
A
205
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);
211
9dae56ea 212 globalObject->setDebugger(0);
81345200
A
213 if (!m_globalObjects.size())
214 m_vm = nullptr;
215}
216
ed1e77d3
A
217bool Debugger::isAttached(JSGlobalObject* globalObject)
218{
219 return globalObject->debugger() == this;
220}
221
81345200
A
222class Debugger::SetSteppingModeFunctor {
223public:
224 SetSteppingModeFunctor(Debugger* debugger, SteppingMode mode)
225 : m_debugger(debugger)
226 , m_mode(mode)
227 {
228 }
229
230 bool operator()(CodeBlock* codeBlock)
231 {
232 if (m_debugger == codeBlock->globalObject()->debugger()) {
233 if (m_mode == SteppingModeEnabled)
234 codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
235 else
236 codeBlock->setSteppingMode(CodeBlock::SteppingModeDisabled);
237 }
238 return false;
239 }
240
241private:
242 Debugger* m_debugger;
243 SteppingMode m_mode;
244};
245
246void Debugger::setSteppingMode(SteppingMode mode)
247{
248 if (mode == m_steppingMode || !m_vm)
249 return;
250
ed1e77d3 251 m_vm->prepareToDiscardCode();
81345200
A
252
253 m_steppingMode = mode;
254 SetSteppingModeFunctor functor(this, mode);
255 m_vm->heap.forEachCodeBlock(functor);
256}
257
258void Debugger::registerCodeBlock(CodeBlock* codeBlock)
259{
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);
265 if (isStepping())
266 codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
267}
268
269void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot)
270{
271 ScriptExecutable* executable = codeBlock->ownerExecutable();
272
273 SourceID sourceID = static_cast<SourceID>(executable->sourceID());
274 if (breakpoint.sourceID != sourceID)
275 return;
276
277 unsigned line = breakpoint.line;
278 unsigned column = breakpoint.column;
279
ed1e77d3 280 unsigned startLine = executable->firstLine();
81345200
A
281 unsigned startColumn = executable->startColumn();
282 unsigned endLine = executable->lastLine();
283 unsigned endColumn = executable->endColumn();
284
285 // Inspector breakpoint line and column values are zero-based but the executable
286 // and CodeBlock line and column values are one-based.
287 line += 1;
288 column = column ? column + 1 : Breakpoint::unspecifiedColumn;
289
290 if (line < startLine || line > endLine)
291 return;
292 if (column != Breakpoint::unspecifiedColumn) {
293 if (line == startLine && column < startColumn)
294 return;
295 if (line == endLine && column > endColumn)
296 return;
297 }
298 if (!codeBlock->hasOpDebugForLineAndColumn(line, column))
299 return;
300
301 if (enabledOrNot == BreakpointEnabled)
302 codeBlock->addBreakpoint(1);
303 else
304 codeBlock->removeBreakpoint(1);
305}
306
307void Debugger::applyBreakpoints(CodeBlock* codeBlock)
308{
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);
313 }
314}
315
316class Debugger::ToggleBreakpointFunctor {
317public:
318 ToggleBreakpointFunctor(Debugger* debugger, Breakpoint& breakpoint, BreakpointState enabledOrNot)
319 : m_debugger(debugger)
320 , m_breakpoint(breakpoint)
321 , m_enabledOrNot(enabledOrNot)
322 {
323 }
324
325 bool operator()(CodeBlock* codeBlock)
326 {
327 if (m_debugger == codeBlock->globalObject()->debugger())
328 m_debugger->toggleBreakpoint(codeBlock, m_breakpoint, m_enabledOrNot);
329 return false;
330 }
331
332private:
333 Debugger* m_debugger;
334 Breakpoint& m_breakpoint;
335 BreakpointState m_enabledOrNot;
336};
337
338void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot)
339{
340 if (!m_vm)
341 return;
342 ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot);
343 forEachCodeBlock(functor);
9dae56ea
A
344}
345
93a37866 346void Debugger::recompileAllJSFunctions(VM* vm)
f9bf01c6
A
347{
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.
81345200 350 if (vm->entryScope) {
ed1e77d3
A
351 auto listener = [] (VM& vm, JSGlobalObject* globalObject)
352 {
353 if (Debugger* debugger = globalObject->debugger())
354 debugger->recompileAllJSFunctions(&vm);
355 };
356
357 vm->entryScope->setEntryScopeDidPopListener(this, listener);
f9bf01c6 358 return;
81345200
A
359 }
360
ed1e77d3 361 vm->prepareToDiscardCode();
f9bf01c6 362
14957cd0 363 Recompiler recompiler(this);
81345200
A
364 HeapIterationScope iterationScope(vm->heap);
365 vm->heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
366}
367
368BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn)
369{
370 SourceID sourceID = breakpoint.sourceID;
371 unsigned line = breakpoint.line;
372 unsigned column = breakpoint.column;
373
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;
380
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;
389 }
390 }
391
392 BreakpointID id = ++m_topBreakpointID;
393 RELEASE_ASSERT(id != noBreakpointID);
394
395 breakpoint.id = id;
396 actualLine = line;
397 actualColumn = column;
398
399 Breakpoint* newBreakpoint = new Breakpoint(breakpoint);
400 breakpoints.append(newBreakpoint);
401 m_breakpointIDToBreakpoint.set(id, newBreakpoint);
402
403 toggleBreakpoint(breakpoint, BreakpointEnabled);
404
405 return id;
406}
407
408void Debugger::removeBreakpoint(BreakpointID id)
409{
410 ASSERT(id != noBreakpointID);
411
412 BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id);
413 ASSERT(idIt != m_breakpointIDToBreakpoint.end());
414 Breakpoint* breakpoint = idIt->value;
415
416 SourceID sourceID = breakpoint->sourceID;
417 ASSERT(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());
422
423 toggleBreakpoint(*breakpoint, BreakpointDisabled);
424
425 BreakpointsList& breakpoints = *breaksIt->value;
426#if !ASSERT_DISABLED
427 bool found = false;
428 for (Breakpoint* current = breakpoints.head(); current && !found; current = current->next()) {
429 if (current->id == breakpoint->id)
430 found = true;
431 }
432 ASSERT(found);
433#endif
434
435 m_breakpointIDToBreakpoint.remove(idIt);
436 breakpoints.remove(breakpoint);
437 delete breakpoint;
438
439 if (breakpoints.isEmpty()) {
440 it->value.remove(breaksIt);
441 if (it->value.isEmpty())
442 m_sourceIDToBreakpoints.remove(it);
443 }
444}
445
446bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint)
447{
448 if (!m_breakpointsActivated)
449 return false;
450
451 SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID);
452 if (it == m_sourceIDToBreakpoints.end())
453 return false;
454
455 unsigned line = position.m_line.zeroBasedInt();
456 unsigned column = position.m_column.zeroBasedInt();
457
458 LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line);
459 if (breaksIt == it->value.end())
460 return false;
461
462 bool hit = false;
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)) {
472 hit = true;
473 break;
474 }
475 }
476 if (!hit)
477 return false;
478
479 if (hitBreakpoint)
480 *hitBreakpoint = *breakpoint;
481
482 if (breakpoint->condition.isEmpty())
483 return true;
484
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);
488
ed1e77d3 489 NakedPtr<Exception> exception;
81345200
A
490 DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
491 JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception);
492
493 // We can lose the debugger while executing JavaScript.
494 if (!m_currentCallFrame)
495 return false;
496
497 if (exception) {
498 // An erroneous condition counts as "false".
499 handleExceptionInBreakpointCondition(m_currentCallFrame, exception);
500 return false;
501 }
502
503 return result.toBoolean(m_currentCallFrame);
504}
505
506class Debugger::ClearCodeBlockDebuggerRequestsFunctor {
507public:
508 ClearCodeBlockDebuggerRequestsFunctor(Debugger* debugger)
509 : m_debugger(debugger)
510 {
511 }
512
513 bool operator()(CodeBlock* codeBlock)
514 {
515 if (codeBlock->hasDebuggerRequests() && m_debugger == codeBlock->globalObject()->debugger())
516 codeBlock->clearDebuggerRequests();
517 return false;
518 }
519
520private:
521 Debugger* m_debugger;
522};
523
524void Debugger::clearBreakpoints()
525{
526 m_topBreakpointID = noBreakpointID;
527 m_breakpointIDToBreakpoint.clear();
528 m_sourceIDToBreakpoints.clear();
529
530 if (!m_vm)
531 return;
532 ClearCodeBlockDebuggerRequestsFunctor functor(this);
533 forEachCodeBlock(functor);
f9bf01c6
A
534}
535
81345200
A
536class Debugger::ClearDebuggerRequestsFunctor {
537public:
538 ClearDebuggerRequestsFunctor(JSGlobalObject* globalObject)
539 : m_globalObject(globalObject)
540 {
541 }
542
543 bool operator()(CodeBlock* codeBlock)
544 {
545 if (codeBlock->hasDebuggerRequests() && m_globalObject == codeBlock->globalObject())
546 codeBlock->clearDebuggerRequests();
547 return false;
548 }
549
550private:
551 JSGlobalObject* m_globalObject;
552};
553
554void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
555{
556 ASSERT(m_vm);
557 ClearDebuggerRequestsFunctor functor(globalObject);
558 forEachCodeBlock(functor);
559}
560
561void Debugger::setBreakpointsActivated(bool activated)
9dae56ea 562{
81345200
A
563 m_breakpointsActivated = activated;
564}
565
566void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
567{
568 m_pauseOnExceptionsState = pause;
569}
570
571void Debugger::setPauseOnNextStatement(bool pause)
572{
573 m_pauseOnNextStatement = pause;
574 if (pause)
575 setSteppingMode(SteppingModeEnabled);
576}
9dae56ea 577
81345200
A
578void Debugger::breakProgram()
579{
580 if (m_isPaused)
581 return;
582
583 m_pauseOnNextStatement = true;
584 setSteppingMode(SteppingModeEnabled);
585 m_currentCallFrame = m_vm->topCallFrame;
586 ASSERT(m_currentCallFrame);
587 pauseIfNeeded(m_currentCallFrame);
588}
589
590void Debugger::continueProgram()
591{
592 if (!m_isPaused)
593 return;
594
595 m_pauseOnNextStatement = false;
596 notifyDoneProcessingDebuggerEvents();
597}
598
599void Debugger::stepIntoStatement()
600{
601 if (!m_isPaused)
602 return;
603
604 m_pauseOnNextStatement = true;
605 setSteppingMode(SteppingModeEnabled);
606 notifyDoneProcessingDebuggerEvents();
607}
608
609void Debugger::stepOverStatement()
610{
611 if (!m_isPaused)
612 return;
613
614 m_pauseOnCallFrame = m_currentCallFrame;
615 notifyDoneProcessingDebuggerEvents();
616}
617
618void Debugger::stepOutOfFunction()
619{
620 if (!m_isPaused)
621 return;
622
ed1e77d3
A
623 VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
624 m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrame(topVMEntryFrame) : 0;
81345200
A
625 notifyDoneProcessingDebuggerEvents();
626}
627
628void Debugger::updateCallFrame(CallFrame* callFrame)
629{
630 m_currentCallFrame = callFrame;
631 SourceID sourceID = DebuggerCallFrame::sourceIDForCallFrame(callFrame);
632 if (m_lastExecutedSourceID != sourceID) {
633 m_lastExecutedLine = UINT_MAX;
634 m_lastExecutedSourceID = sourceID;
14957cd0 635 }
81345200
A
636}
637
638void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame)
639{
640 updateCallFrame(callFrame);
641 pauseIfNeeded(callFrame);
642 if (!isStepping())
643 m_currentCallFrame = 0;
644}
9dae56ea 645
81345200
A
646void Debugger::pauseIfNeeded(CallFrame* callFrame)
647{
648 if (m_isPaused)
649 return;
650
651 JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject();
652 if (!needPauseHandling(vmEntryGlobalObject))
653 return;
654
655 Breakpoint breakpoint;
656 bool didHitBreakpoint = false;
657 bool pauseNow = m_pauseOnNextStatement;
658 pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame);
659
ed1e77d3 660 DebuggerPausedScope debuggerPausedScope(*this);
81345200
A
661
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();
666 if (!pauseNow)
667 return;
668
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;
674
675 if (didHitBreakpoint) {
ed1e77d3 676 handleBreakpointHit(vmEntryGlobalObject, breakpoint);
81345200
A
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)
680 return;
ed1e77d3 681 m_pausingBreakpointID = breakpoint.id;
81345200
A
682 }
683
ed1e77d3
A
684 {
685 PauseReasonDeclaration reason(*this, didHitBreakpoint ? PausedForBreakpoint : m_reasonForPause);
686 handlePause(vmEntryGlobalObject, m_reasonForPause);
687 }
688
689 m_pausingBreakpointID = noBreakpointID;
81345200
A
690
691 if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) {
692 setSteppingMode(SteppingModeDisabled);
693 m_currentCallFrame = nullptr;
694 }
695}
696
ed1e77d3 697void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasCatchHandler)
81345200
A
698{
699 if (m_isPaused)
700 return;
701
702 PauseReasonDeclaration reason(*this, PausedForException);
ed1e77d3 703 if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasCatchHandler)) {
81345200
A
704 m_pauseOnNextStatement = true;
705 setSteppingMode(SteppingModeEnabled);
706 }
707
708 m_hasHandlerForExceptionCallback = true;
709 m_currentException = exception;
710 updateCallFrameAndPauseIfNeeded(callFrame);
711 m_currentException = JSValue();
712 m_hasHandlerForExceptionCallback = false;
713}
714
715void Debugger::atStatement(CallFrame* callFrame)
716{
717 if (m_isPaused)
718 return;
719
720 PauseReasonDeclaration reason(*this, PausedAtStatement);
721 updateCallFrameAndPauseIfNeeded(callFrame);
722}
723
724void Debugger::callEvent(CallFrame* callFrame)
725{
726 if (m_isPaused)
727 return;
728
729 PauseReasonDeclaration reason(*this, PausedAfterCall);
730 updateCallFrameAndPauseIfNeeded(callFrame);
731}
732
733void Debugger::returnEvent(CallFrame* callFrame)
734{
735 if (m_isPaused)
736 return;
737
738 PauseReasonDeclaration reason(*this, PausedBeforeReturn);
739 updateCallFrameAndPauseIfNeeded(callFrame);
740
741 // detach may have been called during pauseIfNeeded
742 if (!m_currentCallFrame)
743 return;
744
745 // Treat stepping over a return statement like stepping out.
ed1e77d3
A
746 if (m_currentCallFrame == m_pauseOnCallFrame) {
747 VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
748 m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
749 }
81345200 750
ed1e77d3
A
751 VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
752 m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
81345200
A
753}
754
755void Debugger::willExecuteProgram(CallFrame* callFrame)
756{
757 if (m_isPaused)
758 return;
759
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);
768}
769
770void Debugger::didExecuteProgram(CallFrame* callFrame)
771{
772 if (m_isPaused)
773 return;
774
775 PauseReasonDeclaration reason(*this, PausedAtEndOfProgram);
776 updateCallFrameAndPauseIfNeeded(callFrame);
777
778 // Treat stepping over the end of a program like stepping out.
779 if (!m_currentCallFrame)
780 return;
781 if (m_currentCallFrame == m_pauseOnCallFrame) {
ed1e77d3
A
782 VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
783 m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
81345200
A
784 if (!m_currentCallFrame)
785 return;
14957cd0 786 }
ed1e77d3
A
787 VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
788 m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
81345200
A
789}
790
791void Debugger::didReachBreakpoint(CallFrame* callFrame)
792{
793 if (m_isPaused)
794 return;
795
ed1e77d3 796 PauseReasonDeclaration reason(*this, PausedForDebuggerStatement);
81345200
A
797 m_pauseOnNextStatement = true;
798 setSteppingMode(SteppingModeEnabled);
799 updateCallFrameAndPauseIfNeeded(callFrame);
800}
801
802DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
803{
804 ASSERT(m_currentDebuggerCallFrame);
805 return m_currentDebuggerCallFrame.get();
9dae56ea
A
806}
807
808} // namespace JSC