summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/debugger
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
commite15dd966d523731101f70ccf768bba12435a0208 (patch)
treeae9cb828a24ded2585a41af3f21411523b47897d /Source/JavaScriptCore/debugger
downloadWebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Source/JavaScriptCore/debugger')
-rw-r--r--Source/JavaScriptCore/debugger/Breakpoint.h96
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.cpp808
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.h222
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp261
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.h94
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h63
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerPrimitives.h41
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerScope.cpp208
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerScope.h120
9 files changed, 1913 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/debugger/Breakpoint.h b/Source/JavaScriptCore/debugger/Breakpoint.h
new file mode 100644
index 000000000..8518ce469
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/Breakpoint.h
@@ -0,0 +1,96 @@
+/*
+ * 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()
+ : id(noBreakpointID)
+ , sourceID(noSourceID)
+ , line(0)
+ , column(0)
+ , autoContinue(false)
+ {
+ }
+
+ Breakpoint(SourceID sourceID, unsigned line, unsigned column, const String& condition, bool autoContinue)
+ : id(noBreakpointID)
+ , sourceID(sourceID)
+ , line(line)
+ , column(column)
+ , condition(condition)
+ , autoContinue(autoContinue)
+ {
+ }
+
+ Breakpoint(const Breakpoint& other)
+ : id(other.id)
+ , sourceID(other.sourceID)
+ , line(other.line)
+ , column(other.column)
+ , condition(other.condition)
+ , autoContinue(other.autoContinue)
+ {
+ }
+
+ BreakpointID id;
+ SourceID sourceID;
+ unsigned line;
+ unsigned column;
+ String condition;
+ bool autoContinue;
+
+ 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
new file mode 100644
index 000000000..4d7871ff8
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/Debugger.cpp
@@ -0,0 +1,808 @@
+/*
+ * 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)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#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 "JSCInlines.h"
+#include "Parser.h"
+#include "Protect.h"
+#include "VMEntryScope.h"
+
+namespace {
+
+using namespace JSC;
+
+class Recompiler : public MarkedBlock::VoidFunctor {
+public:
+ Recompiler(JSC::Debugger*);
+ ~Recompiler();
+ IterationStatus operator()(JSCell*);
+
+private:
+ typedef HashSet<FunctionExecutable*> FunctionExecutableSet;
+ typedef HashMap<SourceProvider*, ExecState*> SourceProviderMap;
+
+ void visit(JSCell*);
+
+ JSC::Debugger* m_debugger;
+ FunctionExecutableSet m_functionExecutables;
+ SourceProviderMap m_sourceProviders;
+};
+
+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());
+}
+
+inline void Recompiler::visit(JSCell* cell)
+{
+ if (!cell->inherits(JSFunction::info()))
+ return;
+
+ JSFunction* function = jsCast<JSFunction*>(cell);
+ if (function->executable()->isHostFunction())
+ return;
+
+ FunctionExecutable* executable = function->jsExecutable();
+
+ // 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->clearCode();
+ executable->clearUnlinkedCodeForRecompilation();
+ if (m_debugger == function->scope()->globalObject()->debugger())
+ m_sourceProviders.add(executable->source().provider(), exec);
+}
+
+inline IterationStatus Recompiler::operator()(JSCell* cell)
+{
+ visit(cell);
+ 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:
+ 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;
+};
+
+template<typename Functor>
+void Debugger::forEachCodeBlock(Functor& functor)
+{
+ m_vm->prepareToDeleteCode();
+ m_vm->heap.forEachCodeBlock(functor);
+}
+
+Debugger::Debugger(bool isInWorkerThread)
+ : m_vm(nullptr)
+ , m_pauseOnExceptionsState(DontPauseOnExceptions)
+ , m_pauseOnNextStatement(false)
+ , m_isPaused(false)
+ , m_breakpointsActivated(true)
+ , m_hasHandlerForExceptionCallback(false)
+ , m_isInWorkerThread(isInWorkerThread)
+ , 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());
+ if (!m_vm)
+ m_vm = &globalObject->vm();
+ else
+ ASSERT(m_vm == &globalObject->vm());
+ globalObject->setDebugger(this);
+ m_globalObjects.add(globalObject);
+}
+
+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);
+ if (!m_globalObjects.size())
+ m_vm = nullptr;
+}
+
+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:
+ Debugger* m_debugger;
+ SteppingMode m_mode;
+};
+
+void Debugger::setSteppingMode(SteppingMode mode)
+{
+ if (mode == m_steppingMode || !m_vm)
+ return;
+
+ m_vm->prepareToDeleteCode();
+
+ m_steppingMode = mode;
+ SetSteppingModeFunctor functor(this, mode);
+ m_vm->heap.forEachCodeBlock(functor);
+}
+
+void Debugger::registerCodeBlock(CodeBlock* codeBlock)
+{
+ // FIXME: We should never have to jettison a code block (due to pending breakpoints
+ // or stepping mode) that is being registered. operationOptimize() should have
+ // prevented the optimizing of such code blocks in the first place. Find a way to
+ // express this with greater clarity in the code. See <https://webkit.org/b131771>.
+ applyBreakpoints(codeBlock);
+ if (isStepping())
+ codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
+}
+
+void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot)
+{
+ ScriptExecutable* executable = codeBlock->ownerExecutable();
+
+ SourceID sourceID = static_cast<SourceID>(executable->sourceID());
+ if (breakpoint.sourceID != sourceID)
+ return;
+
+ 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;
+
+ 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)
+{
+ if (!m_vm)
+ return;
+ ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot);
+ forEachCodeBlock(functor);
+}
+
+void Debugger::recompileAllJSFunctions(VM* vm)
+{
+ // 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) {
+ auto listener = [] (VM& vm, JSGlobalObject* globalObject)
+ {
+ if (Debugger* debugger = globalObject->debugger())
+ debugger->recompileAllJSFunctions(&vm);
+ };
+
+ vm->entryScope->setEntryScopeDidPopListener(this, listener);
+ return;
+ }
+
+ vm->prepareToDeleteCode();
+
+ Recompiler recompiler(this);
+ HeapIterationScope iterationScope(vm->heap);
+ vm->heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
+}
+
+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;
+
+ if (hitBreakpoint)
+ *hitBreakpoint = *breakpoint;
+
+ 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_topBreakpointID = noBreakpointID;
+ m_breakpointIDToBreakpoint.clear();
+ m_sourceIDToBreakpoints.clear();
+
+ if (!m_vm)
+ return;
+ ClearCodeBlockDebuggerRequestsFunctor functor(this);
+ forEachCodeBlock(functor);
+}
+
+class Debugger::ClearDebuggerRequestsFunctor {
+public:
+ ClearDebuggerRequestsFunctor(JSGlobalObject* globalObject)
+ : m_globalObject(globalObject)
+ {
+ }
+
+ bool operator()(CodeBlock* codeBlock)
+ {
+ if (codeBlock->hasDebuggerRequests() && m_globalObject == codeBlock->globalObject())
+ codeBlock->clearDebuggerRequests();
+ return false;
+ }
+
+private:
+ JSGlobalObject* m_globalObject;
+};
+
+void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
+{
+ ASSERT(m_vm);
+ ClearDebuggerRequestsFunctor functor(globalObject);
+ forEachCodeBlock(functor);
+}
+
+void Debugger::setBreakpointsActivated(bool activated)
+{
+ m_breakpointsActivated = activated;
+}
+
+void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
+{
+ m_pauseOnExceptionsState = pause;
+}
+
+void Debugger::setPauseOnNextStatement(bool pause)
+{
+ m_pauseOnNextStatement = pause;
+ if (pause)
+ setSteppingMode(SteppingModeEnabled);
+}
+
+void Debugger::breakProgram()
+{
+ if (m_isPaused)
+ return;
+
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ m_currentCallFrame = m_vm->topCallFrame;
+ ASSERT(m_currentCallFrame);
+ pauseIfNeeded(m_currentCallFrame);
+}
+
+void Debugger::continueProgram()
+{
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnNextStatement = false;
+ notifyDoneProcessingDebuggerEvents();
+}
+
+void Debugger::stepIntoStatement()
+{
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnNextStatement = true;
+ setSteppingMode(SteppingModeEnabled);
+ notifyDoneProcessingDebuggerEvents();
+}
+
+void Debugger::stepOverStatement()
+{
+ if (!m_isPaused)
+ return;
+
+ m_pauseOnCallFrame = m_currentCallFrame;
+ notifyDoneProcessingDebuggerEvents();
+}
+
+void Debugger::stepOutOfFunction()
+{
+ if (!m_isPaused)
+ return;
+
+ 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;
+
+ 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);
+ }
+
+ 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::atStatement(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAtStatement);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+}
+
+void Debugger::callEvent(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAfterCall);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+}
+
+void Debugger::returnEvent(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedBeforeReturn);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+
+ // detach may have been called during pauseIfNeeded
+ if (!m_currentCallFrame)
+ return;
+
+ // Treat stepping over a return statement like stepping out.
+ if (m_currentCallFrame == m_pauseOnCallFrame) {
+ VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+ }
+
+ VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
+ m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+}
+
+void Debugger::willExecuteProgram(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAtStartOfProgram);
+ // FIXME: This check for whether we're debugging a worker thread is a workaround
+ // for https://bugs.webkit.org/show_bug.cgi?id=102637. Remove it when we rework
+ // the debugger implementation to not require callbacks.
+ if (!m_isInWorkerThread)
+ updateCallFrameAndPauseIfNeeded(callFrame);
+ else if (isStepping())
+ updateCallFrame(callFrame);
+}
+
+void Debugger::didExecuteProgram(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ PauseReasonDeclaration reason(*this, PausedAtEndOfProgram);
+ updateCallFrameAndPauseIfNeeded(callFrame);
+
+ // Treat stepping over the end of a program like stepping out.
+ if (!m_currentCallFrame)
+ return;
+ if (m_currentCallFrame == m_pauseOnCallFrame) {
+ VMEntryFrame* topVMEntryFrame = m_vm->topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+ if (!m_currentCallFrame)
+ return;
+ }
+ 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
new file mode 100644
index 000000000..d70f3b76f
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/Debugger.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ * Copyright (C) 2001 Peter Kelly (pmk@post.com)
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef Debugger_h
+#define Debugger_h
+
+#include "Breakpoint.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 CodeBlock;
+class Exception;
+class ExecState;
+class JSGlobalObject;
+class SourceProvider;
+class VM;
+
+typedef ExecState CallFrame;
+
+class JS_EXPORT_PRIVATE Debugger {
+public:
+ Debugger(bool isInWorkerThread = false);
+ virtual ~Debugger();
+
+ 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; }
+
+ 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*);
+
+ void recompileAllJSFunctions(VM*);
+
+ void registerCodeBlock(CodeBlock*);
+
+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() { }
+
+private:
+ typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap;
+
+ 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;
+
+ class PauseReasonDeclaration {
+ public:
+ PauseReasonDeclaration(Debugger& debugger, ReasonForPause reason)
+ : m_debugger(debugger)
+ {
+ m_debugger.m_reasonForPause = reason;
+ }
+
+ ~PauseReasonDeclaration()
+ {
+ m_debugger.m_reasonForPause = NotPaused;
+ }
+ private:
+ Debugger& m_debugger;
+ };
+
+ 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*);
+
+ template<typename Functor> inline void forEachCodeBlock(Functor&);
+
+ 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_isInWorkerThread : 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;
+
+ friend class DebuggerPausedScope;
+ friend class TemporaryPausedState;
+ friend class LLIntOffsetsExtractor;
+};
+
+} // namespace JSC
+
+#endif // Debugger_h
diff --git a/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
new file mode 100644
index 000000000..bf093e89b
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
+ */
+
+#include "config.h"
+#include "DebuggerCallFrame.h"
+
+#include "CodeBlock.h"
+#include "DebuggerEvalEnabler.h"
+#include "DebuggerScope.h"
+#include "Interpreter.h"
+#include "JSFunction.h"
+#include "JSLexicalEnvironment.h"
+#include "JSCInlines.h"
+#include "Parser.h"
+#include "StackVisitor.h"
+#include "StrongInlines.h"
+
+namespace JSC {
+
+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)
+{
+ m_position = positionForCallFrame(m_callFrame);
+}
+
+RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return 0;
+
+ 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;
+}
+
+JSC::JSGlobalObject* DebuggerCallFrame::vmEntryGlobalObject() const
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return 0;
+ return m_callFrame->vmEntryGlobalObject();
+}
+
+SourceID DebuggerCallFrame::sourceID() const
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return noSourceID;
+ return sourceIDForCallFrame(m_callFrame);
+}
+
+String DebuggerCallFrame::functionName() const
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return String();
+ JSFunction* function = jsDynamicCast<JSFunction*>(m_callFrame->callee());
+ if (!function)
+ return String();
+
+ 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
+{
+ ASSERT(isValid());
+ if (!isValid())
+ return ProgramType;
+
+ if (jsDynamicCast<JSFunction*>(m_callFrame->callee()))
+ return FunctionType;
+
+ return ProgramType;
+}
+
+JSValue DebuggerCallFrame::thisValue() const
+{
+ ASSERT(isValid());
+ return thisValueForCallFrame(m_callFrame);
+}
+
+// Evaluate some JavaScript code in the scope of this frame.
+JSValue DebuggerCallFrame::evaluate(const String& script, NakedPtr<Exception>& exception)
+{
+ ASSERT(isValid());
+ CallFrame* callFrame = m_callFrame;
+ if (!callFrame)
+ return jsNull();
+
+ JSLockHolder lock(callFrame);
+
+ if (!callFrame->codeBlock())
+ return 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, &variablesUnderTDZ);
+ if (vm.exception()) {
+ exception = vm.exception();
+ vm.clearException();
+ return jsUndefined();
+ }
+
+ 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->ownerExecutable()->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
new file mode 100644
index 000000000..aa3cca52b
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
@@ -0,0 +1,94 @@
+/*
+ * 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
+ * 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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 DebuggerCallFrame_h
+#define DebuggerCallFrame_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 DebuggerScope;
+class Exception;
+class ExecState;
+typedef ExecState CallFrame;
+
+class DebuggerCallFrame : public RefCounted<DebuggerCallFrame> {
+public:
+ enum Type { ProgramType, FunctionType };
+
+ static Ref<DebuggerCallFrame> create(CallFrame* callFrame)
+ {
+ return adoptRef(*new DebuggerCallFrame(callFrame));
+ }
+
+ 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(); }
+ JS_EXPORT_PRIVATE 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
+
+#endif // DebuggerCallFrame_h
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..c6eba4e93
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerScope.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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::isFunctionOrEvalScope() 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->isActivationObject() && !isCatchScope();
+}
+
+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());
+ 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..e1edb9248
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerScope.h
@@ -0,0 +1,120 @@
+/*
+ * 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 isFunctionOrEvalScope() 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