diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/debugger/Debugger.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/debugger/Debugger.cpp')
-rw-r--r-- | Source/JavaScriptCore/debugger/Debugger.cpp | 301 |
1 files changed, 143 insertions, 158 deletions
diff --git a/Source/JavaScriptCore/debugger/Debugger.cpp b/Source/JavaScriptCore/debugger/Debugger.cpp index f50d54be5..afa7546c8 100644 --- a/Source/JavaScriptCore/debugger/Debugger.cpp +++ b/Source/JavaScriptCore/debugger/Debugger.cpp @@ -25,12 +25,13 @@ #include "CodeBlock.h" #include "DebuggerCallFrame.h" #include "Error.h" + #include "HeapIterationScope.h" #include "Interpreter.h" #include "JSCJSValueInlines.h" #include "JSFunction.h" #include "JSGlobalObject.h" -#include "JSCInlines.h" +#include "Operations.h" #include "Parser.h" #include "Protect.h" #include "VMEntryScope.h" @@ -39,41 +40,65 @@ namespace { using namespace JSC; -struct GatherSourceProviders : public MarkedBlock::VoidFunctor { - HashSet<SourceProvider*> sourceProviders; - JSGlobalObject* m_globalObject; +class Recompiler : public MarkedBlock::VoidFunctor { +public: + Recompiler(JSC::Debugger*); + ~Recompiler(); + void operator()(JSCell*); - GatherSourceProviders(JSGlobalObject* globalObject) - : m_globalObject(globalObject) { } +private: + typedef HashSet<FunctionExecutable*> FunctionExecutableSet; + typedef HashMap<SourceProvider*, ExecState*> SourceProviderMap; + + JSC::Debugger* m_debugger; + FunctionExecutableSet m_functionExecutables; + SourceProviderMap m_sourceProviders; +}; - IterationStatus operator()(JSCell* cell) - { - JSFunction* function = jsDynamicCast<JSFunction*>(cell); - if (!function) - return IterationStatus::Continue; +inline Recompiler::Recompiler(JSC::Debugger* debugger) + : m_debugger(debugger) +{ +} + +inline Recompiler::~Recompiler() +{ + // Call sourceParsed() after reparsing all functions because it will execute + // JavaScript in the inspector. + SourceProviderMap::const_iterator end = m_sourceProviders.end(); + for (SourceProviderMap::const_iterator iter = m_sourceProviders.begin(); iter != end; ++iter) + m_debugger->sourceParsed(iter->value, iter->key, -1, String()); +} - if (function->scope()->globalObject() != m_globalObject) - return IterationStatus::Continue; +inline void Recompiler::operator()(JSCell* cell) +{ + if (!cell->inherits(JSFunction::info())) + return; - if (!function->executable()->isFunctionExecutable()) - return IterationStatus::Continue; + JSFunction* function = jsCast<JSFunction*>(cell); + if (function->executable()->isHostFunction()) + return; - if (function->isHostOrBuiltinFunction()) - return IterationStatus::Continue; + FunctionExecutable* executable = function->jsExecutable(); - sourceProviders.add( - jsCast<FunctionExecutable*>(function->executable())->source().provider()); - return IterationStatus::Continue; - } -}; + // Check if the function is already in the set - if so, + // we've already retranslated it, nothing to do here. + if (!m_functionExecutables.add(executable).isNewEntry) + return; + + ExecState* exec = function->scope()->globalObject()->JSGlobalObject::globalExec(); + executable->clearCodeIfNotCompiling(); + executable->clearUnlinkedCodeForRecompilationIfNotCompiling(); + if (m_debugger == function->scope()->globalObject()->debugger()) + m_sourceProviders.add(executable->source().provider(), exec); +} } // namespace namespace JSC { -class DebuggerPausedScope { +class DebuggerCallFrameScope { public: - DebuggerPausedScope(Debugger& debugger) + DebuggerCallFrameScope(Debugger& debugger) : m_debugger(debugger) { ASSERT(!m_debugger.m_currentDebuggerCallFrame); @@ -81,11 +106,11 @@ public: m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame); } - ~DebuggerPausedScope() + ~DebuggerCallFrameScope() { if (m_debugger.m_currentDebuggerCallFrame) { m_debugger.m_currentDebuggerCallFrame->invalidate(); - m_debugger.m_currentDebuggerCallFrame = nullptr; + m_debugger.m_currentDebuggerCallFrame = 0; } } @@ -113,14 +138,14 @@ private: Debugger& m_debugger; }; -Debugger::Debugger(VM& vm) - : m_vm(vm) +Debugger::Debugger(bool isInWorkerThread) + : m_vm(nullptr) , m_pauseOnExceptionsState(DontPauseOnExceptions) , m_pauseOnNextStatement(false) , m_isPaused(false) , m_breakpointsActivated(true) , m_hasHandlerForExceptionCallback(false) - , m_suppressAllPauses(false) + , m_isInWorkerThread(isInWorkerThread) , m_steppingMode(SteppingModeDisabled) , m_reasonForPause(NotPaused) , m_pauseOnCallFrame(0) @@ -128,7 +153,6 @@ Debugger::Debugger(VM& vm) , m_lastExecutedLine(UINT_MAX) , m_lastExecutedSourceID(noSourceID) , m_topBreakpointID(noBreakpointID) - , m_pausingBreakpointID(noBreakpointID) { } @@ -142,19 +166,12 @@ Debugger::~Debugger() void Debugger::attach(JSGlobalObject* globalObject) { ASSERT(!globalObject->debugger()); + if (!m_vm) + m_vm = &globalObject->vm(); + else + ASSERT(m_vm == &globalObject->vm()); globalObject->setDebugger(this); m_globalObjects.add(globalObject); - - m_vm.setShouldBuildPCToCodeOriginMapping(); - - // Call sourceParsed because it will execute JavaScript in the inspector. - GatherSourceProviders gatherSourceProviders(globalObject); - { - HeapIterationScope iterationScope(m_vm.heap); - m_vm.heap.objectSpace().forEachLiveCell(iterationScope, gatherSourceProviders); - } - for (auto* sourceProvider : gatherSourceProviders.sourceProviders) - sourceParsed(globalObject->globalExec(), sourceProvider, -1, String()); } void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason) @@ -178,11 +195,8 @@ void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason) clearDebuggerRequests(globalObject); globalObject->setDebugger(0); -} - -bool Debugger::isAttached(JSGlobalObject* globalObject) -{ - return globalObject->debugger() == this; + if (!m_globalObjects.size()) + m_vm = nullptr; } class Debugger::SetSteppingModeFunctor { @@ -213,12 +227,12 @@ void Debugger::setSteppingMode(SteppingMode mode) { if (mode == m_steppingMode) return; - - m_vm.heap.completeAllDFGPlans(); - m_steppingMode = mode; + + if (!m_vm) + return; SetSteppingModeFunctor functor(this, mode); - m_vm.heap.forEachCodeBlock(functor); + m_vm->heap.forEachCodeBlock(functor); } void Debugger::registerCodeBlock(CodeBlock* codeBlock) @@ -228,27 +242,9 @@ void Debugger::registerCodeBlock(CodeBlock* codeBlock) codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled); } -void Debugger::setProfilingClient(ProfilingClient* client) -{ - ASSERT(!!m_profilingClient != !!client); - m_profilingClient = client; - - recompileAllJSFunctions(); -} - -double Debugger::willEvaluateScript() -{ - return m_profilingClient->willEvaluateScript(); -} - -void Debugger::didEvaluateScript(double startTime, ProfilingReason reason) -{ - m_profilingClient->didEvaluateScript(startTime, reason); -} - void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot) { - ScriptExecutable* executable = codeBlock->ownerScriptExecutable(); + ScriptExecutable* executable = codeBlock->ownerExecutable(); SourceID sourceID = static_cast<SourceID>(executable->sourceID()); if (breakpoint.sourceID != sourceID) @@ -257,7 +253,7 @@ void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, Br unsigned line = breakpoint.line; unsigned column = breakpoint.column; - unsigned startLine = executable->firstLine(); + unsigned startLine = executable->lineNo(); unsigned startColumn = executable->startColumn(); unsigned endLine = executable->lastLine(); unsigned endColumn = executable->endColumn(); @@ -317,15 +313,26 @@ private: void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot) { - m_vm.heap.completeAllDFGPlans(); - + if (!m_vm) + return; ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot); - m_vm.heap.forEachCodeBlock(functor); + m_vm->heap.forEachCodeBlock(functor); } -void Debugger::recompileAllJSFunctions() +void Debugger::recompileAllJSFunctions(VM* vm) { - m_vm.deleteAllCode(); + // If JavaScript is running, it's not safe to recompile, since we'll end + // up throwing away code that is live on the stack. + if (vm->entryScope) { + vm->entryScope->setRecompilationNeeded(true); + return; + } + + vm->prepareToDiscardCode(); + + Recompiler recompiler(this); + HeapIterationScope iterationScope(vm->heap); + vm->heap.objectSpace().forEachLiveCell(iterationScope, recompiler); } BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn) @@ -339,18 +346,18 @@ BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator; LineToBreakpointsMap::iterator breaksIt = it->value.find(line); if (breaksIt == it->value.end()) - breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator; + breaksIt = it->value.set(line, BreakpointsInLine()).iterator; - BreakpointsList& breakpoints = *breaksIt->value; - for (Breakpoint* current = breakpoints.head(); current; current = current->next()) { - if (current->column == column) { + BreakpointsInLine& breakpoints = breaksIt->value; + unsigned breakpointsCount = breakpoints.size(); + for (unsigned i = 0; i < breakpointsCount; i++) + if (breakpoints[i].column == column) { // The breakpoint already exists. We're not allowed to create a new // breakpoint at this location. Rather than returning the breakpointID // of the pre-existing breakpoint, we need to return noBreakpointID // to indicate that we're not creating a new one. return noBreakpointID; } - } BreakpointID id = ++m_topBreakpointID; RELEASE_ASSERT(id != noBreakpointID); @@ -359,9 +366,8 @@ BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine actualLine = line; actualColumn = column; - Breakpoint* newBreakpoint = new Breakpoint(breakpoint); - breakpoints.append(newBreakpoint); - m_breakpointIDToBreakpoint.set(id, newBreakpoint); + breakpoints.append(breakpoint); + m_breakpointIDToBreakpoint.set(id, &breakpoints.last()); toggleBreakpoint(breakpoint, BreakpointEnabled); @@ -374,35 +380,31 @@ void Debugger::removeBreakpoint(BreakpointID id) BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id); ASSERT(idIt != m_breakpointIDToBreakpoint.end()); - Breakpoint* breakpoint = idIt->value; + Breakpoint& breakpoint = *idIt->value; - SourceID sourceID = breakpoint->sourceID; + SourceID sourceID = breakpoint.sourceID; ASSERT(sourceID); SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID); ASSERT(it != m_sourceIDToBreakpoints.end()); - LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint->line); + LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint.line); ASSERT(breaksIt != it->value.end()); - toggleBreakpoint(*breakpoint, BreakpointDisabled); + toggleBreakpoint(breakpoint, BreakpointDisabled); - BreakpointsList& breakpoints = *breaksIt->value; -#if !ASSERT_DISABLED - bool found = false; - for (Breakpoint* current = breakpoints.head(); current && !found; current = current->next()) { - if (current->id == breakpoint->id) - found = true; - } - ASSERT(found); -#endif - - m_breakpointIDToBreakpoint.remove(idIt); - breakpoints.remove(breakpoint); - delete breakpoint; + BreakpointsInLine& breakpoints = breaksIt->value; + unsigned breakpointsCount = breakpoints.size(); + for (unsigned i = 0; i < breakpointsCount; i++) { + if (breakpoints[i].id == breakpoint.id) { + breakpoints.remove(i); + m_breakpointIDToBreakpoint.remove(idIt); - if (breakpoints.isEmpty()) { - it->value.remove(breaksIt); - if (it->value.isEmpty()) - m_sourceIDToBreakpoints.remove(it); + if (breakpoints.isEmpty()) { + it->value.remove(breaksIt); + if (it->value.isEmpty()) + m_sourceIDToBreakpoints.remove(it); + } + break; + } } } @@ -423,11 +425,12 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br return false; bool hit = false; - const BreakpointsList& breakpoints = *breaksIt->value; - Breakpoint* breakpoint; - for (breakpoint = breakpoints.head(); breakpoint; breakpoint = breakpoint->next()) { - unsigned breakLine = breakpoint->line; - unsigned breakColumn = breakpoint->column; + 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) @@ -440,22 +443,17 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br return false; if (hitBreakpoint) - *hitBreakpoint = *breakpoint; + *hitBreakpoint = breakpoints[i]; - breakpoint->hitCount++; - if (breakpoint->ignoreCount >= breakpoint->hitCount) - return false; - - if (breakpoint->condition.isEmpty()) + 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); - NakedPtr<Exception> exception; - DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame(); - JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception); + JSValue exception; + JSValue result = DebuggerCallFrame::evaluateWithCallFrame(m_currentCallFrame, breakpoints[i].condition, exception); // We can lose the debugger while executing JavaScript. if (!m_currentCallFrame) @@ -490,14 +488,14 @@ private: void Debugger::clearBreakpoints() { - m_vm.heap.completeAllDFGPlans(); - m_topBreakpointID = noBreakpointID; m_breakpointIDToBreakpoint.clear(); m_sourceIDToBreakpoints.clear(); + if (!m_vm) + return; ClearCodeBlockDebuggerRequestsFunctor functor(this); - m_vm.heap.forEachCodeBlock(functor); + m_vm->heap.forEachCodeBlock(functor); } class Debugger::ClearDebuggerRequestsFunctor { @@ -520,10 +518,9 @@ private: void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject) { - m_vm.heap.completeAllDFGPlans(); - + ASSERT(m_vm); ClearDebuggerRequestsFunctor functor(globalObject); - m_vm.heap.forEachCodeBlock(functor); + m_vm->heap.forEachCodeBlock(functor); } void Debugger::setBreakpointsActivated(bool activated) @@ -548,12 +545,10 @@ void Debugger::breakProgram() if (m_isPaused) return; - if (!m_vm.topCallFrame) - return; - m_pauseOnNextStatement = true; setSteppingMode(SteppingModeEnabled); - m_currentCallFrame = m_vm.topCallFrame; + m_currentCallFrame = m_vm->topCallFrame; + ASSERT(m_currentCallFrame); pauseIfNeeded(m_currentCallFrame); } @@ -590,8 +585,7 @@ void Debugger::stepOutOfFunction() if (!m_isPaused) return; - VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame; - m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrame(topVMEntryFrame) : 0; + m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrameSkippingVMEntrySentinel() : 0; notifyDoneProcessingDebuggerEvents(); } @@ -618,9 +612,6 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame) if (m_isPaused) return; - if (m_suppressAllPauses) - return; - JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject(); if (!needPauseHandling(vmEntryGlobalObject)) return; @@ -630,8 +621,6 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame) 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); @@ -639,6 +628,8 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame) if (!pauseNow) return; + DebuggerCallFrameScope debuggerCallFrameScope(*this); + // 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); @@ -646,21 +637,14 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame) m_pauseOnNextStatement = false; if (didHitBreakpoint) { - handleBreakpointHit(vmEntryGlobalObject, breakpoint); + 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; - m_pausingBreakpointID = breakpoint.id; - } - - { - PauseReasonDeclaration reason(*this, didHitBreakpoint ? PausedForBreakpoint : m_reasonForPause); - handlePause(vmEntryGlobalObject, m_reasonForPause); - RELEASE_ASSERT(!callFrame->hadException()); } - m_pausingBreakpointID = noBreakpointID; + handlePause(m_reasonForPause, vmEntryGlobalObject); if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) { setSteppingMode(SteppingModeDisabled); @@ -668,13 +652,13 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame) } } -void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasCatchHandler) +void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasHandler) { if (m_isPaused) return; PauseReasonDeclaration reason(*this, PausedForException); - if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasCatchHandler)) { + if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) { m_pauseOnNextStatement = true; setSteppingMode(SteppingModeEnabled); } @@ -717,13 +701,10 @@ void Debugger::returnEvent(CallFrame* callFrame) return; // Treat stepping over a return statement like stepping out. - if (m_currentCallFrame == m_pauseOnCallFrame) { - VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame; - m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame); - } + if (m_currentCallFrame == m_pauseOnCallFrame) + m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); - VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame; - m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame); + m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); } void Debugger::willExecuteProgram(CallFrame* callFrame) @@ -732,7 +713,13 @@ void Debugger::willExecuteProgram(CallFrame* callFrame) return; PauseReasonDeclaration reason(*this, PausedAtStartOfProgram); - updateCallFrameAndPauseIfNeeded(callFrame); + // 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) @@ -747,13 +734,11 @@ void Debugger::didExecuteProgram(CallFrame* callFrame) if (!m_currentCallFrame) return; if (m_currentCallFrame == m_pauseOnCallFrame) { - VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame; - m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame); + m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); if (!m_currentCallFrame) return; } - VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame; - m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame); + m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel(); } void Debugger::didReachBreakpoint(CallFrame* callFrame) @@ -761,7 +746,7 @@ void Debugger::didReachBreakpoint(CallFrame* callFrame) if (m_isPaused) return; - PauseReasonDeclaration reason(*this, PausedForDebuggerStatement); + PauseReasonDeclaration reason(*this, PausedForBreakpoint); m_pauseOnNextStatement = true; setSteppingMode(SteppingModeEnabled); updateCallFrameAndPauseIfNeeded(callFrame); |