+ JSValue exception;
+ DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
+ JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception);
+
+ // We can lose the debugger while executing JavaScript.
+ if (!m_currentCallFrame)
+ return false;
+
+ if (exception) {
+ // An erroneous condition counts as "false".
+ handleExceptionInBreakpointCondition(m_currentCallFrame, exception);
+ return false;
+ }
+
+ return result.toBoolean(m_currentCallFrame);
+}
+
+class Debugger::ClearCodeBlockDebuggerRequestsFunctor {
+public:
+ ClearCodeBlockDebuggerRequestsFunctor(Debugger* debugger)
+ : m_debugger(debugger)
+ {
+ }
+
+ bool operator()(CodeBlock* codeBlock)
+ {
+ if (codeBlock->hasDebuggerRequests() && m_debugger == codeBlock->globalObject()->debugger())
+ codeBlock->clearDebuggerRequests();
+ return false;
+ }
+
+private:
+ Debugger* m_debugger;
+};
+
+void Debugger::clearBreakpoints()
+{
+ m_topBreakpointID = noBreakpointID;
+ m_breakpointIDToBreakpoint.clear();
+ m_sourceIDToBreakpoints.clear();
+
+ if (!m_vm)
+ return;
+ ClearCodeBlockDebuggerRequestsFunctor functor(this);
+ forEachCodeBlock(functor);
+}
+
+class Debugger::ClearDebuggerRequestsFunctor {
+public:
+ ClearDebuggerRequestsFunctor(JSGlobalObject* globalObject)
+ : m_globalObject(globalObject)
+ {
+ }
+
+ bool operator()(CodeBlock* codeBlock)
+ {
+ if (codeBlock->hasDebuggerRequests() && m_globalObject == codeBlock->globalObject())
+ codeBlock->clearDebuggerRequests();
+ return false;
+ }
+
+private:
+ JSGlobalObject* m_globalObject;
+};
+
+void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
+{
+ ASSERT(m_vm);
+ ClearDebuggerRequestsFunctor functor(globalObject);
+ forEachCodeBlock(functor);
+}
+
+void Debugger::setBreakpointsActivated(bool activated)
+{
+ m_breakpointsActivated = activated;
+}
+
+void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
+{
+ m_pauseOnExceptionsState = pause;
+}
+
+void Debugger::setPauseOnNextStatement(bool pause)
+{
+ m_pauseOnNextStatement = pause;
+ if (pause)
+ setSteppingMode(SteppingModeEnabled);
+}
+
+void Debugger::breakProgram()
+{
+ if (m_isPaused)
+ return;
+
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ m_currentCallFrame = m_vm->topCallFrame;
+ ASSERT(m_currentCallFrame);
+ pauseIfNeeded(m_currentCallFrame);
+}
+
+void Debugger::continueProgram()
+{
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnNextStatement = false;
+ notifyDoneProcessingDebuggerEvents();
+}
+
+void Debugger::stepIntoStatement()
+{
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ notifyDoneProcessingDebuggerEvents();
+}
+
+void Debugger::stepOverStatement()
+{
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnCallFrame = m_currentCallFrame;
+ notifyDoneProcessingDebuggerEvents();
+}
+
+void Debugger::stepOutOfFunction()
+{
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrameSkippingVMEntrySentinel() : 0;
+ notifyDoneProcessingDebuggerEvents();
+}
+
+void Debugger::updateCallFrame(CallFrame* callFrame)
+{
+ m_currentCallFrame = callFrame;
+ SourceID sourceID = DebuggerCallFrame::sourceIDForCallFrame(callFrame);
+ if (m_lastExecutedSourceID != sourceID) {
+ m_lastExecutedLine = UINT_MAX;
+ m_lastExecutedSourceID = sourceID;
+ }
+}
+
+void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame)
+{
+ updateCallFrame(callFrame);
+ pauseIfNeeded(callFrame);
+ if (!isStepping())
+ m_currentCallFrame = 0;
+}
+
+void Debugger::pauseIfNeeded(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject();
+ if (!needPauseHandling(vmEntryGlobalObject))
+ return;
+
+ Breakpoint breakpoint;
+ bool didHitBreakpoint = false;
+ bool pauseNow = m_pauseOnNextStatement;
+ pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame);
+
+ DebuggerCallFrameScope debuggerCallFrameScope(*this);
+
+ intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame);
+ TextPosition position = DebuggerCallFrame::positionForCallFrame(m_currentCallFrame);
+ pauseNow |= didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint);
+ m_lastExecutedLine = position.m_line.zeroBasedInt();
+ if (!pauseNow)
+ return;
+
+ // Make sure we are not going to pause again on breakpoint actions by
+ // reseting the pause state before executing any breakpoint actions.
+ TemporaryPausedState pausedState(*this);
+ m_pauseOnCallFrame = 0;
+ m_pauseOnNextStatement = false;
+
+ if (didHitBreakpoint) {
+ handleBreakpointHit(breakpoint);
+ // Note that the actions can potentially stop the debugger, so we need to check that
+ // we still have a current call frame when we get back.
+ if (breakpoint.autoContinue || !m_currentCallFrame)
+ return;
+ }
+
+ handlePause(m_reasonForPause, vmEntryGlobalObject);
+
+ if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) {
+ setSteppingMode(SteppingModeDisabled);
+ m_currentCallFrame = nullptr;
+ }
+}
+
+void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasHandler)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedForException);
+ if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) {
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ }
+
+ m_hasHandlerForExceptionCallback = true;
+ m_currentException = exception;
+ updateCallFrameAndPauseIfNeeded(callFrame);
+ m_currentException = JSValue();
+ m_hasHandlerForExceptionCallback = false;
+}
+
+void Debugger::atStatement(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAtStatement);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+}
+
+void Debugger::callEvent(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAfterCall);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+}
+
+void Debugger::returnEvent(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedBeforeReturn);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+
+ // detach may have been called during pauseIfNeeded
+ if (!m_currentCallFrame)
+ return;
+
+ // Treat stepping over a return statement like stepping out.
+ if (m_currentCallFrame == m_pauseOnCallFrame)
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+
+ m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+}
+
+void Debugger::willExecuteProgram(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAtStartOfProgram);
+ // FIXME: This check for whether we're debugging a worker thread is a workaround
+ // for https://bugs.webkit.org/show_bug.cgi?id=102637. Remove it when we rework
+ // the debugger implementation to not require callbacks.
+ if (!m_isInWorkerThread)
+ updateCallFrameAndPauseIfNeeded(callFrame);
+ else if (isStepping())
+ updateCallFrame(callFrame);
+}
+
+void Debugger::didExecuteProgram(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAtEndOfProgram);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+
+ // Treat stepping over the end of a program like stepping out.
+ if (!m_currentCallFrame)
+ return;
+ if (m_currentCallFrame == m_pauseOnCallFrame) {
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+ if (!m_currentCallFrame)
+ return;
+ }
+ m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+}
+
+void Debugger::didReachBreakpoint(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedForBreakpoint);
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+}
+
+DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
+{
+ ASSERT(m_currentDebuggerCallFrame);
+ return m_currentDebuggerCallFrame.get();