bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Breakpoint *hitBreakpoint) { if (!m_breakpointsActivated) return false; SourceIDToBreakpointsMap::const_iterator it = m_sourceIDToBreakpoints.find(sourceID); if (it == m_sourceIDToBreakpoints.end()) return false; unsigned line = position.m_line.zeroBasedInt(); unsigned column = position.m_column.zeroBasedInt(); LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line); if (breaksIt == it->value.end()) return false; bool hit = false; const BreakpointsInLine& breakpoints = breaksIt->value; unsigned breakpointsCount = breakpoints.size(); unsigned i; for (i = 0; i < breakpointsCount; i++) { unsigned breakLine = breakpoints[i].line; unsigned breakColumn = breakpoints[i].column; // Since frontend truncates the indent, the first statement in a line must match the breakpoint (line,0). ASSERT(this == m_currentCallFrame->codeBlock()->globalObject()->debugger()); if ((line != m_lastExecutedLine && line == breakLine && !breakColumn) || (line == breakLine && column == breakColumn)) { hit = true; break; } } if (!hit) return false; if (hitBreakpoint) *hitBreakpoint = breakpoints[i]; if (breakpoints[i].condition.isEmpty()) return true; // We cannot stop in the debugger while executing condition code, // so make it looks like the debugger is already paused. TemporaryPausedState pausedState(*this); JSValue exception; DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame(); JSValue result = debuggerCallFrame->evaluate(breakpoints[i].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); }
void Debugger::pauseIfNeeded(CallFrame* callFrame) { if (m_isPaused) return; if (m_suppressAllPauses) return; JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject(); if (!needPauseHandling(vmEntryGlobalObject)) return; Breakpoint breakpoint; bool didHitBreakpoint = false; bool pauseNow = m_pauseOnNextStatement; pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame); DebuggerPausedScope debuggerPausedScope(*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(vmEntryGlobalObject, 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; m_pausingBreakpointID = breakpoint.id; } { PauseReasonDeclaration reason(*this, didHitBreakpoint ? PausedForBreakpoint : m_reasonForPause); handlePause(vmEntryGlobalObject, m_reasonForPause); RELEASE_ASSERT(!callFrame->hadException()); } m_pausingBreakpointID = noBreakpointID; if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) { setSteppingMode(SteppingModeDisabled); m_currentCallFrame = nullptr; } }