summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/debugger
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/debugger')
-rw-r--r--Source/JavaScriptCore/debugger/Breakpoint.h95
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.cpp763
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.h222
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerActivation.cpp110
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerActivation.h73
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp232
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.h84
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h63
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerPrimitives.h41
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerScope.cpp218
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerScope.h122
-rw-r--r--Source/JavaScriptCore/debugger/ScriptProfilingScope.h93
12 files changed, 1772 insertions, 344 deletions
diff --git a/Source/JavaScriptCore/debugger/Breakpoint.h b/Source/JavaScriptCore/debugger/Breakpoint.h
new file mode 100644
index 000000000..78d208a2b
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/Breakpoint.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Breakpoint_h
+#define Breakpoint_h
+
+#include "DebuggerPrimitives.h"
+#include <wtf/DoublyLinkedList.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+struct Breakpoint : public DoublyLinkedListNode<Breakpoint> {
+ Breakpoint()
+ {
+ }
+
+ Breakpoint(SourceID sourceID, unsigned line, unsigned column, const String& condition, bool autoContinue, unsigned ignoreCount)
+ : sourceID(sourceID)
+ , line(line)
+ , column(column)
+ , condition(condition)
+ , autoContinue(autoContinue)
+ , ignoreCount(ignoreCount)
+ {
+ }
+
+ Breakpoint(const Breakpoint& other)
+ : id(other.id)
+ , sourceID(other.sourceID)
+ , line(other.line)
+ , column(other.column)
+ , condition(other.condition)
+ , autoContinue(other.autoContinue)
+ , ignoreCount(other.ignoreCount)
+ , hitCount(other.hitCount)
+ {
+ }
+
+ BreakpointID id { noBreakpointID };
+ SourceID sourceID { noSourceID };
+ unsigned line { 0 };
+ unsigned column { 0 };
+ String condition;
+ bool autoContinue { false };
+ unsigned ignoreCount { 0 };
+ unsigned hitCount { 0 };
+
+ static const unsigned unspecifiedColumn = UINT_MAX;
+
+private:
+ Breakpoint* m_prev;
+ Breakpoint* m_next;
+
+ friend class WTF::DoublyLinkedListNode<Breakpoint>;
+};
+
+class BreakpointsList : public DoublyLinkedList<Breakpoint>,
+ public RefCounted<BreakpointsList> {
+public:
+ ~BreakpointsList()
+ {
+ Breakpoint* breakpoint;
+ while ((breakpoint = removeHead()))
+ delete breakpoint;
+ ASSERT(isEmpty());
+ }
+};
+
+} // namespace JSC
+
+#endif // Breakpoint_h
diff --git a/Source/JavaScriptCore/debugger/Debugger.cpp b/Source/JavaScriptCore/debugger/Debugger.cpp
index 2c5a1261a..f50d54be5 100644
--- a/Source/JavaScriptCore/debugger/Debugger.cpp
+++ b/Source/JavaScriptCore/debugger/Debugger.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved.
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
*
@@ -22,126 +22,755 @@
#include "config.h"
#include "Debugger.h"
+#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 "Operations.h"
+#include "JSCInlines.h"
#include "Parser.h"
#include "Protect.h"
+#include "VMEntryScope.h"
namespace {
using namespace JSC;
-class Recompiler : public MarkedBlock::VoidFunctor {
+struct GatherSourceProviders : public MarkedBlock::VoidFunctor {
+ HashSet<SourceProvider*> sourceProviders;
+ JSGlobalObject* m_globalObject;
+
+ GatherSourceProviders(JSGlobalObject* globalObject)
+ : m_globalObject(globalObject) { }
+
+ IterationStatus operator()(JSCell* cell)
+ {
+ JSFunction* function = jsDynamicCast<JSFunction*>(cell);
+ if (!function)
+ return IterationStatus::Continue;
+
+ if (function->scope()->globalObject() != m_globalObject)
+ return IterationStatus::Continue;
+
+ if (!function->executable()->isFunctionExecutable())
+ return IterationStatus::Continue;
+
+ if (function->isHostOrBuiltinFunction())
+ return IterationStatus::Continue;
+
+ sourceProviders.add(
+ jsCast<FunctionExecutable*>(function->executable())->source().provider());
+ return IterationStatus::Continue;
+ }
+};
+
+} // namespace
+
+namespace JSC {
+
+class DebuggerPausedScope {
+public:
+ DebuggerPausedScope(Debugger& debugger)
+ : m_debugger(debugger)
+ {
+ ASSERT(!m_debugger.m_currentDebuggerCallFrame);
+ if (m_debugger.m_currentCallFrame)
+ m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame);
+ }
+
+ ~DebuggerPausedScope()
+ {
+ if (m_debugger.m_currentDebuggerCallFrame) {
+ m_debugger.m_currentDebuggerCallFrame->invalidate();
+ m_debugger.m_currentDebuggerCallFrame = nullptr;
+ }
+ }
+
+private:
+ Debugger& m_debugger;
+};
+
+// This is very similar to TemporaryChange<bool>, but that cannot be used
+// as the m_isPaused field uses only one bit.
+class TemporaryPausedState {
public:
- Recompiler(Debugger*);
- ~Recompiler();
- void operator()(JSCell*);
+ TemporaryPausedState(Debugger& debugger)
+ : m_debugger(debugger)
+ {
+ ASSERT(!m_debugger.m_isPaused);
+ m_debugger.m_isPaused = true;
+ }
+
+ ~TemporaryPausedState()
+ {
+ m_debugger.m_isPaused = false;
+ }
+
+private:
+ Debugger& m_debugger;
+};
+
+Debugger::Debugger(VM& vm)
+ : m_vm(vm)
+ , m_pauseOnExceptionsState(DontPauseOnExceptions)
+ , m_pauseOnNextStatement(false)
+ , m_isPaused(false)
+ , m_breakpointsActivated(true)
+ , m_hasHandlerForExceptionCallback(false)
+ , m_suppressAllPauses(false)
+ , m_steppingMode(SteppingModeDisabled)
+ , m_reasonForPause(NotPaused)
+ , m_pauseOnCallFrame(0)
+ , m_currentCallFrame(0)
+ , m_lastExecutedLine(UINT_MAX)
+ , m_lastExecutedSourceID(noSourceID)
+ , m_topBreakpointID(noBreakpointID)
+ , m_pausingBreakpointID(noBreakpointID)
+{
+}
+
+Debugger::~Debugger()
+{
+ HashSet<JSGlobalObject*>::iterator end = m_globalObjects.end();
+ for (HashSet<JSGlobalObject*>::iterator it = m_globalObjects.begin(); it != end; ++it)
+ (*it)->setDebugger(0);
+}
+
+void Debugger::attach(JSGlobalObject* globalObject)
+{
+ ASSERT(!globalObject->debugger());
+ 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)
+{
+ // If we're detaching from the currently executing global object, manually tear down our
+ // stack, since we won't get further debugger callbacks to do so. Also, resume execution,
+ // since there's no point in staying paused once a window closes.
+ if (m_currentCallFrame && m_currentCallFrame->vmEntryGlobalObject() == globalObject) {
+ m_currentCallFrame = 0;
+ m_pauseOnCallFrame = 0;
+ continueProgram();
+ }
+
+ ASSERT(m_globalObjects.contains(globalObject));
+ m_globalObjects.remove(globalObject);
+
+ // If the globalObject is destructing, then its CodeBlocks will also be
+ // destructed. There is no need to do the debugger requests clean up, and
+ // it is not safe to access those CodeBlocks at this time anyway.
+ if (reason != GlobalObjectIsDestructing)
+ clearDebuggerRequests(globalObject);
+
+ globalObject->setDebugger(0);
+}
+
+bool Debugger::isAttached(JSGlobalObject* globalObject)
+{
+ return globalObject->debugger() == this;
+}
+
+class Debugger::SetSteppingModeFunctor {
+public:
+ SetSteppingModeFunctor(Debugger* debugger, SteppingMode mode)
+ : m_debugger(debugger)
+ , m_mode(mode)
+ {
+ }
+
+ bool operator()(CodeBlock* codeBlock)
+ {
+ if (m_debugger == codeBlock->globalObject()->debugger()) {
+ if (m_mode == SteppingModeEnabled)
+ codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
+ else
+ codeBlock->setSteppingMode(CodeBlock::SteppingModeDisabled);
+ }
+ return false;
+ }
private:
- typedef HashSet<FunctionExecutable*> FunctionExecutableSet;
- typedef HashMap<SourceProvider*, ExecState*> SourceProviderMap;
-
Debugger* m_debugger;
- FunctionExecutableSet m_functionExecutables;
- SourceProviderMap m_sourceProviders;
+ SteppingMode m_mode;
};
-inline Recompiler::Recompiler(Debugger* debugger)
- : m_debugger(debugger)
+void Debugger::setSteppingMode(SteppingMode mode)
+{
+ if (mode == m_steppingMode)
+ return;
+
+ m_vm.heap.completeAllDFGPlans();
+
+ m_steppingMode = mode;
+ SetSteppingModeFunctor functor(this, mode);
+ m_vm.heap.forEachCodeBlock(functor);
+}
+
+void Debugger::registerCodeBlock(CodeBlock* codeBlock)
{
+ applyBreakpoints(codeBlock);
+ if (isStepping())
+ codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
}
-inline Recompiler::~Recompiler()
+void Debugger::setProfilingClient(ProfilingClient* client)
{
- // 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());
+ ASSERT(!!m_profilingClient != !!client);
+ m_profilingClient = client;
+
+ recompileAllJSFunctions();
}
-inline void Recompiler::operator()(JSCell* cell)
+double Debugger::willEvaluateScript()
{
- if (!cell->inherits(&JSFunction::s_info))
+ 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();
+
+ SourceID sourceID = static_cast<SourceID>(executable->sourceID());
+ if (breakpoint.sourceID != sourceID)
return;
- JSFunction* function = jsCast<JSFunction*>(cell);
- if (function->executable()->isHostFunction())
+ unsigned line = breakpoint.line;
+ unsigned column = breakpoint.column;
+
+ unsigned startLine = executable->firstLine();
+ unsigned startColumn = executable->startColumn();
+ unsigned endLine = executable->lastLine();
+ unsigned endColumn = executable->endColumn();
+
+ // Inspector breakpoint line and column values are zero-based but the executable
+ // and CodeBlock line and column values are one-based.
+ line += 1;
+ column = column ? column + 1 : Breakpoint::unspecifiedColumn;
+
+ if (line < startLine || line > endLine)
+ return;
+ if (column != Breakpoint::unspecifiedColumn) {
+ if (line == startLine && column < startColumn)
+ return;
+ if (line == endLine && column > endColumn)
+ return;
+ }
+ if (!codeBlock->hasOpDebugForLineAndColumn(line, column))
return;
- FunctionExecutable* executable = function->jsExecutable();
+ if (enabledOrNot == BreakpointEnabled)
+ codeBlock->addBreakpoint(1);
+ else
+ codeBlock->removeBreakpoint(1);
+}
+
+void Debugger::applyBreakpoints(CodeBlock* codeBlock)
+{
+ BreakpointIDToBreakpointMap& breakpoints = m_breakpointIDToBreakpoint;
+ for (auto it = breakpoints.begin(); it != breakpoints.end(); ++it) {
+ Breakpoint& breakpoint = *it->value;
+ toggleBreakpoint(codeBlock, breakpoint, BreakpointEnabled);
+ }
+}
+
+class Debugger::ToggleBreakpointFunctor {
+public:
+ ToggleBreakpointFunctor(Debugger* debugger, Breakpoint& breakpoint, BreakpointState enabledOrNot)
+ : m_debugger(debugger)
+ , m_breakpoint(breakpoint)
+ , m_enabledOrNot(enabledOrNot)
+ {
+ }
+
+ bool operator()(CodeBlock* codeBlock)
+ {
+ if (m_debugger == codeBlock->globalObject()->debugger())
+ m_debugger->toggleBreakpoint(codeBlock, m_breakpoint, m_enabledOrNot);
+ return false;
+ }
+
+private:
+ Debugger* m_debugger;
+ Breakpoint& m_breakpoint;
+ BreakpointState m_enabledOrNot;
+};
+
+void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot)
+{
+ m_vm.heap.completeAllDFGPlans();
+
+ ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot);
+ m_vm.heap.forEachCodeBlock(functor);
+}
+
+void Debugger::recompileAllJSFunctions()
+{
+ m_vm.deleteAllCode();
+}
+
+BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn)
+{
+ SourceID sourceID = breakpoint.sourceID;
+ unsigned line = breakpoint.line;
+ unsigned column = breakpoint.column;
+
+ SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
+ if (it == m_sourceIDToBreakpoints.end())
+ 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;
+
+ BreakpointsList& breakpoints = *breaksIt->value;
+ for (Breakpoint* current = breakpoints.head(); current; current = current->next()) {
+ if (current->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);
+
+ breakpoint.id = id;
+ actualLine = line;
+ actualColumn = column;
+
+ Breakpoint* newBreakpoint = new Breakpoint(breakpoint);
+ breakpoints.append(newBreakpoint);
+ m_breakpointIDToBreakpoint.set(id, newBreakpoint);
+
+ toggleBreakpoint(breakpoint, BreakpointEnabled);
+
+ return id;
+}
+
+void Debugger::removeBreakpoint(BreakpointID id)
+{
+ ASSERT(id != noBreakpointID);
+
+ BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id);
+ ASSERT(idIt != m_breakpointIDToBreakpoint.end());
+ Breakpoint* breakpoint = idIt->value;
+
+ 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);
+ ASSERT(breaksIt != it->value.end());
+
+ 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;
+
+ if (breakpoints.isEmpty()) {
+ it->value.remove(breaksIt);
+ if (it->value.isEmpty())
+ m_sourceIDToBreakpoints.remove(it);
+ }
+}
+
+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 BreakpointsList& breakpoints = *breaksIt->value;
+ Breakpoint* breakpoint;
+ for (breakpoint = breakpoints.head(); breakpoint; breakpoint = breakpoint->next()) {
+ unsigned breakLine = breakpoint->line;
+ unsigned breakColumn = breakpoint->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;
- // 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)
+ if (hitBreakpoint)
+ *hitBreakpoint = *breakpoint;
+
+ breakpoint->hitCount++;
+ if (breakpoint->ignoreCount >= breakpoint->hitCount)
+ return false;
+
+ if (breakpoint->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);
+
+ // 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_vm.heap.completeAllDFGPlans();
+
+ m_topBreakpointID = noBreakpointID;
+ m_breakpointIDToBreakpoint.clear();
+ m_sourceIDToBreakpoints.clear();
+
+ ClearCodeBlockDebuggerRequestsFunctor functor(this);
+ m_vm.heap.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)
+{
+ m_vm.heap.completeAllDFGPlans();
+
+ ClearDebuggerRequestsFunctor functor(globalObject);
+ m_vm.heap.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;
+
+ if (!m_vm.topCallFrame)
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);
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ m_currentCallFrame = m_vm.topCallFrame;
+ pauseIfNeeded(m_currentCallFrame);
}
-} // namespace
+void Debugger::continueProgram()
+{
+ if (!m_isPaused)
+ return;
-namespace JSC {
+ m_pauseOnNextStatement = false;
+ notifyDoneProcessingDebuggerEvents();
+}
-Debugger::~Debugger()
+void Debugger::stepIntoStatement()
{
- HashSet<JSGlobalObject*>::iterator end = m_globalObjects.end();
- for (HashSet<JSGlobalObject*>::iterator it = m_globalObjects.begin(); it != end; ++it)
- (*it)->setDebugger(0);
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ notifyDoneProcessingDebuggerEvents();
}
-void Debugger::attach(JSGlobalObject* globalObject)
+void Debugger::stepOverStatement()
{
- ASSERT(!globalObject->debugger());
- globalObject->setDebugger(this);
- m_globalObjects.add(globalObject);
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnCallFrame = m_currentCallFrame;
+ notifyDoneProcessingDebuggerEvents();
}
-void Debugger::detach(JSGlobalObject* globalObject)
+void Debugger::stepOutOfFunction()
{
- ASSERT(m_globalObjects.contains(globalObject));
- m_globalObjects.remove(globalObject);
- globalObject->setDebugger(0);
+ if (!m_isPaused)
+ return;
+
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrame(topVMEntryFrame) : 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;
+
+ 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;
+ }
+}
+
+void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasCatchHandler)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedForException);
+ if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasCatchHandler)) {
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ }
+
+ m_hasHandlerForExceptionCallback = true;
+ m_currentException = exception;
+ updateCallFrameAndPauseIfNeeded(callFrame);
+ m_currentException = JSValue();
+ m_hasHandlerForExceptionCallback = false;
}
-void Debugger::recompileAllJSFunctions(VM* vm)
+void Debugger::atStatement(CallFrame* callFrame)
{
- // If JavaScript is running, it's not safe to recompile, since we'll end
- // up throwing away code that is live on the stack.
- ASSERT(!vm->dynamicGlobalObject);
- if (vm->dynamicGlobalObject)
+ if (m_isPaused)
return;
- Recompiler recompiler(this);
- vm->heap.objectSpace().forEachLiveCell(recompiler);
+ PauseReasonDeclaration reason(*this, PausedAtStatement);
+ updateCallFrameAndPauseIfNeeded(callFrame);
}
-JSValue evaluateInGlobalCallFrame(const String& script, JSValue& exception, JSGlobalObject* globalObject)
+void Debugger::callEvent(CallFrame* callFrame)
{
- CallFrame* globalCallFrame = globalObject->globalExec();
- VM& vm = globalObject->vm();
+ if (m_isPaused)
+ return;
- EvalExecutable* eval = EvalExecutable::create(globalCallFrame, vm.codeCache(), makeSource(script), false);
- if (!eval) {
- exception = vm.exception;
- vm.exception = JSValue();
- return exception;
+ 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) {
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
}
- JSValue result = vm.interpreter->execute(eval, globalCallFrame, globalObject, globalCallFrame->scope());
- if (vm.exception) {
- exception = vm.exception;
- vm.exception = JSValue();
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+}
+
+void Debugger::willExecuteProgram(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAtStartOfProgram);
+ updateCallFrameAndPauseIfNeeded(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) {
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+ if (!m_currentCallFrame)
+ return;
}
- ASSERT(result);
- return result;
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+}
+
+void Debugger::didReachBreakpoint(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedForDebuggerStatement);
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+}
+
+DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
+{
+ ASSERT(m_currentDebuggerCallFrame);
+ return m_currentDebuggerCallFrame.get();
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/Debugger.h b/Source/JavaScriptCore/debugger/Debugger.h
index 95dd62b06..2e91aafbe 100644
--- a/Source/JavaScriptCore/debugger/Debugger.h
+++ b/Source/JavaScriptCore/debugger/Debugger.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,44 +22,220 @@
#ifndef Debugger_h
#define Debugger_h
+#include "Breakpoint.h"
+#include "CallData.h"
+#include "DebuggerCallFrame.h"
+#include "DebuggerPrimitives.h"
+#include "JSCJSValue.h"
+#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
+#include <wtf/RefPtr.h>
+#include <wtf/text/TextPosition.h>
namespace JSC {
- class DebuggerCallFrame;
- class ExecState;
- class VM;
- class JSGlobalObject;
- class JSValue;
- class SourceProvider;
+class CodeBlock;
+class Exception;
+class ExecState;
+class JSGlobalObject;
+class SourceProvider;
+class VM;
- class JS_EXPORT_PRIVATE Debugger {
+typedef ExecState CallFrame;
+
+class JS_EXPORT_PRIVATE Debugger {
+public:
+ Debugger(VM&);
+ virtual ~Debugger();
+
+ VM& vm() { return m_vm; }
+
+ JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
+ bool hasHandlerForExceptionCallback() const
+ {
+ ASSERT(m_reasonForPause == PausedForException);
+ return m_hasHandlerForExceptionCallback;
+ }
+ JSValue currentException()
+ {
+ ASSERT(m_reasonForPause == PausedForException);
+ return m_currentException;
+ }
+
+ bool needsExceptionCallbacks() const { return m_pauseOnExceptionsState != DontPauseOnExceptions; }
+
+ enum ReasonForDetach {
+ TerminatingDebuggingSession,
+ GlobalObjectIsDestructing
+ };
+ void attach(JSGlobalObject*);
+ void detach(JSGlobalObject*, ReasonForDetach);
+ bool isAttached(JSGlobalObject*);
+
+ BreakpointID setBreakpoint(Breakpoint, unsigned& actualLine, unsigned& actualColumn);
+ void removeBreakpoint(BreakpointID);
+ void clearBreakpoints();
+ void setBreakpointsActivated(bool);
+ void activateBreakpoints() { setBreakpointsActivated(true); }
+ void deactivateBreakpoints() { setBreakpointsActivated(false); }
+
+ enum PauseOnExceptionsState {
+ DontPauseOnExceptions,
+ PauseOnAllExceptions,
+ PauseOnUncaughtExceptions
+ };
+ PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; }
+ void setPauseOnExceptionsState(PauseOnExceptionsState);
+
+ enum ReasonForPause {
+ NotPaused,
+ PausedForException,
+ PausedAtStatement,
+ PausedAfterCall,
+ PausedBeforeReturn,
+ PausedAtStartOfProgram,
+ PausedAtEndOfProgram,
+ PausedForBreakpoint,
+ PausedForDebuggerStatement,
+ };
+ ReasonForPause reasonForPause() const { return m_reasonForPause; }
+ BreakpointID pausingBreakpointID() const { return m_pausingBreakpointID; }
+
+ void setPauseOnNextStatement(bool);
+ void breakProgram();
+ void continueProgram();
+ void stepIntoStatement();
+ void stepOverStatement();
+ void stepOutOfFunction();
+
+ bool isPaused() const { return m_isPaused; }
+ bool isStepping() const { return m_steppingMode == SteppingModeEnabled; }
+
+ bool suppressAllPauses() const { return m_suppressAllPauses; }
+ void setSuppressAllPauses(bool suppress) { m_suppressAllPauses = suppress; }
+
+ virtual void sourceParsed(ExecState*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0;
+
+ void exception(CallFrame*, JSValue exceptionValue, bool hasCatchHandler);
+ void atStatement(CallFrame*);
+ void callEvent(CallFrame*);
+ void returnEvent(CallFrame*);
+ void willExecuteProgram(CallFrame*);
+ void didExecuteProgram(CallFrame*);
+ void didReachBreakpoint(CallFrame*);
+
+ virtual void recompileAllJSFunctions();
+
+ void registerCodeBlock(CodeBlock*);
+
+ class ProfilingClient {
public:
- virtual ~Debugger();
+ virtual ~ProfilingClient() { }
+ virtual bool isAlreadyProfiling() const = 0;
+ virtual double willEvaluateScript() = 0;
+ virtual void didEvaluateScript(double startTime, ProfilingReason) = 0;
+ };
- void attach(JSGlobalObject*);
- virtual void detach(JSGlobalObject*);
+ void setProfilingClient(ProfilingClient*);
+ bool hasProfilingClient() const { return m_profilingClient != nullptr; }
+ bool isAlreadyProfiling() const { return m_profilingClient && m_profilingClient->isAlreadyProfiling(); }
+ double willEvaluateScript();
+ void didEvaluateScript(double startTime, ProfilingReason);
- virtual void sourceParsed(ExecState*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0;
+protected:
+ virtual bool needPauseHandling(JSGlobalObject*) { return false; }
+ virtual void handleBreakpointHit(JSGlobalObject*, const Breakpoint&) { }
+ virtual void handleExceptionInBreakpointCondition(ExecState*, Exception*) const { }
+ virtual void handlePause(JSGlobalObject*, ReasonForPause) { }
+ virtual void notifyDoneProcessingDebuggerEvents() { }
- virtual void exception(const DebuggerCallFrame&, intptr_t, int, int, bool) = 0;
- virtual void atStatement(const DebuggerCallFrame&, intptr_t, int, int) = 0;
- virtual void callEvent(const DebuggerCallFrame&, intptr_t, int, int) = 0;
- virtual void returnEvent(const DebuggerCallFrame&, intptr_t, int, int) = 0;
+private:
+ typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap;
- virtual void willExecuteProgram(const DebuggerCallFrame&, intptr_t, int, int) = 0;
- virtual void didExecuteProgram(const DebuggerCallFrame&, intptr_t, int, int) = 0;
- virtual void didReachBreakpoint(const DebuggerCallFrame&, intptr_t, int, int) = 0;
+ typedef HashMap<unsigned, RefPtr<BreakpointsList>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
+ typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap;
+ class ClearCodeBlockDebuggerRequestsFunctor;
+ class ClearDebuggerRequestsFunctor;
+ class SetSteppingModeFunctor;
+ class ToggleBreakpointFunctor;
- void recompileAllJSFunctions(VM*);
+ class PauseReasonDeclaration {
+ public:
+ PauseReasonDeclaration(Debugger& debugger, ReasonForPause reason)
+ : m_debugger(debugger)
+ {
+ m_debugger.m_reasonForPause = reason;
+ }
+ ~PauseReasonDeclaration()
+ {
+ m_debugger.m_reasonForPause = NotPaused;
+ }
private:
- HashSet<JSGlobalObject*> m_globalObjects;
+ Debugger& m_debugger;
};
- // This function exists only for backwards compatibility with existing WebScriptDebugger clients.
- JS_EXPORT_PRIVATE JSValue evaluateInGlobalCallFrame(const WTF::String&, JSValue& exception, JSGlobalObject*);
+ bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);
+
+ void updateNeedForOpDebugCallbacks();
+
+ // These update functions are only needed because our current breakpoints are
+ // key'ed off the source position instead of the bytecode PC. This ensures
+ // that we don't break on the same line more than once. Once we switch to a
+ // bytecode PC key'ed breakpoint, we will not need these anymore and should
+ // be able to remove them.
+ void updateCallFrame(JSC::CallFrame*);
+ void updateCallFrameAndPauseIfNeeded(JSC::CallFrame*);
+ void pauseIfNeeded(JSC::CallFrame*);
+
+ enum SteppingMode {
+ SteppingModeDisabled,
+ SteppingModeEnabled
+ };
+ void setSteppingMode(SteppingMode);
+
+ enum BreakpointState {
+ BreakpointDisabled,
+ BreakpointEnabled
+ };
+ void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState);
+ void applyBreakpoints(CodeBlock*);
+ void toggleBreakpoint(Breakpoint&, BreakpointState);
+
+ void clearDebuggerRequests(JSGlobalObject*);
+
+ VM& m_vm;
+ HashSet<JSGlobalObject*> m_globalObjects;
+
+ PauseOnExceptionsState m_pauseOnExceptionsState;
+ bool m_pauseOnNextStatement : 1;
+ bool m_isPaused : 1;
+ bool m_breakpointsActivated : 1;
+ bool m_hasHandlerForExceptionCallback : 1;
+ bool m_suppressAllPauses : 1;
+ unsigned m_steppingMode : 1; // SteppingMode
+
+ ReasonForPause m_reasonForPause;
+ JSValue m_currentException;
+ CallFrame* m_pauseOnCallFrame;
+ CallFrame* m_currentCallFrame;
+ unsigned m_lastExecutedLine;
+ SourceID m_lastExecutedSourceID;
+
+ BreakpointID m_topBreakpointID;
+ BreakpointID m_pausingBreakpointID;
+ BreakpointIDToBreakpointMap m_breakpointIDToBreakpoint;
+ SourceIDToBreakpointsMap m_sourceIDToBreakpoints;
+
+ RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame;
+
+ ProfilingClient* m_profilingClient { nullptr };
+
+ friend class DebuggerPausedScope;
+ friend class TemporaryPausedState;
+ friend class LLIntOffsetsExtractor;
+};
} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerActivation.cpp b/Source/JavaScriptCore/debugger/DebuggerActivation.cpp
deleted file mode 100644
index eec2d6bd7..000000000
--- a/Source/JavaScriptCore/debugger/DebuggerActivation.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "DebuggerActivation.h"
-
-#include "JSActivation.h"
-#include "Operations.h"
-
-namespace JSC {
-
-ASSERT_HAS_TRIVIAL_DESTRUCTOR(DebuggerActivation);
-
-const ClassInfo DebuggerActivation::s_info = { "DebuggerActivation", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(DebuggerActivation) };
-
-DebuggerActivation::DebuggerActivation(VM& vm)
- : JSNonFinalObject(vm, vm.debuggerActivationStructure.get())
-{
-}
-
-void DebuggerActivation::finishCreation(VM& vm, JSObject* activation)
-{
- Base::finishCreation(vm);
- ASSERT(activation);
- ASSERT(activation->isActivationObject());
- m_activation.set(vm, this, jsCast<JSActivation*>(activation));
-}
-
-void DebuggerActivation::visitChildren(JSCell* cell, SlotVisitor& visitor)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(cell);
- ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
- COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
- ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
-
- JSObject::visitChildren(thisObject, visitor);
- visitor.append(&thisObject->m_activation);
-}
-
-String DebuggerActivation::className(const JSObject* object)
-{
- const DebuggerActivation* thisObject = jsCast<const DebuggerActivation*>(object);
- return thisObject->m_activation->methodTable()->className(thisObject->m_activation.get());
-}
-
-bool DebuggerActivation::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(cell);
- return thisObject->m_activation->methodTable()->getOwnPropertySlot(thisObject->m_activation.get(), exec, propertyName, slot);
-}
-
-void DebuggerActivation::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(cell);
- thisObject->m_activation->methodTable()->put(thisObject->m_activation.get(), exec, propertyName, value, slot);
-}
-
-void DebuggerActivation::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(object);
- thisObject->m_activation->methodTable()->putDirectVirtual(thisObject->m_activation.get(), exec, propertyName, value, attributes);
-}
-
-bool DebuggerActivation::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(cell);
- return thisObject->m_activation->methodTable()->deleteProperty(thisObject->m_activation.get(), exec, propertyName);
-}
-
-void DebuggerActivation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(object);
- thisObject->m_activation->methodTable()->getPropertyNames(thisObject->m_activation.get(), exec, propertyNames, mode);
-}
-
-bool DebuggerActivation::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(object);
- return thisObject->m_activation->methodTable()->getOwnPropertyDescriptor(thisObject->m_activation.get(), exec, propertyName, descriptor);
-}
-
-bool DebuggerActivation::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(object);
- return thisObject->m_activation->methodTable()->defineOwnProperty(thisObject->m_activation.get(), exec, propertyName, descriptor, shouldThrow);
-}
-
-} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerActivation.h b/Source/JavaScriptCore/debugger/DebuggerActivation.h
deleted file mode 100644
index a33d6ddb2..000000000
--- a/Source/JavaScriptCore/debugger/DebuggerActivation.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef DebuggerActivation_h
-#define DebuggerActivation_h
-
-#include "JSObject.h"
-
-namespace JSC {
-
- class DebuggerActivation : public JSNonFinalObject {
- public:
- typedef JSNonFinalObject Base;
-
- static DebuggerActivation* create(VM& vm, JSObject* object)
- {
- DebuggerActivation* activation = new (NotNull, allocateCell<DebuggerActivation>(vm.heap)) DebuggerActivation(vm);
- activation->finishCreation(vm, object);
- return activation;
- }
-
- static void visitChildren(JSCell*, SlotVisitor&);
- static String className(const JSObject*);
- static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
- static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
- static void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes);
- static bool deleteProperty(JSCell*, ExecState*, PropertyName);
- static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
- static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
- static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
-
- JS_EXPORTDATA static const ClassInfo s_info;
-
- static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
- {
- return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
- }
-
- protected:
- static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | JSObject::StructureFlags;
-
- JS_EXPORT_PRIVATE void finishCreation(VM&, JSObject* activation);
-
- private:
- JS_EXPORT_PRIVATE DebuggerActivation(VM&);
- WriteBarrier<JSActivation> m_activation;
- };
-
-} // namespace JSC
-
-#endif // DebuggerActivation_h
diff --git a/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
index a5d045cb9..f4794fd45 100644
--- a/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -29,81 +29,229 @@
#include "config.h"
#include "DebuggerCallFrame.h"
-#include "JSFunction.h"
#include "CodeBlock.h"
+#include "DebuggerEvalEnabler.h"
+#include "DebuggerScope.h"
#include "Interpreter.h"
-#include "Operations.h"
+#include "JSFunction.h"
+#include "JSLexicalEnvironment.h"
+#include "JSCInlines.h"
#include "Parser.h"
+#include "StackVisitor.h"
+#include "StrongInlines.h"
namespace JSC {
-String DebuggerCallFrame::functionName() const
+class LineAndColumnFunctor {
+public:
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ visitor->computeLineAndColumn(m_line, m_column);
+ return StackVisitor::Done;
+ }
+
+ unsigned line() const { return m_line; }
+ unsigned column() const { return m_column; }
+
+private:
+ unsigned m_line;
+ unsigned m_column;
+};
+
+class FindCallerMidStackFunctor {
+public:
+ FindCallerMidStackFunctor(CallFrame* callFrame)
+ : m_callFrame(callFrame)
+ , m_callerFrame(nullptr)
+ { }
+
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ if (visitor->callFrame() == m_callFrame) {
+ m_callerFrame = visitor->callerFrame();
+ return StackVisitor::Done;
+ }
+ return StackVisitor::Continue;
+ }
+
+ CallFrame* getCallerFrame() const { return m_callerFrame; }
+
+private:
+ CallFrame* m_callFrame;
+ CallFrame* m_callerFrame;
+};
+
+DebuggerCallFrame::DebuggerCallFrame(CallFrame* callFrame)
+ : m_callFrame(callFrame)
{
- if (!m_callFrame->codeBlock())
- return String();
+ m_position = positionForCallFrame(m_callFrame);
+}
- if (!m_callFrame->callee())
- return String();
+RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return 0;
- JSObject* function = m_callFrame->callee();
- if (!function || !function->inherits(&JSFunction::s_info))
- return String();
- return jsCast<JSFunction*>(function)->name(m_callFrame);
+ if (m_caller)
+ return m_caller;
+
+ FindCallerMidStackFunctor functor(m_callFrame);
+ m_callFrame->vm().topCallFrame->iterate(functor);
+
+ CallFrame* callerFrame = functor.getCallerFrame();
+ if (!callerFrame)
+ return nullptr;
+
+ m_caller = DebuggerCallFrame::create(callerFrame);
+ return m_caller;
}
-
-String DebuggerCallFrame::calculatedFunctionName() const
+
+JSC::JSGlobalObject* DebuggerCallFrame::vmEntryGlobalObject() const
{
- if (!m_callFrame->codeBlock())
- return String();
+ ASSERT(isValid());
+ if (!isValid())
+ return 0;
+ return m_callFrame->vmEntryGlobalObject();
+}
- JSObject* function = m_callFrame->callee();
+SourceID DebuggerCallFrame::sourceID() const
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return noSourceID;
+ return sourceIDForCallFrame(m_callFrame);
+}
- if (!function)
+String DebuggerCallFrame::functionName() const
+{
+ ASSERT(isValid());
+ if (!isValid())
return String();
+ return m_callFrame->friendlyFunctionName();
+}
- return getCalculatedDisplayName(m_callFrame, function);
+DebuggerScope* DebuggerCallFrame::scope()
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return 0;
+
+ if (!m_scope) {
+ VM& vm = m_callFrame->vm();
+ JSScope* scope;
+ CodeBlock* codeBlock = m_callFrame->codeBlock();
+ if (codeBlock && codeBlock->scopeRegister().isValid())
+ scope = m_callFrame->scope(codeBlock->scopeRegister().offset());
+ else if (JSCallee* callee = jsDynamicCast<JSCallee*>(m_callFrame->callee()))
+ scope = callee->scope();
+ else
+ scope = m_callFrame->lexicalGlobalObject();
+
+ m_scope.set(vm, DebuggerScope::create(vm, scope));
+ }
+ return m_scope.get();
}
DebuggerCallFrame::Type DebuggerCallFrame::type() const
{
- if (m_callFrame->callee())
+ ASSERT(isValid());
+ if (!isValid())
+ return ProgramType;
+
+ if (jsDynamicCast<JSFunction*>(m_callFrame->callee()))
return FunctionType;
return ProgramType;
}
-JSObject* DebuggerCallFrame::thisObject() const
+JSValue DebuggerCallFrame::thisValue() const
{
- CodeBlock* codeBlock = m_callFrame->codeBlock();
- if (!codeBlock)
- return 0;
-
- JSValue thisValue = m_callFrame->uncheckedR(codeBlock->thisRegister()).jsValue();
- if (!thisValue.isObject())
- return 0;
-
- return asObject(thisValue);
+ ASSERT(isValid());
+ return thisValueForCallFrame(m_callFrame);
}
-JSValue DebuggerCallFrame::evaluate(const String& script, JSValue& exception) const
+// Evaluate some JavaScript code in the scope of this frame.
+JSValue DebuggerCallFrame::evaluate(const String& script, NakedPtr<Exception>& exception)
{
- if (!m_callFrame->codeBlock())
+ ASSERT(isValid());
+ CallFrame* callFrame = m_callFrame;
+ if (!callFrame)
+ return jsNull();
+
+ JSLockHolder lock(callFrame);
+
+ if (!callFrame->codeBlock())
return JSValue();
- VM& vm = m_callFrame->vm();
- EvalExecutable* eval = EvalExecutable::create(m_callFrame, m_callFrame->codeBlock()->unlinkedCodeBlock()->codeCacheForEval(), makeSource(script), m_callFrame->codeBlock()->isStrictMode());
- if (vm.exception) {
- exception = vm.exception;
- vm.exception = JSValue();
+ DebuggerEvalEnabler evalEnabler(callFrame);
+ VM& vm = callFrame->vm();
+ auto& codeBlock = *callFrame->codeBlock();
+ ThisTDZMode thisTDZMode = codeBlock.unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded;
+
+ VariableEnvironment variablesUnderTDZ;
+ JSScope::collectVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ);
+
+ EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), codeBlock.isStrictMode(), thisTDZMode, codeBlock.unlinkedCodeBlock()->derivedContextType(), codeBlock.unlinkedCodeBlock()->isArrowFunction(), &variablesUnderTDZ);
+ if (vm.exception()) {
+ exception = vm.exception();
+ vm.clearException();
+ return jsUndefined();
}
- JSValue result = vm.interpreter->execute(eval, m_callFrame, thisObject(), m_callFrame->scope());
- if (vm.exception) {
- exception = vm.exception;
- vm.exception = JSValue();
+ JSValue thisValue = thisValueForCallFrame(callFrame);
+ JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, scope()->jsScope());
+ if (vm.exception()) {
+ exception = vm.exception();
+ vm.clearException();
}
ASSERT(result);
return result;
}
+void DebuggerCallFrame::invalidate()
+{
+ RefPtr<DebuggerCallFrame> frame = this;
+ while (frame) {
+ frame->m_callFrame = nullptr;
+ if (frame->m_scope) {
+ frame->m_scope->invalidateChain();
+ frame->m_scope.clear();
+ }
+ frame = frame->m_caller.release();
+ }
+}
+
+TextPosition DebuggerCallFrame::positionForCallFrame(CallFrame* callFrame)
+{
+ if (!callFrame)
+ return TextPosition();
+
+ LineAndColumnFunctor functor;
+ callFrame->iterate(functor);
+ return TextPosition(OrdinalNumber::fromOneBasedInt(functor.line()), OrdinalNumber::fromOneBasedInt(functor.column()));
+}
+
+SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* callFrame)
+{
+ ASSERT(callFrame);
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ if (!codeBlock)
+ return noSourceID;
+ return codeBlock->ownerScriptExecutable()->sourceID();
+}
+
+JSValue DebuggerCallFrame::thisValueForCallFrame(CallFrame* callFrame)
+{
+ if (!callFrame)
+ return jsNull();
+
+ ECMAMode ecmaMode = NotStrictMode;
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ if (codeBlock && codeBlock->isStrictMode())
+ ecmaMode = StrictMode;
+ JSValue thisValue = callFrame->thisValue().toThis(callFrame, ecmaMode);
+ return thisValue;
+}
+
} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerCallFrame.h b/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
index 66585a637..b583455bc 100644
--- a/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -29,39 +29,65 @@
#ifndef DebuggerCallFrame_h
#define DebuggerCallFrame_h
-#include "CallFrame.h"
+#include "DebuggerPrimitives.h"
+#include "Strong.h"
+#include <wtf/NakedPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/TextPosition.h>
namespace JSC {
-
- class DebuggerCallFrame {
- public:
- enum Type { ProgramType, FunctionType };
- DebuggerCallFrame(CallFrame* callFrame)
- : m_callFrame(callFrame)
- {
- }
+class DebuggerScope;
+class Exception;
+class ExecState;
+typedef ExecState CallFrame;
- DebuggerCallFrame(CallFrame* callFrame, JSValue exception)
- : m_callFrame(callFrame)
- , m_exception(exception)
- {
- }
+class DebuggerCallFrame : public RefCounted<DebuggerCallFrame> {
+public:
+ enum Type { ProgramType, FunctionType };
- CallFrame* callFrame() const { return m_callFrame; }
- JSGlobalObject* dynamicGlobalObject() const { return m_callFrame->dynamicGlobalObject(); }
- JSScope* scope() const { return m_callFrame->scope(); }
- JS_EXPORT_PRIVATE String functionName() const;
- JS_EXPORT_PRIVATE String calculatedFunctionName() const;
- JS_EXPORT_PRIVATE Type type() const;
- JS_EXPORT_PRIVATE JSObject* thisObject() const;
- JS_EXPORT_PRIVATE JSValue evaluate(const String&, JSValue& exception) const;
- JSValue exception() const { return m_exception; }
+ static Ref<DebuggerCallFrame> create(CallFrame* callFrame)
+ {
+ return adoptRef(*new DebuggerCallFrame(callFrame));
+ }
- private:
- CallFrame* m_callFrame;
- JSValue m_exception;
- };
+ JS_EXPORT_PRIVATE explicit DebuggerCallFrame(CallFrame*);
+
+ JS_EXPORT_PRIVATE RefPtr<DebuggerCallFrame> callerFrame();
+ ExecState* exec() const { return m_callFrame; }
+ JS_EXPORT_PRIVATE SourceID sourceID() const;
+
+ // line and column are in base 0 e.g. the first line is line 0.
+ int line() const { return m_position.m_line.zeroBasedInt(); }
+ int column() const { return m_position.m_column.zeroBasedInt(); }
+ const TextPosition& position() const { return m_position; }
+
+ JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject() const;
+ JS_EXPORT_PRIVATE DebuggerScope* scope();
+ JS_EXPORT_PRIVATE String functionName() const;
+ JS_EXPORT_PRIVATE Type type() const;
+ JS_EXPORT_PRIVATE JSValue thisValue() const;
+ JSValue evaluate(const String&, NakedPtr<Exception>&);
+
+ bool isValid() const { return !!m_callFrame; }
+ JS_EXPORT_PRIVATE void invalidate();
+
+ // The following are only public for the Debugger's use only. They will be
+ // made private soon. Other clients should not use these.
+
+ JS_EXPORT_PRIVATE static TextPosition positionForCallFrame(CallFrame*);
+ JS_EXPORT_PRIVATE static SourceID sourceIDForCallFrame(CallFrame*);
+ static JSValue thisValueForCallFrame(CallFrame*);
+
+private:
+ CallFrame* m_callFrame;
+ RefPtr<DebuggerCallFrame> m_caller;
+ TextPosition m_position;
+ // The DebuggerPausedScope is responsible for calling invalidate() which,
+ // in turn, will clear this strong ref.
+ Strong<DebuggerScope> m_scope;
+};
} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h b/Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h
new file mode 100644
index 000000000..bb75b1f15
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DebuggerEvalEnabler_h
+#define DebuggerEvalEnabler_h
+
+#include "CallFrame.h"
+#include "JSGlobalObject.h"
+
+namespace JSC {
+
+class DebuggerEvalEnabler {
+public:
+ explicit DebuggerEvalEnabler(const ExecState* exec)
+ : m_exec(exec)
+ , m_evalWasDisabled(false)
+ {
+ if (exec) {
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ m_evalWasDisabled = !globalObject->evalEnabled();
+ if (m_evalWasDisabled)
+ globalObject->setEvalEnabled(true, globalObject->evalDisabledErrorMessage());
+ }
+ }
+
+ ~DebuggerEvalEnabler()
+ {
+ if (m_evalWasDisabled) {
+ JSGlobalObject* globalObject = m_exec->lexicalGlobalObject();
+ globalObject->setEvalEnabled(false, globalObject->evalDisabledErrorMessage());
+ }
+ }
+
+private:
+ const ExecState* m_exec;
+ bool m_evalWasDisabled;
+};
+
+} // namespace JSC
+
+#endif // DebuggerEvalEnabler_h
diff --git a/Source/JavaScriptCore/debugger/DebuggerPrimitives.h b/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
new file mode 100644
index 000000000..ab7301b5c
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DebuggerPrimitives_h
+#define DebuggerPrimitives_h
+
+#include <stddef.h>
+
+namespace JSC {
+
+typedef size_t SourceID;
+static const SourceID noSourceID = 0;
+
+typedef size_t BreakpointID;
+static const BreakpointID noBreakpointID = 0;
+
+} // namespace JSC
+
+#endif // DebuggerPrimitives_h
diff --git a/Source/JavaScriptCore/debugger/DebuggerScope.cpp b/Source/JavaScriptCore/debugger/DebuggerScope.cpp
new file mode 100644
index 000000000..c3efbd56d
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerScope.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2008-2009, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DebuggerScope.h"
+
+#include "JSLexicalEnvironment.h"
+#include "JSCInlines.h"
+#include "JSWithScope.h"
+
+namespace JSC {
+
+STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DebuggerScope);
+
+const ClassInfo DebuggerScope::s_info = { "DebuggerScope", &Base::s_info, 0, CREATE_METHOD_TABLE(DebuggerScope) };
+
+DebuggerScope::DebuggerScope(VM& vm, JSScope* scope)
+ : JSNonFinalObject(vm, scope->globalObject()->debuggerScopeStructure())
+{
+ ASSERT(scope);
+ m_scope.set(vm, this, scope);
+}
+
+void DebuggerScope::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+}
+
+void DebuggerScope::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+ DebuggerScope* thisObject = jsCast<DebuggerScope*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ JSObject::visitChildren(thisObject, visitor);
+ visitor.append(&thisObject->m_scope);
+ visitor.append(&thisObject->m_next);
+}
+
+String DebuggerScope::className(const JSObject* object)
+{
+ const DebuggerScope* scope = jsCast<const DebuggerScope*>(object);
+ ASSERT(scope->isValid());
+ if (!scope->isValid())
+ return String();
+ JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
+ return thisObject->methodTable()->className(thisObject);
+}
+
+bool DebuggerScope::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+ DebuggerScope* scope = jsCast<DebuggerScope*>(object);
+ ASSERT(scope->isValid());
+ if (!scope->isValid())
+ return false;
+ JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
+ slot.setThisValue(JSValue(thisObject));
+
+ // By default, JSObject::getPropertySlot() will look in the DebuggerScope's prototype
+ // chain and not the wrapped scope, and JSObject::getPropertySlot() cannot be overridden
+ // to behave differently for the DebuggerScope.
+ //
+ // Instead, we'll treat all properties in the wrapped scope and its prototype chain as
+ // the own properties of the DebuggerScope. This is fine because the WebInspector
+ // does not presently need to distinguish between what's owned at each level in the
+ // prototype chain. Hence, we'll invoke getPropertySlot() on the wrapped scope here
+ // instead of getOwnPropertySlot().
+ bool result = thisObject->getPropertySlot(exec, propertyName, slot);
+ if (result && slot.isValue() && slot.getValue(exec, propertyName) == jsTDZValue()) {
+ // FIXME:
+ // We hit a scope property that has the TDZ empty value.
+ // Currently, we just lie to the inspector and claim that this property is undefined.
+ // This is not ideal and we should fix it.
+ // https://bugs.webkit.org/show_bug.cgi?id=144977
+ slot.setValue(slot.slotBase(), DontEnum, jsUndefined());
+ return true;
+ }
+ return result;
+}
+
+void DebuggerScope::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+{
+ DebuggerScope* scope = jsCast<DebuggerScope*>(cell);
+ ASSERT(scope->isValid());
+ if (!scope->isValid())
+ return;
+ JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
+ slot.setThisValue(JSValue(thisObject));
+ thisObject->methodTable()->put(thisObject, exec, propertyName, value, slot);
+}
+
+bool DebuggerScope::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
+{
+ DebuggerScope* scope = jsCast<DebuggerScope*>(cell);
+ ASSERT(scope->isValid());
+ if (!scope->isValid())
+ return false;
+ JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
+ return thisObject->methodTable()->deleteProperty(thisObject, exec, propertyName);
+}
+
+void DebuggerScope::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ DebuggerScope* scope = jsCast<DebuggerScope*>(object);
+ ASSERT(scope->isValid());
+ if (!scope->isValid())
+ return;
+ JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
+ thisObject->methodTable()->getPropertyNames(thisObject, exec, propertyNames, mode);
+}
+
+bool DebuggerScope::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
+{
+ DebuggerScope* scope = jsCast<DebuggerScope*>(object);
+ ASSERT(scope->isValid());
+ if (!scope->isValid())
+ return false;
+ JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
+ return thisObject->methodTable()->defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
+}
+
+DebuggerScope* DebuggerScope::next()
+{
+ ASSERT(isValid());
+ if (!m_next && m_scope->next()) {
+ VM& vm = *m_scope->vm();
+ DebuggerScope* nextScope = create(vm, m_scope->next());
+ m_next.set(vm, this, nextScope);
+ }
+ return m_next.get();
+}
+
+void DebuggerScope::invalidateChain()
+{
+ if (!isValid())
+ return;
+
+ DebuggerScope* scope = this;
+ while (scope) {
+ DebuggerScope* nextScope = scope->m_next.get();
+ scope->m_next.clear();
+ scope->m_scope.clear(); // This also marks this scope as invalid.
+ scope = nextScope;
+ }
+}
+
+bool DebuggerScope::isCatchScope() const
+{
+ return m_scope->isCatchScope();
+}
+
+bool DebuggerScope::isFunctionNameScope() const
+{
+ return m_scope->isFunctionNameScopeObject();
+}
+
+bool DebuggerScope::isWithScope() const
+{
+ return m_scope->isWithScope();
+}
+
+bool DebuggerScope::isGlobalScope() const
+{
+ return m_scope->isGlobalObject();
+}
+
+bool DebuggerScope::isGlobalLexicalEnvironment() const
+{
+ return m_scope->isGlobalLexicalEnvironment();
+}
+
+bool DebuggerScope::isClosureScope() const
+{
+ // In the current debugger implementation, every function or eval will create an
+ // lexical environment object. Hence, a lexical environment object implies a
+ // function or eval scope.
+ return m_scope->isVarScope() || m_scope->isLexicalScope();
+}
+
+bool DebuggerScope::isNestedLexicalScope() const
+{
+ return m_scope->isNestedLexicalScope();
+}
+
+JSValue DebuggerScope::caughtValue(ExecState* exec) const
+{
+ ASSERT(isCatchScope());
+ JSLexicalEnvironment* catchEnvironment = jsCast<JSLexicalEnvironment*>(m_scope.get());
+ SymbolTable* catchSymbolTable = catchEnvironment->symbolTable();
+ RELEASE_ASSERT(catchSymbolTable->size() == 1);
+ PropertyName errorName(catchSymbolTable->begin(catchSymbolTable->m_lock)->key.get());
+ PropertySlot slot(m_scope.get(), PropertySlot::InternalMethodType::Get);
+ bool success = catchEnvironment->getOwnPropertySlot(catchEnvironment, exec, errorName, slot);
+ RELEASE_ASSERT(success && slot.isValue());
+ return slot.getValue(exec, errorName);
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerScope.h b/Source/JavaScriptCore/debugger/DebuggerScope.h
new file mode 100644
index 000000000..fef60c899
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerScope.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008-2009, 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DebuggerScope_h
+#define DebuggerScope_h
+
+#include "JSObject.h"
+
+namespace JSC {
+
+class DebuggerCallFrame;
+class JSScope;
+
+class DebuggerScope : public JSNonFinalObject {
+public:
+ typedef JSNonFinalObject Base;
+ static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
+
+ static DebuggerScope* create(VM& vm, JSScope* scope)
+ {
+ DebuggerScope* debuggerScope = new (NotNull, allocateCell<DebuggerScope>(vm.heap)) DebuggerScope(vm, scope);
+ debuggerScope->finishCreation(vm);
+ return debuggerScope;
+ }
+
+ static void visitChildren(JSCell*, SlotVisitor&);
+ static String className(const JSObject*);
+ static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
+ static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
+ static bool deleteProperty(JSCell*, ExecState*, PropertyName);
+ static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+ static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
+
+ DECLARE_EXPORT_INFO;
+
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject)
+ {
+ return Structure::create(vm, globalObject, jsNull(), TypeInfo(ObjectType, StructureFlags), info());
+ }
+
+ class iterator {
+ public:
+ iterator(DebuggerScope* node)
+ : m_node(node)
+ {
+ }
+
+ DebuggerScope* get() { return m_node; }
+ iterator& operator++() { m_node = m_node->next(); return *this; }
+ // postfix ++ intentionally omitted
+
+ bool operator==(const iterator& other) const { return m_node == other.m_node; }
+ bool operator!=(const iterator& other) const { return m_node != other.m_node; }
+
+ private:
+ DebuggerScope* m_node;
+ };
+
+ iterator begin();
+ iterator end();
+ DebuggerScope* next();
+
+ void invalidateChain();
+ bool isValid() const { return !!m_scope; }
+
+ bool isCatchScope() const;
+ bool isFunctionNameScope() const;
+ bool isWithScope() const;
+ bool isGlobalScope() const;
+ bool isClosureScope() const;
+ bool isGlobalLexicalEnvironment() const;
+ bool isNestedLexicalScope() const;
+
+ JSValue caughtValue(ExecState*) const;
+
+private:
+ JS_EXPORT_PRIVATE DebuggerScope(VM&, JSScope*);
+ JS_EXPORT_PRIVATE void finishCreation(VM&);
+
+ JSScope* jsScope() const { return m_scope.get(); }
+
+ WriteBarrier<JSScope> m_scope;
+ WriteBarrier<DebuggerScope> m_next;
+
+ friend class DebuggerCallFrame;
+};
+
+inline DebuggerScope::iterator DebuggerScope::begin()
+{
+ return iterator(this);
+}
+
+inline DebuggerScope::iterator DebuggerScope::end()
+{
+ return iterator(0);
+}
+
+} // namespace JSC
+
+#endif // DebuggerScope_h
diff --git a/Source/JavaScriptCore/debugger/ScriptProfilingScope.h b/Source/JavaScriptCore/debugger/ScriptProfilingScope.h
new file mode 100644
index 000000000..ba6bdfc51
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/ScriptProfilingScope.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ScriptProfilingScope_h
+#define ScriptProfilingScope_h
+
+#include "Debugger.h"
+#include "JSGlobalObject.h"
+#include <wtf/Optional.h>
+
+namespace JSC {
+
+class ScriptProfilingScope {
+public:
+ ScriptProfilingScope(JSGlobalObject* globalObject, ProfilingReason reason)
+ : m_globalObject(globalObject)
+ , m_reason(reason)
+ {
+ if (shouldStartProfile())
+ m_startTime = m_globalObject->debugger()->willEvaluateScript();
+ }
+
+ ~ScriptProfilingScope()
+ {
+ if (shouldEndProfile())
+ m_globalObject->debugger()->didEvaluateScript(m_startTime.value(), m_reason);
+ }
+
+private:
+ bool shouldStartProfile() const
+ {
+ if (!m_globalObject)
+ return false;
+
+ if (!m_globalObject->hasDebugger())
+ return false;
+
+ if (!m_globalObject->debugger()->hasProfilingClient())
+ return false;
+
+ if (m_globalObject->debugger()->isAlreadyProfiling())
+ return false;
+
+ return true;
+ }
+
+ bool shouldEndProfile() const
+ {
+ // Did not start a profile.
+ if (!m_startTime)
+ return false;
+
+ // Debugger may have been removed.
+ if (!m_globalObject->hasDebugger())
+ return false;
+
+ // Profiling Client may have been removed.
+ if (!m_globalObject->debugger()->hasProfilingClient())
+ return false;
+
+ return true;
+ }
+
+ JSGlobalObject* m_globalObject { nullptr };
+ Optional<double> m_startTime;
+ ProfilingReason m_reason;
+};
+
+}
+
+#endif // ScriptProfilingScope_h