summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/debugger
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-05-24 08:28:08 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-05-24 08:28:08 +0000
commita4e969f4965059196ca948db781e52f7cfebf19e (patch)
tree6ca352808c8fdc52006a0f33f6ae3c593b23867d /Source/JavaScriptCore/debugger
parent41386e9cb918eed93b3f13648cbef387e371e451 (diff)
downloadWebKitGtk-tarball-a4e969f4965059196ca948db781e52f7cfebf19e.tar.gz
webkitgtk-2.12.3webkitgtk-2.12.3
Diffstat (limited to 'Source/JavaScriptCore/debugger')
-rw-r--r--Source/JavaScriptCore/debugger/Breakpoint.h57
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.cpp301
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.h82
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerActivation.cpp98
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerActivation.h71
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp99
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.h26
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h63
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerPrimitives.h2
-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, 842 insertions, 390 deletions
diff --git a/Source/JavaScriptCore/debugger/Breakpoint.h b/Source/JavaScriptCore/debugger/Breakpoint.h
index 95b92881d..78d208a2b 100644
--- a/Source/JavaScriptCore/debugger/Breakpoint.h
+++ b/Source/JavaScriptCore/debugger/Breakpoint.h
@@ -27,38 +27,67 @@
#define Breakpoint_h
#include "DebuggerPrimitives.h"
+#include <wtf/DoublyLinkedList.h>
+#include <wtf/RefCounted.h>
#include <wtf/text/WTFString.h>
namespace JSC {
-struct Breakpoint {
+struct Breakpoint : public DoublyLinkedListNode<Breakpoint> {
Breakpoint()
- : id(noBreakpointID)
- , sourceID(noSourceID)
- , line(0)
- , column(0)
- , autoContinue(false)
{
}
- Breakpoint(SourceID sourceID, unsigned line, unsigned column, String condition, bool autoContinue)
- : id(noBreakpointID)
- , sourceID(sourceID)
+ 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)
{
}
- BreakpointID id;
- SourceID sourceID;
- unsigned line;
- unsigned column;
+ 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;
+ 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
diff --git a/Source/JavaScriptCore/debugger/Debugger.cpp b/Source/JavaScriptCore/debugger/Debugger.cpp
index afa7546c8..f50d54be5 100644
--- a/Source/JavaScriptCore/debugger/Debugger.cpp
+++ b/Source/JavaScriptCore/debugger/Debugger.cpp
@@ -25,13 +25,12 @@
#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"
@@ -40,65 +39,41 @@ namespace {
using namespace JSC;
-class Recompiler : public MarkedBlock::VoidFunctor {
-public:
- Recompiler(JSC::Debugger*);
- ~Recompiler();
- void operator()(JSCell*);
-
-private:
- typedef HashSet<FunctionExecutable*> FunctionExecutableSet;
- typedef HashMap<SourceProvider*, ExecState*> SourceProviderMap;
-
- JSC::Debugger* m_debugger;
- FunctionExecutableSet m_functionExecutables;
- SourceProviderMap m_sourceProviders;
-};
-
-inline Recompiler::Recompiler(JSC::Debugger* debugger)
- : m_debugger(debugger)
-{
-}
+struct GatherSourceProviders : public MarkedBlock::VoidFunctor {
+ HashSet<SourceProvider*> sourceProviders;
+ JSGlobalObject* m_globalObject;
-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());
-}
+ GatherSourceProviders(JSGlobalObject* globalObject)
+ : m_globalObject(globalObject) { }
-inline void Recompiler::operator()(JSCell* cell)
-{
- if (!cell->inherits(JSFunction::info()))
- return;
+ IterationStatus operator()(JSCell* cell)
+ {
+ JSFunction* function = jsDynamicCast<JSFunction*>(cell);
+ if (!function)
+ return IterationStatus::Continue;
- JSFunction* function = jsCast<JSFunction*>(cell);
- if (function->executable()->isHostFunction())
- return;
+ if (function->scope()->globalObject() != m_globalObject)
+ return IterationStatus::Continue;
- FunctionExecutable* executable = function->jsExecutable();
+ if (!function->executable()->isFunctionExecutable())
+ return IterationStatus::Continue;
- // Check if the function is already in the set - if so,
- // we've already retranslated it, nothing to do here.
- if (!m_functionExecutables.add(executable).isNewEntry)
- return;
+ if (function->isHostOrBuiltinFunction())
+ return IterationStatus::Continue;
- 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);
-}
+ sourceProviders.add(
+ jsCast<FunctionExecutable*>(function->executable())->source().provider());
+ return IterationStatus::Continue;
+ }
+};
} // namespace
namespace JSC {
-class DebuggerCallFrameScope {
+class DebuggerPausedScope {
public:
- DebuggerCallFrameScope(Debugger& debugger)
+ DebuggerPausedScope(Debugger& debugger)
: m_debugger(debugger)
{
ASSERT(!m_debugger.m_currentDebuggerCallFrame);
@@ -106,11 +81,11 @@ public:
m_debugger.m_currentDebuggerCallFrame = DebuggerCallFrame::create(debugger.m_currentCallFrame);
}
- ~DebuggerCallFrameScope()
+ ~DebuggerPausedScope()
{
if (m_debugger.m_currentDebuggerCallFrame) {
m_debugger.m_currentDebuggerCallFrame->invalidate();
- m_debugger.m_currentDebuggerCallFrame = 0;
+ m_debugger.m_currentDebuggerCallFrame = nullptr;
}
}
@@ -138,14 +113,14 @@ private:
Debugger& m_debugger;
};
-Debugger::Debugger(bool isInWorkerThread)
- : m_vm(nullptr)
+Debugger::Debugger(VM& vm)
+ : m_vm(vm)
, m_pauseOnExceptionsState(DontPauseOnExceptions)
, m_pauseOnNextStatement(false)
, m_isPaused(false)
, m_breakpointsActivated(true)
, m_hasHandlerForExceptionCallback(false)
- , m_isInWorkerThread(isInWorkerThread)
+ , m_suppressAllPauses(false)
, m_steppingMode(SteppingModeDisabled)
, m_reasonForPause(NotPaused)
, m_pauseOnCallFrame(0)
@@ -153,6 +128,7 @@ Debugger::Debugger(bool isInWorkerThread)
, m_lastExecutedLine(UINT_MAX)
, m_lastExecutedSourceID(noSourceID)
, m_topBreakpointID(noBreakpointID)
+ , m_pausingBreakpointID(noBreakpointID)
{
}
@@ -166,12 +142,19 @@ Debugger::~Debugger()
void Debugger::attach(JSGlobalObject* globalObject)
{
ASSERT(!globalObject->debugger());
- if (!m_vm)
- m_vm = &globalObject->vm();
- else
- ASSERT(m_vm == &globalObject->vm());
globalObject->setDebugger(this);
m_globalObjects.add(globalObject);
+
+ m_vm.setShouldBuildPCToCodeOriginMapping();
+
+ // Call sourceParsed because it will execute JavaScript in the inspector.
+ GatherSourceProviders gatherSourceProviders(globalObject);
+ {
+ HeapIterationScope iterationScope(m_vm.heap);
+ m_vm.heap.objectSpace().forEachLiveCell(iterationScope, gatherSourceProviders);
+ }
+ for (auto* sourceProvider : gatherSourceProviders.sourceProviders)
+ sourceParsed(globalObject->globalExec(), sourceProvider, -1, String());
}
void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason)
@@ -195,8 +178,11 @@ void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason)
clearDebuggerRequests(globalObject);
globalObject->setDebugger(0);
- if (!m_globalObjects.size())
- m_vm = nullptr;
+}
+
+bool Debugger::isAttached(JSGlobalObject* globalObject)
+{
+ return globalObject->debugger() == this;
}
class Debugger::SetSteppingModeFunctor {
@@ -227,12 +213,12 @@ void Debugger::setSteppingMode(SteppingMode mode)
{
if (mode == m_steppingMode)
return;
- m_steppingMode = mode;
- if (!m_vm)
- return;
+ m_vm.heap.completeAllDFGPlans();
+
+ m_steppingMode = mode;
SetSteppingModeFunctor functor(this, mode);
- m_vm->heap.forEachCodeBlock(functor);
+ m_vm.heap.forEachCodeBlock(functor);
}
void Debugger::registerCodeBlock(CodeBlock* codeBlock)
@@ -242,9 +228,27 @@ void Debugger::registerCodeBlock(CodeBlock* codeBlock)
codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
}
+void Debugger::setProfilingClient(ProfilingClient* client)
+{
+ ASSERT(!!m_profilingClient != !!client);
+ m_profilingClient = client;
+
+ recompileAllJSFunctions();
+}
+
+double Debugger::willEvaluateScript()
+{
+ return m_profilingClient->willEvaluateScript();
+}
+
+void Debugger::didEvaluateScript(double startTime, ProfilingReason reason)
+{
+ m_profilingClient->didEvaluateScript(startTime, reason);
+}
+
void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot)
{
- ScriptExecutable* executable = codeBlock->ownerExecutable();
+ ScriptExecutable* executable = codeBlock->ownerScriptExecutable();
SourceID sourceID = static_cast<SourceID>(executable->sourceID());
if (breakpoint.sourceID != sourceID)
@@ -253,7 +257,7 @@ void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, Br
unsigned line = breakpoint.line;
unsigned column = breakpoint.column;
- unsigned startLine = executable->lineNo();
+ unsigned startLine = executable->firstLine();
unsigned startColumn = executable->startColumn();
unsigned endLine = executable->lastLine();
unsigned endColumn = executable->endColumn();
@@ -313,26 +317,15 @@ private:
void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot)
{
- if (!m_vm)
- return;
+ m_vm.heap.completeAllDFGPlans();
+
ToggleBreakpointFunctor functor(this, breakpoint, enabledOrNot);
- m_vm->heap.forEachCodeBlock(functor);
+ m_vm.heap.forEachCodeBlock(functor);
}
-void Debugger::recompileAllJSFunctions(VM* vm)
+void Debugger::recompileAllJSFunctions()
{
- // If JavaScript is running, it's not safe to recompile, since we'll end
- // up throwing away code that is live on the stack.
- if (vm->entryScope) {
- vm->entryScope->setRecompilationNeeded(true);
- return;
- }
-
- vm->prepareToDiscardCode();
-
- Recompiler recompiler(this);
- HeapIterationScope iterationScope(vm->heap);
- vm->heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
+ m_vm.deleteAllCode();
}
BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn)
@@ -346,18 +339,18 @@ BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine
it = m_sourceIDToBreakpoints.set(sourceID, LineToBreakpointsMap()).iterator;
LineToBreakpointsMap::iterator breaksIt = it->value.find(line);
if (breaksIt == it->value.end())
- breaksIt = it->value.set(line, BreakpointsInLine()).iterator;
+ breaksIt = it->value.set(line, adoptRef(new BreakpointsList)).iterator;
- BreakpointsInLine& breakpoints = breaksIt->value;
- unsigned breakpointsCount = breakpoints.size();
- for (unsigned i = 0; i < breakpointsCount; i++)
- if (breakpoints[i].column == column) {
+ 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);
@@ -366,8 +359,9 @@ BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine
actualLine = line;
actualColumn = column;
- breakpoints.append(breakpoint);
- m_breakpointIDToBreakpoint.set(id, &breakpoints.last());
+ Breakpoint* newBreakpoint = new Breakpoint(breakpoint);
+ breakpoints.append(newBreakpoint);
+ m_breakpointIDToBreakpoint.set(id, newBreakpoint);
toggleBreakpoint(breakpoint, BreakpointEnabled);
@@ -380,31 +374,35 @@ void Debugger::removeBreakpoint(BreakpointID id)
BreakpointIDToBreakpointMap::iterator idIt = m_breakpointIDToBreakpoint.find(id);
ASSERT(idIt != m_breakpointIDToBreakpoint.end());
- Breakpoint& breakpoint = *idIt->value;
+ Breakpoint* breakpoint = idIt->value;
- SourceID sourceID = breakpoint.sourceID;
+ SourceID sourceID = breakpoint->sourceID;
ASSERT(sourceID);
SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
ASSERT(it != m_sourceIDToBreakpoints.end());
- LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint.line);
+ LineToBreakpointsMap::iterator breaksIt = it->value.find(breakpoint->line);
ASSERT(breaksIt != it->value.end());
- toggleBreakpoint(breakpoint, BreakpointDisabled);
+ toggleBreakpoint(*breakpoint, BreakpointDisabled);
- BreakpointsInLine& breakpoints = breaksIt->value;
- unsigned breakpointsCount = breakpoints.size();
- for (unsigned i = 0; i < breakpointsCount; i++) {
- if (breakpoints[i].id == breakpoint.id) {
- breakpoints.remove(i);
- m_breakpointIDToBreakpoint.remove(idIt);
+ 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
- if (breakpoints.isEmpty()) {
- it->value.remove(breaksIt);
- if (it->value.isEmpty())
- m_sourceIDToBreakpoints.remove(it);
- }
- break;
- }
+ m_breakpointIDToBreakpoint.remove(idIt);
+ breakpoints.remove(breakpoint);
+ delete breakpoint;
+
+ if (breakpoints.isEmpty()) {
+ it->value.remove(breaksIt);
+ if (it->value.isEmpty())
+ m_sourceIDToBreakpoints.remove(it);
}
}
@@ -425,12 +423,11 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br
return false;
bool hit = false;
- const BreakpointsInLine& breakpoints = breaksIt->value;
- unsigned breakpointsCount = breakpoints.size();
- unsigned i;
- for (i = 0; i < breakpointsCount; i++) {
- unsigned breakLine = breakpoints[i].line;
- unsigned breakColumn = breakpoints[i].column;
+ 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)
@@ -443,17 +440,22 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br
return false;
if (hitBreakpoint)
- *hitBreakpoint = breakpoints[i];
+ *hitBreakpoint = *breakpoint;
- if (breakpoints[i].condition.isEmpty())
+ 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);
- JSValue exception;
- JSValue result = DebuggerCallFrame::evaluateWithCallFrame(m_currentCallFrame, breakpoints[i].condition, exception);
+ NakedPtr<Exception> exception;
+ DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
+ JSValue result = debuggerCallFrame->evaluate(breakpoint->condition, exception);
// We can lose the debugger while executing JavaScript.
if (!m_currentCallFrame)
@@ -488,14 +490,14 @@ private:
void Debugger::clearBreakpoints()
{
+ m_vm.heap.completeAllDFGPlans();
+
m_topBreakpointID = noBreakpointID;
m_breakpointIDToBreakpoint.clear();
m_sourceIDToBreakpoints.clear();
- if (!m_vm)
- return;
ClearCodeBlockDebuggerRequestsFunctor functor(this);
- m_vm->heap.forEachCodeBlock(functor);
+ m_vm.heap.forEachCodeBlock(functor);
}
class Debugger::ClearDebuggerRequestsFunctor {
@@ -518,9 +520,10 @@ private:
void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
{
- ASSERT(m_vm);
+ m_vm.heap.completeAllDFGPlans();
+
ClearDebuggerRequestsFunctor functor(globalObject);
- m_vm->heap.forEachCodeBlock(functor);
+ m_vm.heap.forEachCodeBlock(functor);
}
void Debugger::setBreakpointsActivated(bool activated)
@@ -545,10 +548,12 @@ void Debugger::breakProgram()
if (m_isPaused)
return;
+ if (!m_vm.topCallFrame)
+ return;
+
m_pauseOnNextStatement = true;
setSteppingMode(SteppingModeEnabled);
- m_currentCallFrame = m_vm->topCallFrame;
- ASSERT(m_currentCallFrame);
+ m_currentCallFrame = m_vm.topCallFrame;
pauseIfNeeded(m_currentCallFrame);
}
@@ -585,7 +590,8 @@ void Debugger::stepOutOfFunction()
if (!m_isPaused)
return;
- m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrameSkippingVMEntrySentinel() : 0;
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->callerFrame(topVMEntryFrame) : 0;
notifyDoneProcessingDebuggerEvents();
}
@@ -612,6 +618,9 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame)
if (m_isPaused)
return;
+ if (m_suppressAllPauses)
+ return;
+
JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject();
if (!needPauseHandling(vmEntryGlobalObject))
return;
@@ -621,6 +630,8 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame)
bool pauseNow = m_pauseOnNextStatement;
pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame);
+ DebuggerPausedScope debuggerPausedScope(*this);
+
intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame);
TextPosition position = DebuggerCallFrame::positionForCallFrame(m_currentCallFrame);
pauseNow |= didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint);
@@ -628,8 +639,6 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame)
if (!pauseNow)
return;
- DebuggerCallFrameScope debuggerCallFrameScope(*this);
-
// Make sure we are not going to pause again on breakpoint actions by
// reseting the pause state before executing any breakpoint actions.
TemporaryPausedState pausedState(*this);
@@ -637,14 +646,21 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame)
m_pauseOnNextStatement = false;
if (didHitBreakpoint) {
- handleBreakpointHit(breakpoint);
+ 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());
}
- handlePause(m_reasonForPause, vmEntryGlobalObject);
+ m_pausingBreakpointID = noBreakpointID;
if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) {
setSteppingMode(SteppingModeDisabled);
@@ -652,13 +668,13 @@ void Debugger::pauseIfNeeded(CallFrame* callFrame)
}
}
-void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasHandler)
+void Debugger::exception(CallFrame* callFrame, JSValue exception, bool hasCatchHandler)
{
if (m_isPaused)
return;
PauseReasonDeclaration reason(*this, PausedForException);
- if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) {
+ if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasCatchHandler)) {
m_pauseOnNextStatement = true;
setSteppingMode(SteppingModeEnabled);
}
@@ -701,10 +717,13 @@ void Debugger::returnEvent(CallFrame* callFrame)
return;
// Treat stepping over a return statement like stepping out.
- if (m_currentCallFrame == m_pauseOnCallFrame)
- m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+ if (m_currentCallFrame == m_pauseOnCallFrame) {
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+ }
- m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
}
void Debugger::willExecuteProgram(CallFrame* callFrame)
@@ -713,13 +732,7 @@ void Debugger::willExecuteProgram(CallFrame* callFrame)
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);
+ updateCallFrameAndPauseIfNeeded(callFrame);
}
void Debugger::didExecuteProgram(CallFrame* callFrame)
@@ -734,11 +747,13 @@ void Debugger::didExecuteProgram(CallFrame* callFrame)
if (!m_currentCallFrame)
return;
if (m_currentCallFrame == m_pauseOnCallFrame) {
- m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_pauseOnCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
if (!m_currentCallFrame)
return;
}
- m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ m_currentCallFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
}
void Debugger::didReachBreakpoint(CallFrame* callFrame)
@@ -746,7 +761,7 @@ void Debugger::didReachBreakpoint(CallFrame* callFrame)
if (m_isPaused)
return;
- PauseReasonDeclaration reason(*this, PausedForBreakpoint);
+ PauseReasonDeclaration reason(*this, PausedForDebuggerStatement);
m_pauseOnNextStatement = true;
setSteppingMode(SteppingModeEnabled);
updateCallFrameAndPauseIfNeeded(callFrame);
diff --git a/Source/JavaScriptCore/debugger/Debugger.h b/Source/JavaScriptCore/debugger/Debugger.h
index f7b734f37..2e91aafbe 100644
--- a/Source/JavaScriptCore/debugger/Debugger.h
+++ b/Source/JavaScriptCore/debugger/Debugger.h
@@ -23,17 +23,19 @@
#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/Vector.h>
#include <wtf/text/TextPosition.h>
namespace JSC {
+class CodeBlock;
+class Exception;
class ExecState;
class JSGlobalObject;
class SourceProvider;
@@ -43,9 +45,11 @@ typedef ExecState CallFrame;
class JS_EXPORT_PRIVATE Debugger {
public:
- Debugger(bool isInWorkerThread = false);
+ Debugger(VM&);
virtual ~Debugger();
+ VM& vm() { return m_vm; }
+
JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
bool hasHandlerForExceptionCallback() const
{
@@ -60,12 +64,13 @@ public:
bool needsExceptionCallbacks() const { return m_pauseOnExceptionsState != DontPauseOnExceptions; }
- void attach(JSGlobalObject*);
enum ReasonForDetach {
TerminatingDebuggingSession,
GlobalObjectIsDestructing
};
- virtual void detach(JSGlobalObject*, ReasonForDetach);
+ void attach(JSGlobalObject*);
+ void detach(JSGlobalObject*, ReasonForDetach);
+ bool isAttached(JSGlobalObject*);
BreakpointID setBreakpoint(Breakpoint, unsigned& actualLine, unsigned& actualColumn);
void removeBreakpoint(BreakpointID);
@@ -82,6 +87,20 @@ public:
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();
@@ -89,12 +108,15 @@ public:
void stepOverStatement();
void stepOutOfFunction();
- bool isPaused() { return m_isPaused; }
+ 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 hasHandler);
+ void exception(CallFrame*, JSValue exceptionValue, bool hasCatchHandler);
void atStatement(CallFrame*);
void callEvent(CallFrame*);
void returnEvent(CallFrame*);
@@ -102,34 +124,35 @@ public:
void didExecuteProgram(CallFrame*);
void didReachBreakpoint(CallFrame*);
- void recompileAllJSFunctions(VM*);
+ virtual void recompileAllJSFunctions();
void registerCodeBlock(CodeBlock*);
-protected:
- virtual bool needPauseHandling(JSGlobalObject*) { return false; }
- virtual void handleBreakpointHit(const Breakpoint&) { }
- virtual void handleExceptionInBreakpointCondition(ExecState*, JSValue exception) const { UNUSED_PARAM(exception); }
-
- enum ReasonForPause {
- NotPaused,
- PausedForException,
- PausedAtStatement,
- PausedAfterCall,
- PausedBeforeReturn,
- PausedAtStartOfProgram,
- PausedAtEndOfProgram,
- PausedForBreakpoint
+ class ProfilingClient {
+ public:
+ virtual ~ProfilingClient() { }
+ virtual bool isAlreadyProfiling() const = 0;
+ virtual double willEvaluateScript() = 0;
+ virtual void didEvaluateScript(double startTime, ProfilingReason) = 0;
};
- virtual void handlePause(ReasonForPause, 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);
+
+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 Vector<Breakpoint> BreakpointsInLine;
- typedef HashMap<unsigned, BreakpointsInLine, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
+ 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;
@@ -182,7 +205,7 @@ private:
void clearDebuggerRequests(JSGlobalObject*);
- VM* m_vm;
+ VM& m_vm;
HashSet<JSGlobalObject*> m_globalObjects;
PauseOnExceptionsState m_pauseOnExceptionsState;
@@ -190,8 +213,8 @@ private:
bool m_isPaused : 1;
bool m_breakpointsActivated : 1;
bool m_hasHandlerForExceptionCallback : 1;
- bool m_isInWorkerThread : 1;
- SteppingMode m_steppingMode : 1;
+ bool m_suppressAllPauses : 1;
+ unsigned m_steppingMode : 1; // SteppingMode
ReasonForPause m_reasonForPause;
JSValue m_currentException;
@@ -201,12 +224,15 @@ private:
SourceID m_lastExecutedSourceID;
BreakpointID m_topBreakpointID;
+ BreakpointID m_pausingBreakpointID;
BreakpointIDToBreakpointMap m_breakpointIDToBreakpoint;
SourceIDToBreakpointsMap m_sourceIDToBreakpoints;
RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame;
- friend class DebuggerCallFrameScope;
+ ProfilingClient* m_profilingClient { nullptr };
+
+ friend class DebuggerPausedScope;
friend class TemporaryPausedState;
friend class LLIntOffsetsExtractor;
};
diff --git a/Source/JavaScriptCore/debugger/DebuggerActivation.cpp b/Source/JavaScriptCore/debugger/DebuggerActivation.cpp
deleted file mode 100644
index 75b08ea81..000000000
--- a/Source/JavaScriptCore/debugger/DebuggerActivation.cpp
+++ /dev/null
@@ -1,98 +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 {
-
-STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(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, 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(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
-{
- DebuggerActivation* thisObject = jsCast<DebuggerActivation*>(object);
- 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);
-}
-
-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::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const 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 e90383eaa..000000000
--- a/Source/JavaScriptCore/debugger/DebuggerActivation.h
+++ /dev/null
@@ -1,71 +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(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, JSValue prototype)
- {
- return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), 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 90cee8c23..f4794fd45 100644
--- a/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013 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,13 +29,16 @@
#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 "VMEntryScope.h"
+#include "StrongInlines.h"
namespace JSC {
@@ -55,13 +58,36 @@ private:
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);
}
-PassRefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
+RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
{
ASSERT(isValid());
if (!isValid())
@@ -70,9 +96,12 @@ PassRefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
if (m_caller)
return m_caller;
- CallFrame* callerFrame = m_callFrame->callerFrameSkippingVMEntrySentinel();
+ FindCallerMidStackFunctor functor(m_callFrame);
+ m_callFrame->vm().topCallFrame->iterate(functor);
+
+ CallFrame* callerFrame = functor.getCallerFrame();
if (!callerFrame)
- return 0;
+ return nullptr;
m_caller = DebuggerCallFrame::create(callerFrame);
return m_caller;
@@ -99,19 +128,29 @@ String DebuggerCallFrame::functionName() const
ASSERT(isValid());
if (!isValid())
return String();
- JSObject* function = m_callFrame->callee();
- if (!function)
- return String();
-
- return getCalculatedDisplayName(m_callFrame, function);
+ return m_callFrame->friendlyFunctionName();
}
-JSScope* DebuggerCallFrame::scope() const
+DebuggerScope* DebuggerCallFrame::scope()
{
ASSERT(isValid());
if (!isValid())
return 0;
- return m_callFrame->scope();
+
+ 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
@@ -120,7 +159,7 @@ DebuggerCallFrame::Type DebuggerCallFrame::type() const
if (!isValid())
return ProgramType;
- if (m_callFrame->callee())
+ if (jsDynamicCast<JSFunction*>(m_callFrame->callee()))
return FunctionType;
return ProgramType;
@@ -133,14 +172,10 @@ JSValue DebuggerCallFrame::thisValue() const
}
// Evaluate some JavaScript code in the scope of this frame.
-JSValue DebuggerCallFrame::evaluate(const String& script, JSValue& exception) const
+JSValue DebuggerCallFrame::evaluate(const String& script, NakedPtr<Exception>& exception)
{
ASSERT(isValid());
- return evaluateWithCallFrame(m_callFrame, script, exception);
-}
-
-JSValue DebuggerCallFrame::evaluateWithCallFrame(CallFrame* callFrame, const String& script, JSValue& exception)
-{
+ CallFrame* callFrame = m_callFrame;
if (!callFrame)
return jsNull();
@@ -149,8 +184,15 @@ JSValue DebuggerCallFrame::evaluateWithCallFrame(CallFrame* callFrame, const Str
if (!callFrame->codeBlock())
return JSValue();
+ DebuggerEvalEnabler evalEnabler(callFrame);
VM& vm = callFrame->vm();
- EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), callFrame->codeBlock()->isStrictMode());
+ 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();
@@ -158,7 +200,7 @@ JSValue DebuggerCallFrame::evaluateWithCallFrame(CallFrame* callFrame, const Str
}
JSValue thisValue = thisValueForCallFrame(callFrame);
- JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, callFrame->scope());
+ JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, scope()->jsScope());
if (vm.exception()) {
exception = vm.exception();
vm.clearException();
@@ -169,10 +211,13 @@ JSValue DebuggerCallFrame::evaluateWithCallFrame(CallFrame* callFrame, const Str
void DebuggerCallFrame::invalidate()
{
- m_callFrame = nullptr;
- RefPtr<DebuggerCallFrame> frame = m_caller.release();
+ 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();
}
}
@@ -193,7 +238,7 @@ SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* callFrame)
CodeBlock* codeBlock = callFrame->codeBlock();
if (!codeBlock)
return noSourceID;
- return codeBlock->ownerExecutable()->sourceID();
+ return codeBlock->ownerScriptExecutable()->sourceID();
}
JSValue DebuggerCallFrame::thisValueForCallFrame(CallFrame* callFrame)
diff --git a/Source/JavaScriptCore/debugger/DebuggerCallFrame.h b/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
index 09c3fb9d8..aa3cca52b 100644
--- a/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
+++ b/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013 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,26 +29,32 @@
#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 DebuggerScope;
+class Exception;
+class ExecState;
+typedef ExecState CallFrame;
+
class DebuggerCallFrame : public RefCounted<DebuggerCallFrame> {
public:
enum Type { ProgramType, FunctionType };
- static PassRefPtr<DebuggerCallFrame> create(CallFrame* callFrame)
+ static Ref<DebuggerCallFrame> create(CallFrame* callFrame)
{
- return adoptRef(new DebuggerCallFrame(callFrame));
+ return adoptRef(*new DebuggerCallFrame(callFrame));
}
JS_EXPORT_PRIVATE explicit DebuggerCallFrame(CallFrame*);
- JS_EXPORT_PRIVATE PassRefPtr<DebuggerCallFrame> callerFrame();
+ JS_EXPORT_PRIVATE RefPtr<DebuggerCallFrame> callerFrame();
ExecState* exec() const { return m_callFrame; }
JS_EXPORT_PRIVATE SourceID sourceID() const;
@@ -58,11 +64,11 @@ public:
JS_EXPORT_PRIVATE const TextPosition& position() const { return m_position; }
JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject() const;
- JS_EXPORT_PRIVATE JSScope* scope() const;
+ JS_EXPORT_PRIVATE DebuggerScope* scope();
JS_EXPORT_PRIVATE String functionName() const;
JS_EXPORT_PRIVATE Type type() const;
JS_EXPORT_PRIVATE JSValue thisValue() const;
- JS_EXPORT_PRIVATE JSValue evaluate(const String&, JSValue& exception) const;
+ JSValue evaluate(const String&, NakedPtr<Exception>&);
bool isValid() const { return !!m_callFrame; }
JS_EXPORT_PRIVATE void invalidate();
@@ -70,7 +76,6 @@ public:
// 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 JSValue evaluateWithCallFrame(CallFrame*, const String& script, JSValue& exception);
JS_EXPORT_PRIVATE static TextPosition positionForCallFrame(CallFrame*);
JS_EXPORT_PRIVATE static SourceID sourceIDForCallFrame(CallFrame*);
static JSValue thisValueForCallFrame(CallFrame*);
@@ -79,6 +84,9 @@ 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
index 635a5bfe8..ab7301b5c 100644
--- a/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
+++ b/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
@@ -26,6 +26,8 @@
#ifndef DebuggerPrimitives_h
#define DebuggerPrimitives_h
+#include <stddef.h>
+
namespace JSC {
typedef size_t SourceID;
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