summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/debugger
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/debugger
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/debugger')
-rw-r--r--Source/JavaScriptCore/debugger/Breakpoint.h66
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.cpp611
-rw-r--r--Source/JavaScriptCore/debugger/Debugger.h123
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerActivation.cpp98
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerActivation.h71
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp253
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.h46
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h60
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerLocation.cpp46
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerLocation.h53
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerParseData.cpp185
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerParseData.h81
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerPrimitives.h7
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerScope.cpp253
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerScope.h118
-rw-r--r--Source/JavaScriptCore/debugger/ScriptProfilingScope.h90
16 files changed, 1617 insertions, 544 deletions
diff --git a/Source/JavaScriptCore/debugger/Breakpoint.h b/Source/JavaScriptCore/debugger/Breakpoint.h
index 95b92881d..c1504a150 100644
--- a/Source/JavaScriptCore/debugger/Breakpoint.h
+++ b/Source/JavaScriptCore/debugger/Breakpoint.h
@@ -20,47 +20,75 @@
* 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.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Breakpoint_h
-#define Breakpoint_h
+#pragma once
#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)
+ , resolved(other.resolved)
+ {
+ }
+
+ 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 };
+ bool resolved { false };
static const unsigned unspecifiedColumn = UINT_MAX;
+
+private:
+ Breakpoint* m_prev;
+ Breakpoint* m_next;
+
+ friend class WTF::DoublyLinkedListNode<Breakpoint>;
};
-} // namespace JSC
+class BreakpointsList : public DoublyLinkedList<Breakpoint>,
+ public RefCounted<BreakpointsList> {
+public:
+ ~BreakpointsList()
+ {
+ Breakpoint* breakpoint;
+ while ((breakpoint = removeHead()))
+ delete breakpoint;
+ ASSERT(isEmpty());
+ }
+};
-#endif // Breakpoint_h
+} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/Debugger.cpp b/Source/JavaScriptCore/debugger/Debugger.cpp
index afa7546c8..3eefde28b 100644
--- a/Source/JavaScriptCore/debugger/Debugger.cpp
+++ b/Source/JavaScriptCore/debugger/Debugger.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013, 2014, 2016 Apple Inc. All rights reserved.
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
*
@@ -25,13 +25,13 @@
#include "CodeBlock.h"
#include "DebuggerCallFrame.h"
#include "Error.h"
-
#include "HeapIterationScope.h"
#include "Interpreter.h"
+#include "JSCInlines.h"
#include "JSCJSValueInlines.h"
#include "JSFunction.h"
#include "JSGlobalObject.h"
-#include "Operations.h"
+#include "MarkedSpaceInlines.h"
#include "Parser.h"
#include "Protect.h"
#include "VMEntryScope.h"
@@ -40,77 +40,58 @@ 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)
-{
-}
-
-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::operator()(JSCell* cell)
-{
- if (!cell->inherits(JSFunction::info()))
- return;
-
- JSFunction* function = jsCast<JSFunction*>(cell);
- if (function->executable()->isHostFunction())
- return;
-
- FunctionExecutable* executable = function->jsExecutable();
+struct GatherSourceProviders : public MarkedBlock::VoidFunctor {
+ // FIXME: This is a mutable field because this isn't a C++ lambda.
+ // https://bugs.webkit.org/show_bug.cgi?id=159644
+ mutable HashSet<SourceProvider*> sourceProviders;
+ JSGlobalObject* m_globalObject;
- // 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;
+ GatherSourceProviders(JSGlobalObject* globalObject)
+ : m_globalObject(globalObject) { }
- 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);
-}
+ IterationStatus operator()(HeapCell* heapCell, HeapCell::Kind kind) const
+ {
+ if (kind != HeapCell::JSCell)
+ return IterationStatus::Continue;
+
+ JSCell* cell = static_cast<JSCell*>(heapCell);
+
+ JSFunction* function = jsDynamicCast<JSFunction*>(*cell->vm(), cell);
+ if (!function)
+ return IterationStatus::Continue;
+
+ if (function->scope()->globalObject() != m_globalObject)
+ return IterationStatus::Continue;
+
+ if (!function->executable()->isFunctionExecutable())
+ return IterationStatus::Continue;
+
+ if (function->isHostOrBuiltinFunction())
+ return IterationStatus::Continue;
+
+ sourceProviders.add(
+ jsCast<FunctionExecutable*>(function->executable())->source().provider());
+ return IterationStatus::Continue;
+ }
+};
} // namespace
namespace JSC {
-class DebuggerCallFrameScope {
+class DebuggerPausedScope {
public:
- DebuggerCallFrameScope(Debugger& debugger)
+ 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);
}
- ~DebuggerCallFrameScope()
+ ~DebuggerPausedScope()
{
if (m_debugger.m_currentDebuggerCallFrame) {
m_debugger.m_currentDebuggerCallFrame->invalidate();
- m_debugger.m_currentDebuggerCallFrame = 0;
+ m_debugger.m_currentDebuggerCallFrame = nullptr;
}
}
@@ -118,7 +99,7 @@ private:
Debugger& m_debugger;
};
-// This is very similar to TemporaryChange<bool>, but that cannot be used
+// This is very similar to SetForScope<bool>, but that cannot be used
// as the m_isPaused field uses only one bit.
class TemporaryPausedState {
public:
@@ -138,21 +119,26 @@ private:
Debugger& m_debugger;
};
-Debugger::Debugger(bool isInWorkerThread)
- : m_vm(nullptr)
+
+Debugger::ProfilingClient::~ProfilingClient()
+{
+}
+
+Debugger::Debugger(VM& vm)
+ : m_vm(vm)
, m_pauseOnExceptionsState(DontPauseOnExceptions)
- , m_pauseOnNextStatement(false)
+ , m_pauseAtNextOpportunity(false)
+ , m_pastFirstExpressionInStatement(false)
, m_isPaused(false)
- , m_breakpointsActivated(true)
+ , m_breakpointsActivated(false)
, m_hasHandlerForExceptionCallback(false)
- , m_isInWorkerThread(isInWorkerThread)
+ , m_suppressAllPauses(false)
, m_steppingMode(SteppingModeDisabled)
, m_reasonForPause(NotPaused)
- , m_pauseOnCallFrame(0)
- , m_currentCallFrame(0)
, m_lastExecutedLine(UINT_MAX)
, m_lastExecutedSourceID(noSourceID)
, m_topBreakpointID(noBreakpointID)
+ , m_pausingBreakpointID(noBreakpointID)
{
}
@@ -160,18 +146,25 @@ Debugger::~Debugger()
{
HashSet<JSGlobalObject*>::iterator end = m_globalObjects.end();
for (HashSet<JSGlobalObject*>::iterator it = m_globalObjects.begin(); it != end; ++it)
- (*it)->setDebugger(0);
+ (*it)->setDebugger(nullptr);
}
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)
@@ -179,9 +172,10 @@ 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;
+ // We know there is an entry scope, otherwise, m_currentCallFrame would be null.
+ if (m_isPaused && m_currentCallFrame && globalObject->vm().entryScope->globalObject() == globalObject) {
+ m_currentCallFrame = nullptr;
+ m_pauseOnCallFrame = nullptr;
continueProgram();
}
@@ -194,9 +188,15 @@ void Debugger::detach(JSGlobalObject* globalObject, ReasonForDetach reason)
if (reason != GlobalObjectIsDestructing)
clearDebuggerRequests(globalObject);
- globalObject->setDebugger(0);
- if (!m_globalObjects.size())
- m_vm = nullptr;
+ globalObject->setDebugger(nullptr);
+
+ if (m_globalObjects.isEmpty())
+ clearParsedData();
+}
+
+bool Debugger::isAttached(JSGlobalObject* globalObject)
+{
+ return globalObject->debugger() == this;
}
class Debugger::SetSteppingModeFunctor {
@@ -207,7 +207,7 @@ public:
{
}
- bool operator()(CodeBlock* codeBlock)
+ bool operator()(CodeBlock* codeBlock) const
{
if (m_debugger == codeBlock->globalObject()->debugger()) {
if (m_mode == SteppingModeEnabled)
@@ -227,12 +227,12 @@ void Debugger::setSteppingMode(SteppingMode mode)
{
if (mode == m_steppingMode)
return;
- m_steppingMode = mode;
- if (!m_vm)
- return;
+ m_vm.heap.completeAllJITPlans();
+
+ m_steppingMode = mode;
SetSteppingModeFunctor functor(this, mode);
- m_vm->heap.forEachCodeBlock(functor);
+ m_vm.heap.forEachCodeBlock(functor);
}
void Debugger::registerCodeBlock(CodeBlock* codeBlock)
@@ -242,9 +242,27 @@ void Debugger::registerCodeBlock(CodeBlock* codeBlock)
codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled);
}
+void Debugger::setProfilingClient(ProfilingClient* client)
+{
+ ASSERT(!!m_profilingClient != !!client);
+ m_profilingClient = client;
+}
+
+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();
+ ASSERT(breakpoint.resolved);
+
+ ScriptExecutable* executable = codeBlock->ownerScriptExecutable();
SourceID sourceID = static_cast<SourceID>(executable->sourceID());
if (breakpoint.sourceID != sourceID)
@@ -253,7 +271,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();
@@ -271,6 +289,7 @@ void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, Br
if (line == endLine && column > endColumn)
return;
}
+
if (!codeBlock->hasOpDebugForLineAndColumn(line, column))
return;
@@ -283,10 +302,8 @@ void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, Br
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);
- }
+ for (auto* breakpoint : breakpoints.values())
+ toggleBreakpoint(codeBlock, *breakpoint, BreakpointEnabled);
}
class Debugger::ToggleBreakpointFunctor {
@@ -298,7 +315,7 @@ public:
{
}
- bool operator()(CodeBlock* codeBlock)
+ bool operator()(CodeBlock* codeBlock) const
{
if (m_debugger == codeBlock->globalObject()->debugger())
m_debugger->toggleBreakpoint(codeBlock, m_breakpoint, m_enabledOrNot);
@@ -313,63 +330,90 @@ private:
void Debugger::toggleBreakpoint(Breakpoint& breakpoint, Debugger::BreakpointState enabledOrNot)
{
- if (!m_vm)
- return;
+ m_vm.heap.completeAllJITPlans();
+
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);
+ m_vm.deleteAllCode(PreventCollectionAndDeleteAllCode);
+}
+
+DebuggerParseData& Debugger::debuggerParseData(SourceID sourceID, SourceProvider* provider)
+{
+ auto iter = m_parseDataMap.find(sourceID);
+ if (iter != m_parseDataMap.end())
+ return iter->value;
+
+ DebuggerParseData parseData;
+ gatherDebuggerParseDataForSource(m_vm, provider, parseData);
+ auto result = m_parseDataMap.add(sourceID, parseData);
+ return result.iterator->value;
+}
+
+void Debugger::resolveBreakpoint(Breakpoint& breakpoint, SourceProvider* sourceProvider)
+{
+ RELEASE_ASSERT(!breakpoint.resolved);
+ ASSERT(breakpoint.sourceID != noSourceID);
+
+ // FIXME: <https://webkit.org/b/162771> Web Inspector: Adopt TextPosition in Inspector to avoid oneBasedInt/zeroBasedInt ambiguity
+ // Inspector breakpoint line and column values are zero-based but the executable
+ // and CodeBlock line and column values are one-based.
+ unsigned line = breakpoint.line + 1;
+ unsigned column = breakpoint.column ? breakpoint.column : Breakpoint::unspecifiedColumn;
+
+ DebuggerParseData& parseData = debuggerParseData(breakpoint.sourceID, sourceProvider);
+ std::optional<JSTextPosition> resolvedPosition = parseData.pausePositions.breakpointLocationForLineColumn((int)line, (int)column);
+ if (!resolvedPosition)
return;
- }
- vm->prepareToDiscardCode();
+ unsigned resolvedLine = resolvedPosition->line;
+ unsigned resolvedColumn = resolvedPosition->offset - resolvedPosition->lineStartOffset + 1;
- Recompiler recompiler(this);
- HeapIterationScope iterationScope(vm->heap);
- vm->heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
+ breakpoint.line = resolvedLine - 1;
+ breakpoint.column = resolvedColumn - 1;
+ breakpoint.resolved = true;
}
-BreakpointID Debugger::setBreakpoint(Breakpoint breakpoint, unsigned& actualLine, unsigned& actualColumn)
+BreakpointID Debugger::setBreakpoint(Breakpoint& breakpoint, bool& existing)
{
+ ASSERT(breakpoint.resolved);
+ ASSERT(breakpoint.sourceID != noSourceID);
+
SourceID sourceID = breakpoint.sourceID;
unsigned line = breakpoint.line;
unsigned column = breakpoint.column;
- SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(sourceID);
+ SourceIDToBreakpointsMap::iterator it = m_sourceIDToBreakpoints.find(breakpoint.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, BreakpointsInLine()).iterator;
-
- BreakpointsInLine& breakpoints = breaksIt->value;
- unsigned breakpointsCount = breakpoints.size();
- for (unsigned i = 0; i < breakpointsCount; i++)
- if (breakpoints[i].column == column) {
- // The breakpoint already exists. We're not allowed to create a new
- // breakpoint at this location. Rather than returning the breakpointID
- // of the pre-existing breakpoint, we need to return noBreakpointID
- // to indicate that we're not creating a new one.
- return noBreakpointID;
+ 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) {
+ // Found existing breakpoint. Do not create a duplicate at this location.
+ existing = true;
+ return current->id;
}
+ }
+ existing = false;
BreakpointID id = ++m_topBreakpointID;
RELEASE_ASSERT(id != noBreakpointID);
breakpoint.id = id;
- 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);
+ toggleBreakpoint(*newBreakpoint, BreakpointEnabled);
return id;
}
@@ -380,31 +424,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);
}
}
@@ -419,18 +467,17 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br
unsigned line = position.m_line.zeroBasedInt();
unsigned column = position.m_column.zeroBasedInt();
-
+
LineToBreakpointsMap::const_iterator breaksIt = it->value.find(line);
if (breaksIt == it->value.end())
return false;
bool hit = false;
- const BreakpointsInLine& breakpoints = breaksIt->value;
- unsigned breakpointsCount = breakpoints.size();
- unsigned i;
- for (i = 0; i < breakpointsCount; i++) {
- unsigned breakLine = breakpoints[i].line;
- unsigned breakColumn = breakpoints[i].column;
+ 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 +490,23 @@ bool Debugger::hasBreakpoint(SourceID sourceID, const TextPosition& position, Br
return false;
if (hitBreakpoint)
- *hitBreakpoint = breakpoints[i];
+ *hitBreakpoint = *breakpoint;
+
+ breakpoint->hitCount++;
+ if (breakpoint->ignoreCount >= breakpoint->hitCount)
+ return false;
- if (breakpoints[i].condition.isEmpty())
+ 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();
+ JSObject* scopeExtensionObject = nullptr;
+ JSValue result = debuggerCallFrame.evaluateWithScopeExtension(breakpoint->condition, scopeExtensionObject, exception);
// We can lose the debugger while executing JavaScript.
if (!m_currentCallFrame)
@@ -475,7 +528,7 @@ public:
{
}
- bool operator()(CodeBlock* codeBlock)
+ bool operator()(CodeBlock* codeBlock) const
{
if (codeBlock->hasDebuggerRequests() && m_debugger == codeBlock->globalObject()->debugger())
codeBlock->clearDebuggerRequests();
@@ -488,14 +541,14 @@ private:
void Debugger::clearBreakpoints()
{
+ m_vm.heap.completeAllJITPlans();
+
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 {
@@ -505,7 +558,7 @@ public:
{
}
- bool operator()(CodeBlock* codeBlock)
+ bool operator()(CodeBlock* codeBlock) const
{
if (codeBlock->hasDebuggerRequests() && m_globalObject == codeBlock->globalObject())
codeBlock->clearDebuggerRequests();
@@ -518,14 +571,24 @@ private:
void Debugger::clearDebuggerRequests(JSGlobalObject* globalObject)
{
- ASSERT(m_vm);
+ m_vm.heap.completeAllJITPlans();
+
ClearDebuggerRequestsFunctor functor(globalObject);
- m_vm->heap.forEachCodeBlock(functor);
+ m_vm.heap.forEachCodeBlock(functor);
+}
+
+void Debugger::clearParsedData()
+{
+ m_parseDataMap.clear();
}
void Debugger::setBreakpointsActivated(bool activated)
{
+ if (activated == m_breakpointsActivated)
+ return;
+
m_breakpointsActivated = activated;
+ recompileAllJSFunctions();
}
void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
@@ -535,7 +598,7 @@ void Debugger::setPauseOnExceptionsState(PauseOnExceptionsState pause)
void Debugger::setPauseOnNextStatement(bool pause)
{
- m_pauseOnNextStatement = pause;
+ m_pauseAtNextOpportunity = pause;
if (pause)
setSteppingMode(SteppingModeEnabled);
}
@@ -545,19 +608,22 @@ void Debugger::breakProgram()
if (m_isPaused)
return;
- m_pauseOnNextStatement = true;
+ if (!m_vm.topCallFrame)
+ return;
+
+ m_pauseAtNextOpportunity = true;
setSteppingMode(SteppingModeEnabled);
- m_currentCallFrame = m_vm->topCallFrame;
- ASSERT(m_currentCallFrame);
+ m_currentCallFrame = m_vm.topCallFrame;
pauseIfNeeded(m_currentCallFrame);
}
void Debugger::continueProgram()
{
+ clearNextPauseState();
+
if (!m_isPaused)
return;
- m_pauseOnNextStatement = false;
notifyDoneProcessingDebuggerEvents();
}
@@ -566,7 +632,7 @@ void Debugger::stepIntoStatement()
if (!m_isPaused)
return;
- m_pauseOnNextStatement = true;
+ m_pauseAtNextOpportunity = true;
setSteppingMode(SteppingModeEnabled);
notifyDoneProcessingDebuggerEvents();
}
@@ -577,6 +643,7 @@ void Debugger::stepOverStatement()
return;
m_pauseOnCallFrame = m_currentCallFrame;
+ setSteppingMode(SteppingModeEnabled);
notifyDoneProcessingDebuggerEvents();
}
@@ -585,11 +652,30 @@ 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) : nullptr;
+ m_pauseOnStepOut = true;
+ setSteppingMode(SteppingModeEnabled);
notifyDoneProcessingDebuggerEvents();
}
-void Debugger::updateCallFrame(CallFrame* callFrame)
+void Debugger::updateCallFrame(CallFrame* callFrame, CallFrameUpdateAction action)
+{
+ if (!callFrame) {
+ m_currentCallFrame = nullptr;
+ return;
+ }
+
+ updateCallFrameInternal(callFrame);
+
+ if (action == AttemptPause)
+ pauseIfNeeded(callFrame);
+
+ if (!isStepping())
+ m_currentCallFrame = nullptr;
+}
+
+void Debugger::updateCallFrameInternal(CallFrame* callFrame)
{
m_currentCallFrame = callFrame;
SourceID sourceID = DebuggerCallFrame::sourceIDForCallFrame(callFrame);
@@ -599,73 +685,87 @@ void Debugger::updateCallFrame(CallFrame* callFrame)
}
}
-void Debugger::updateCallFrameAndPauseIfNeeded(CallFrame* callFrame)
-{
- updateCallFrame(callFrame);
- pauseIfNeeded(callFrame);
- if (!isStepping())
- m_currentCallFrame = 0;
-}
-
void Debugger::pauseIfNeeded(CallFrame* callFrame)
{
+ VM& vm = callFrame->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
if (m_isPaused)
return;
- JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject();
- if (!needPauseHandling(vmEntryGlobalObject))
+ if (m_suppressAllPauses)
return;
- Breakpoint breakpoint;
- bool didHitBreakpoint = false;
- bool pauseNow = m_pauseOnNextStatement;
+ intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame);
+ if (isBlacklisted(sourceID))
+ return;
+
+ DebuggerPausedScope debuggerPausedScope(*this);
+
+ bool pauseNow = m_pauseAtNextOpportunity;
pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame);
- intptr_t sourceID = DebuggerCallFrame::sourceIDForCallFrame(m_currentCallFrame);
+ bool didPauseForStep = pauseNow;
+ bool didHitBreakpoint = false;
+
+ Breakpoint breakpoint;
TextPosition position = DebuggerCallFrame::positionForCallFrame(m_currentCallFrame);
pauseNow |= didHitBreakpoint = hasBreakpoint(sourceID, position, &breakpoint);
m_lastExecutedLine = position.m_line.zeroBasedInt();
if (!pauseNow)
return;
- DebuggerCallFrameScope debuggerCallFrameScope(*this);
+ clearNextPauseState();
// 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;
+
+ JSGlobalObject* vmEntryGlobalObject = callFrame->vmEntryGlobalObject();
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)
+ if (!m_currentCallFrame)
return;
+
+ if (breakpoint.autoContinue) {
+ if (!didPauseForStep)
+ return;
+ didHitBreakpoint = false;
+ } else
+ m_pausingBreakpointID = breakpoint.id;
+ }
+
+ {
+ PauseReasonDeclaration reason(*this, didHitBreakpoint ? PausedForBreakpoint : m_reasonForPause);
+ handlePause(vmEntryGlobalObject, m_reasonForPause);
+ RELEASE_ASSERT(!scope.exception());
}
- handlePause(m_reasonForPause, vmEntryGlobalObject);
+ m_pausingBreakpointID = noBreakpointID;
- if (!m_pauseOnNextStatement && !m_pauseOnCallFrame) {
+ if (!m_pauseAtNextOpportunity && !m_pauseOnCallFrame) {
setSteppingMode(SteppingModeDisabled);
m_currentCallFrame = nullptr;
}
}
-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)) {
- m_pauseOnNextStatement = true;
+ if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasCatchHandler)) {
+ m_pauseAtNextOpportunity = true;
setSteppingMode(SteppingModeEnabled);
}
m_hasHandlerForExceptionCallback = true;
m_currentException = exception;
- updateCallFrameAndPauseIfNeeded(callFrame);
+ updateCallFrame(callFrame, AttemptPause);
m_currentException = JSValue();
m_hasHandlerForExceptionCallback = false;
}
@@ -675,8 +775,28 @@ void Debugger::atStatement(CallFrame* callFrame)
if (m_isPaused)
return;
+ m_pastFirstExpressionInStatement = false;
+
PauseReasonDeclaration reason(*this, PausedAtStatement);
- updateCallFrameAndPauseIfNeeded(callFrame);
+ updateCallFrame(callFrame, AttemptPause);
+}
+
+void Debugger::atExpression(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ // If this is the first call in a statement, then we would have paused at the statement.
+ if (!m_pastFirstExpressionInStatement) {
+ m_pastFirstExpressionInStatement = true;
+ return;
+ }
+
+ // Only pause at the next expression with step-in and step-out, not step-over.
+ bool shouldAttemptPause = m_pauseAtNextOpportunity || m_pauseOnStepOut;
+
+ PauseReasonDeclaration reason(*this, PausedAtExpression);
+ updateCallFrame(callFrame, shouldAttemptPause ? AttemptPause : NoPause);
}
void Debugger::callEvent(CallFrame* callFrame)
@@ -684,8 +804,7 @@ void Debugger::callEvent(CallFrame* callFrame)
if (m_isPaused)
return;
- PauseReasonDeclaration reason(*this, PausedAfterCall);
- updateCallFrameAndPauseIfNeeded(callFrame);
+ updateCallFrame(callFrame, NoPause);
}
void Debugger::returnEvent(CallFrame* callFrame)
@@ -693,18 +812,48 @@ void Debugger::returnEvent(CallFrame* callFrame)
if (m_isPaused)
return;
- PauseReasonDeclaration reason(*this, PausedBeforeReturn);
- updateCallFrameAndPauseIfNeeded(callFrame);
+ {
+ PauseReasonDeclaration reason(*this, PausedBeforeReturn);
+ updateCallFrame(callFrame, AttemptPause);
+ }
+
+ // Detach may have been called during pauseIfNeeded.
+ if (!m_currentCallFrame)
+ return;
+
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ CallFrame* callerFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+
+ // Returning from a call, there was at least one expression on the statement we are returning to.
+ m_pastFirstExpressionInStatement = true;
+
+ // Treat stepping over a return statement like a step-out.
+ if (m_currentCallFrame == m_pauseOnCallFrame) {
+ m_pauseOnCallFrame = callerFrame;
+ m_pauseOnStepOut = true;
+ }
+
+ updateCallFrame(callerFrame, NoPause);
+}
+
+void Debugger::unwindEvent(CallFrame* callFrame)
+{
+ if (m_isPaused)
+ return;
+
+ updateCallFrame(callFrame, NoPause);
- // detach may have been called during pauseIfNeeded
if (!m_currentCallFrame)
return;
- // Treat stepping over a return statement like stepping out.
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ CallFrame* callerFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+
+ // Treat stepping over an exception location like a step-out.
if (m_currentCallFrame == m_pauseOnCallFrame)
- m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+ m_pauseOnCallFrame = callerFrame;
- m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+ updateCallFrame(callerFrame, NoPause);
}
void Debugger::willExecuteProgram(CallFrame* callFrame)
@@ -712,14 +861,7 @@ 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);
+ updateCallFrame(callFrame, NoPause);
}
void Debugger::didExecuteProgram(CallFrame* callFrame)
@@ -728,17 +870,36 @@ void Debugger::didExecuteProgram(CallFrame* callFrame)
return;
PauseReasonDeclaration reason(*this, PausedAtEndOfProgram);
- updateCallFrameAndPauseIfNeeded(callFrame);
+ updateCallFrame(callFrame, AttemptPause);
- // Treat stepping over the end of a program like stepping out.
+ // Detach may have been called during pauseIfNeeded.
if (!m_currentCallFrame)
return;
+
+ VMEntryFrame* topVMEntryFrame = m_vm.topVMEntryFrame;
+ CallFrame* callerFrame = m_currentCallFrame->callerFrame(topVMEntryFrame);
+
+ // Returning from a program, could be eval(), there was at least one expression on the statement we are returning to.
+ m_pastFirstExpressionInStatement = true;
+
+ // Treat stepping over the end of a program like a step-out.
if (m_currentCallFrame == m_pauseOnCallFrame) {
- m_pauseOnCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
- if (!m_currentCallFrame)
- return;
+ m_pauseOnCallFrame = callerFrame;
+ m_pauseAtNextOpportunity = true;
}
- m_currentCallFrame = m_currentCallFrame->callerFrameSkippingVMEntrySentinel();
+
+ updateCallFrame(callerFrame, NoPause);
+
+ // Do not continue stepping into an unknown future program.
+ if (!m_currentCallFrame)
+ clearNextPauseState();
+}
+
+void Debugger::clearNextPauseState()
+{
+ m_pauseOnCallFrame = nullptr;
+ m_pauseAtNextOpportunity = false;
+ m_pauseOnStepOut = false;
}
void Debugger::didReachBreakpoint(CallFrame* callFrame)
@@ -746,16 +907,32 @@ void Debugger::didReachBreakpoint(CallFrame* callFrame)
if (m_isPaused)
return;
- PauseReasonDeclaration reason(*this, PausedForBreakpoint);
- m_pauseOnNextStatement = true;
+ PauseReasonDeclaration reason(*this, PausedForDebuggerStatement);
+ m_pauseAtNextOpportunity = true;
setSteppingMode(SteppingModeEnabled);
- updateCallFrameAndPauseIfNeeded(callFrame);
+ updateCallFrame(callFrame, AttemptPause);
+}
+
+DebuggerCallFrame& Debugger::currentDebuggerCallFrame()
+{
+ if (!m_currentDebuggerCallFrame)
+ m_currentDebuggerCallFrame = DebuggerCallFrame::create(m_currentCallFrame);
+ return *m_currentDebuggerCallFrame;
+}
+
+bool Debugger::isBlacklisted(SourceID sourceID) const
+{
+ return m_blacklistedScripts.contains(sourceID);
+}
+
+void Debugger::addToBlacklist(SourceID sourceID)
+{
+ m_blacklistedScripts.add(sourceID);
}
-DebuggerCallFrame* Debugger::currentDebuggerCallFrame() const
+void Debugger::clearBlacklist()
{
- ASSERT(m_currentDebuggerCallFrame);
- return m_currentDebuggerCallFrame.get();
+ m_blacklistedScripts.clear();
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/Debugger.h b/Source/JavaScriptCore/debugger/Debugger.h
index f7b734f37..3f51bb42d 100644
--- a/Source/JavaScriptCore/debugger/Debugger.h
+++ b/Source/JavaScriptCore/debugger/Debugger.h
@@ -19,21 +19,23 @@
*
*/
-#ifndef Debugger_h
-#define Debugger_h
+#pragma once
#include "Breakpoint.h"
+#include "CallData.h"
#include "DebuggerCallFrame.h"
+#include "DebuggerParseData.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,10 +45,12 @@ typedef ExecState CallFrame;
class JS_EXPORT_PRIVATE Debugger {
public:
- Debugger(bool isInWorkerThread = false);
+ Debugger(VM&);
virtual ~Debugger();
- JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
+ VM& vm() { return m_vm; }
+
+ JSC::DebuggerCallFrame& currentDebuggerCallFrame();
bool hasHandlerForExceptionCallback() const
{
ASSERT(m_reasonForPause == PausedForException);
@@ -58,21 +62,25 @@ public:
return m_currentException;
}
- bool needsExceptionCallbacks() const { return m_pauseOnExceptionsState != DontPauseOnExceptions; }
+ bool needsExceptionCallbacks() const { return m_breakpointsActivated && m_pauseOnExceptionsState != DontPauseOnExceptions; }
+ bool isInteractivelyDebugging() const { return m_breakpointsActivated; }
- 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 resolveBreakpoint(Breakpoint&, SourceProvider*);
+ BreakpointID setBreakpoint(Breakpoint&, bool& existing);
void removeBreakpoint(BreakpointID);
void clearBreakpoints();
- void setBreakpointsActivated(bool);
+
void activateBreakpoints() { setBreakpointsActivated(true); }
void deactivateBreakpoints() { setBreakpointsActivated(false); }
+ bool breakpointsActive() const { return m_breakpointsActivated; }
enum PauseOnExceptionsState {
DontPauseOnExceptions,
@@ -82,6 +90,19 @@ public:
PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; }
void setPauseOnExceptionsState(PauseOnExceptionsState);
+ enum ReasonForPause {
+ NotPaused,
+ PausedForException,
+ PausedAtStatement,
+ PausedAtExpression,
+ PausedBeforeReturn,
+ PausedAtEndOfProgram,
+ PausedForBreakpoint,
+ PausedForDebuggerStatement,
+ };
+ ReasonForPause reasonForPause() const { return m_reasonForPause; }
+ BreakpointID pausingBreakpointID() const { return m_pausingBreakpointID; }
+
void setPauseOnNextStatement(bool);
void breakProgram();
void continueProgram();
@@ -89,47 +110,56 @@ public:
void stepOverStatement();
void stepOutOfFunction();
- bool isPaused() { return m_isPaused; }
+ bool isBlacklisted(SourceID) const;
+ void addToBlacklist(SourceID);
+ void clearBlacklist();
+
+ 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 atExpression(CallFrame*);
void callEvent(CallFrame*);
void returnEvent(CallFrame*);
+ void unwindEvent(CallFrame*);
void willExecuteProgram(CallFrame*);
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 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;
@@ -155,6 +185,8 @@ private:
bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);
+ DebuggerParseData& debuggerParseData(SourceID, SourceProvider*);
+
void updateNeedForOpDebugCallbacks();
// These update functions are only needed because our current breakpoints are
@@ -162,9 +194,11 @@ private:
// 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*);
+ enum CallFrameUpdateAction { AttemptPause, NoPause };
+ void updateCallFrame(JSC::CallFrame*, CallFrameUpdateAction);
+ void updateCallFrameInternal(JSC::CallFrame*);
void pauseIfNeeded(JSC::CallFrame*);
+ void clearNextPauseState();
enum SteppingMode {
SteppingModeDisabled,
@@ -176,41 +210,48 @@ private:
BreakpointDisabled,
BreakpointEnabled
};
+ void setBreakpointsActivated(bool);
void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState);
void applyBreakpoints(CodeBlock*);
void toggleBreakpoint(Breakpoint&, BreakpointState);
void clearDebuggerRequests(JSGlobalObject*);
+ void clearParsedData();
- VM* m_vm;
+ VM& m_vm;
HashSet<JSGlobalObject*> m_globalObjects;
+ HashMap<SourceID, DebuggerParseData, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> m_parseDataMap;
+ HashSet<SourceID, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> m_blacklistedScripts;
PauseOnExceptionsState m_pauseOnExceptionsState;
- bool m_pauseOnNextStatement : 1;
+ bool m_pauseAtNextOpportunity : 1;
+ bool m_pauseOnStepOut : 1;
+ bool m_pastFirstExpressionInStatement : 1;
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;
- CallFrame* m_pauseOnCallFrame;
- CallFrame* m_currentCallFrame;
+ CallFrame* m_pauseOnCallFrame { nullptr };
+ CallFrame* m_currentCallFrame { nullptr };
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 DebuggerCallFrameScope;
+ ProfilingClient* m_profilingClient { nullptr };
+
+ friend class DebuggerPausedScope;
friend class TemporaryPausedState;
friend class LLIntOffsetsExtractor;
};
} // namespace JSC
-
-#endif // Debugger_h
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..47294b47e 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, 2016 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,19 +29,24 @@
#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 "JSCInlines.h"
+#include "JSFunction.h"
+#include "JSLexicalEnvironment.h"
+#include "JSWithScope.h"
#include "Parser.h"
+#include "ShadowChickenInlines.h"
#include "StackVisitor.h"
-#include "VMEntryScope.h"
+#include "StrongInlines.h"
namespace JSC {
class LineAndColumnFunctor {
public:
- StackVisitor::Status operator()(StackVisitor& visitor)
+ StackVisitor::Status operator()(StackVisitor& visitor) const
{
visitor->computeLineAndColumn(m_line, m_column);
return StackVisitor::Done;
@@ -51,39 +56,68 @@ public:
unsigned column() const { return m_column; }
private:
- unsigned m_line;
- unsigned m_column;
+ mutable unsigned m_line;
+ mutable unsigned m_column;
};
-DebuggerCallFrame::DebuggerCallFrame(CallFrame* callFrame)
- : m_callFrame(callFrame)
+Ref<DebuggerCallFrame> DebuggerCallFrame::create(CallFrame* callFrame)
+{
+ if (UNLIKELY(callFrame == callFrame->lexicalGlobalObject()->globalExec())) {
+ ShadowChicken::Frame emptyFrame;
+ RELEASE_ASSERT(!emptyFrame.isTailDeleted);
+ return adoptRef(*new DebuggerCallFrame(callFrame, emptyFrame));
+ }
+
+ Vector<ShadowChicken::Frame> frames;
+ callFrame->vm().shadowChicken().iterate(callFrame->vm(), callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
+ frames.append(frame);
+ return true;
+ });
+
+ RELEASE_ASSERT(frames.size());
+ ASSERT(!frames[0].isTailDeleted); // The top frame should never be tail deleted.
+
+ RefPtr<DebuggerCallFrame> currentParent = nullptr;
+ ExecState* exec = callFrame->lexicalGlobalObject()->globalExec();
+ // This walks the stack from the entry stack frame to the top of the stack.
+ for (unsigned i = frames.size(); i--; ) {
+ const ShadowChicken::Frame& frame = frames[i];
+ if (!frame.isTailDeleted)
+ exec = frame.frame;
+ Ref<DebuggerCallFrame> currentFrame = adoptRef(*new DebuggerCallFrame(exec, frame));
+ currentFrame->m_caller = currentParent;
+ currentParent = WTFMove(currentFrame);
+ }
+ return *currentParent;
+}
+
+DebuggerCallFrame::DebuggerCallFrame(CallFrame* callFrame, const ShadowChicken::Frame& frame)
+ : m_validMachineFrame(callFrame)
+ , m_shadowChickenFrame(frame)
{
- m_position = positionForCallFrame(m_callFrame);
+ m_position = currentPosition();
}
-PassRefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
+RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
{
ASSERT(isValid());
if (!isValid())
- return 0;
+ return nullptr;
- if (m_caller)
- return m_caller;
-
- CallFrame* callerFrame = m_callFrame->callerFrameSkippingVMEntrySentinel();
- if (!callerFrame)
- return 0;
-
- m_caller = DebuggerCallFrame::create(callerFrame);
return m_caller;
}
+ExecState* DebuggerCallFrame::globalExec()
+{
+ return scope()->globalObject()->globalExec();
+}
+
JSC::JSGlobalObject* DebuggerCallFrame::vmEntryGlobalObject() const
{
ASSERT(isValid());
if (!isValid())
- return 0;
- return m_callFrame->vmEntryGlobalObject();
+ return nullptr;
+ return m_validMachineFrame->vmEntryGlobalObject();
}
SourceID DebuggerCallFrame::sourceID() const
@@ -91,7 +125,9 @@ SourceID DebuggerCallFrame::sourceID() const
ASSERT(isValid());
if (!isValid())
return noSourceID;
- return sourceIDForCallFrame(m_callFrame);
+ if (isTailDeleted())
+ return m_shadowChickenFrame.codeBlock->ownerScriptExecutable()->sourceID();
+ return sourceIDForCallFrame(m_validMachineFrame);
}
String DebuggerCallFrame::functionName() const
@@ -99,19 +135,39 @@ String DebuggerCallFrame::functionName() const
ASSERT(isValid());
if (!isValid())
return String();
- JSObject* function = m_callFrame->callee();
- if (!function)
- return String();
- return getCalculatedDisplayName(m_callFrame, function);
+ VM& vm = m_validMachineFrame->vm();
+ if (isTailDeleted()) {
+ if (JSFunction* func = jsDynamicCast<JSFunction*>(vm, m_shadowChickenFrame.callee))
+ return func->calculatedDisplayName(vm);
+ return m_shadowChickenFrame.codeBlock->inferredName().data();
+ }
+
+ return m_validMachineFrame->friendlyFunctionName();
}
-JSScope* DebuggerCallFrame::scope() const
+DebuggerScope* DebuggerCallFrame::scope()
{
ASSERT(isValid());
if (!isValid())
- return 0;
- return m_callFrame->scope();
+ return nullptr;
+
+ if (!m_scope) {
+ VM& vm = m_validMachineFrame->vm();
+ JSScope* scope;
+ CodeBlock* codeBlock = m_validMachineFrame->codeBlock();
+ if (isTailDeleted())
+ scope = m_shadowChickenFrame.scope;
+ else if (codeBlock && codeBlock->scopeRegister().isValid())
+ scope = m_validMachineFrame->scope(codeBlock->scopeRegister().offset());
+ else if (JSCallee* callee = jsDynamicCast<JSCallee*>(vm, m_validMachineFrame->jsCallee()))
+ scope = callee->scope();
+ else
+ scope = m_validMachineFrame->lexicalGlobalObject()->globalLexicalEnvironment();
+
+ m_scope.set(vm, DebuggerScope::create(vm, scope));
+ }
+ return m_scope.get();
}
DebuggerCallFrame::Type DebuggerCallFrame::type() const
@@ -120,7 +176,10 @@ DebuggerCallFrame::Type DebuggerCallFrame::type() const
if (!isValid())
return ProgramType;
- if (m_callFrame->callee())
+ if (isTailDeleted())
+ return FunctionType;
+
+ if (jsDynamicCast<JSFunction*>(m_validMachineFrame->vm(), m_validMachineFrame->jsCallee()))
return FunctionType;
return ProgramType;
@@ -129,59 +188,120 @@ DebuggerCallFrame::Type DebuggerCallFrame::type() const
JSValue DebuggerCallFrame::thisValue() const
{
ASSERT(isValid());
- return thisValueForCallFrame(m_callFrame);
+ if (!isValid())
+ return jsUndefined();
+
+ CodeBlock* codeBlock = nullptr;
+ JSValue thisValue;
+ if (isTailDeleted()) {
+ thisValue = m_shadowChickenFrame.thisValue;
+ codeBlock = m_shadowChickenFrame.codeBlock;
+ } else {
+ thisValue = m_validMachineFrame->thisValue();
+ codeBlock = m_validMachineFrame->codeBlock();
+ }
+
+ if (!thisValue)
+ return jsUndefined();
+
+ ECMAMode ecmaMode = NotStrictMode;
+ if (codeBlock && codeBlock->isStrictMode())
+ ecmaMode = StrictMode;
+ return thisValue.toThis(m_validMachineFrame, ecmaMode);
}
// Evaluate some JavaScript code in the scope of this frame.
-JSValue DebuggerCallFrame::evaluate(const String& script, JSValue& exception) const
+JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
{
ASSERT(isValid());
- return evaluateWithCallFrame(m_callFrame, script, exception);
-}
-
-JSValue DebuggerCallFrame::evaluateWithCallFrame(CallFrame* callFrame, const String& script, JSValue& exception)
-{
+ CallFrame* callFrame = m_validMachineFrame;
if (!callFrame)
- return jsNull();
+ return jsUndefined();
- JSLockHolder lock(callFrame);
+ VM& vm = callFrame->vm();
+ JSLockHolder lock(vm);
+ auto catchScope = DECLARE_CATCH_SCOPE(vm);
+
+ CodeBlock* codeBlock = nullptr;
+ if (isTailDeleted())
+ codeBlock = m_shadowChickenFrame.codeBlock;
+ else
+ codeBlock = callFrame->codeBlock();
+ if (!codeBlock)
+ return jsUndefined();
+
+ DebuggerEvalEnabler evalEnabler(callFrame);
- if (!callFrame->codeBlock())
- return JSValue();
+ EvalContextType evalContextType;
- VM& vm = callFrame->vm();
- EvalExecutable* eval = EvalExecutable::create(callFrame, makeSource(script), callFrame->codeBlock()->isStrictMode());
- if (vm.exception()) {
- exception = vm.exception();
- vm.clearException();
+ if (isFunctionParseMode(codeBlock->unlinkedCodeBlock()->parseMode()))
+ evalContextType = EvalContextType::FunctionEvalContext;
+ else if (codeBlock->unlinkedCodeBlock()->codeType() == EvalCode)
+ evalContextType = codeBlock->unlinkedCodeBlock()->evalContextType();
+ else
+ evalContextType = EvalContextType::None;
+
+ VariableEnvironment variablesUnderTDZ;
+ JSScope::collectClosureVariablesUnderTDZ(scope()->jsScope(), variablesUnderTDZ);
+
+ auto* eval = DirectEvalExecutable::create(callFrame, makeSource(script, callFrame->callerSourceOrigin()), codeBlock->isStrictMode(), codeBlock->unlinkedCodeBlock()->derivedContextType(), codeBlock->unlinkedCodeBlock()->isArrowFunction(), evalContextType, &variablesUnderTDZ);
+ if (UNLIKELY(catchScope.exception())) {
+ exception = catchScope.exception();
+ catchScope.clearException();
return jsUndefined();
}
- JSValue thisValue = thisValueForCallFrame(callFrame);
- JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, callFrame->scope());
- if (vm.exception()) {
- exception = vm.exception();
- vm.clearException();
+ JSGlobalObject* globalObject = callFrame->vmEntryGlobalObject();
+ if (scopeExtensionObject) {
+ JSScope* ignoredPreviousScope = globalObject->globalScope();
+ globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, scopeExtensionObject, ignoredPreviousScope));
}
+
+ JSValue thisValue = this->thisValue();
+ JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, scope()->jsScope());
+ if (UNLIKELY(catchScope.exception())) {
+ exception = catchScope.exception();
+ catchScope.clearException();
+ }
+
+ if (scopeExtensionObject)
+ globalObject->clearGlobalScopeExtension();
+
ASSERT(result);
return result;
}
void DebuggerCallFrame::invalidate()
{
- m_callFrame = nullptr;
- RefPtr<DebuggerCallFrame> frame = m_caller.release();
+ RefPtr<DebuggerCallFrame> frame = this;
while (frame) {
- frame->m_callFrame = nullptr;
- frame = frame->m_caller.release();
+ frame->m_validMachineFrame = nullptr;
+ if (frame->m_scope) {
+ frame->m_scope->invalidateChain();
+ frame->m_scope.clear();
+ }
+ frame = WTFMove(frame->m_caller);
}
}
-TextPosition DebuggerCallFrame::positionForCallFrame(CallFrame* callFrame)
+TextPosition DebuggerCallFrame::currentPosition()
{
- if (!callFrame)
+ if (!m_validMachineFrame)
return TextPosition();
+ if (isTailDeleted()) {
+ CodeBlock* codeBlock = m_shadowChickenFrame.codeBlock;
+ if (std::optional<unsigned> bytecodeOffset = codeBlock->bytecodeOffsetFromCallSiteIndex(m_shadowChickenFrame.callSiteIndex)) {
+ return TextPosition(OrdinalNumber::fromOneBasedInt(codeBlock->lineNumberForBytecodeOffset(*bytecodeOffset)),
+ OrdinalNumber::fromOneBasedInt(codeBlock->columnNumberForBytecodeOffset(*bytecodeOffset)));
+ }
+ }
+
+ return positionForCallFrame(m_validMachineFrame);
+}
+
+TextPosition DebuggerCallFrame::positionForCallFrame(CallFrame* callFrame)
+{
LineAndColumnFunctor functor;
callFrame->iterate(functor);
return TextPosition(OrdinalNumber::fromOneBasedInt(functor.line()), OrdinalNumber::fromOneBasedInt(functor.column()));
@@ -193,20 +313,7 @@ SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* 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;
+ return codeBlock->ownerScriptExecutable()->sourceID();
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerCallFrame.h b/Source/JavaScriptCore/debugger/DebuggerCallFrame.h
index 09c3fb9d8..462465854 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.
*
@@ -26,30 +26,29 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DebuggerCallFrame_h
-#define DebuggerCallFrame_h
+#pragma once
#include "CallFrame.h"
#include "DebuggerPrimitives.h"
-#include <wtf/PassRefPtr.h>
+#include "ShadowChicken.h"
+#include "Strong.h"
+#include <wtf/NakedPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/text/TextPosition.h>
namespace JSC {
+class DebuggerScope;
+class Exception;
+
class DebuggerCallFrame : public RefCounted<DebuggerCallFrame> {
public:
enum Type { ProgramType, FunctionType };
- static PassRefPtr<DebuggerCallFrame> create(CallFrame* callFrame)
- {
- return adoptRef(new DebuggerCallFrame(callFrame));
- }
-
- JS_EXPORT_PRIVATE explicit DebuggerCallFrame(CallFrame*);
+ static Ref<DebuggerCallFrame> create(CallFrame*);
- JS_EXPORT_PRIVATE PassRefPtr<DebuggerCallFrame> callerFrame();
- ExecState* exec() const { return m_callFrame; }
+ JS_EXPORT_PRIVATE RefPtr<DebuggerCallFrame> callerFrame();
+ ExecState* globalExec();
JS_EXPORT_PRIVATE SourceID sourceID() const;
// line and column are in base 0 e.g. the first line is line 0.
@@ -58,29 +57,34 @@ 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 evaluateWithScopeExtension(const String&, JSObject* scopeExtensionObject, NakedPtr<Exception>&);
- bool isValid() const { return !!m_callFrame; }
+ bool isValid() const { return !!m_validMachineFrame || isTailDeleted(); }
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 JSValue evaluateWithCallFrame(CallFrame*, const String& script, JSValue& exception);
+ JS_EXPORT_PRIVATE TextPosition currentPosition();
JS_EXPORT_PRIVATE static TextPosition positionForCallFrame(CallFrame*);
JS_EXPORT_PRIVATE static SourceID sourceIDForCallFrame(CallFrame*);
- static JSValue thisValueForCallFrame(CallFrame*);
+
+ bool isTailDeleted() const { return m_shadowChickenFrame.isTailDeleted; }
private:
- CallFrame* m_callFrame;
+ DebuggerCallFrame(CallFrame*, const ShadowChicken::Frame&);
+
+ CallFrame* m_validMachineFrame;
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;
+ ShadowChicken::Frame m_shadowChickenFrame;
};
} // 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..0e4c4cfac
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerEvalEnabler.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#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
diff --git a/Source/JavaScriptCore/debugger/DebuggerLocation.cpp b/Source/JavaScriptCore/debugger/DebuggerLocation.cpp
new file mode 100644
index 000000000..b139720b4
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerLocation.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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 "DebuggerLocation.h"
+
+#include "ScriptExecutable.h"
+
+namespace JSC {
+
+DebuggerLocation::DebuggerLocation(ScriptExecutable* executable)
+{
+ if (executable->isHostFunction())
+ return;
+
+ sourceID = executable->sourceID();
+ line = executable->firstLine();
+ column = executable->startColumn();
+ url = executable->sourceURL();
+ if (url.isEmpty())
+ url = executable->source().provider()->sourceURL();
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerLocation.h b/Source/JavaScriptCore/debugger/DebuggerLocation.h
new file mode 100644
index 000000000..3e1c4166b
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerLocation.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "DebuggerPrimitives.h"
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+class ScriptExecutable;
+
+struct DebuggerLocation {
+
+ DebuggerLocation() { }
+ DebuggerLocation(const String& url, intptr_t sourceID, unsigned line, unsigned column)
+ : url(url)
+ , sourceID(sourceID)
+ , line(line)
+ , column(column)
+ { }
+
+ DebuggerLocation(ScriptExecutable*);
+
+ String url;
+ intptr_t sourceID { noSourceID };
+ unsigned line { 0 };
+ unsigned column { 0 };
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerParseData.cpp b/Source/JavaScriptCore/debugger/DebuggerParseData.cpp
new file mode 100644
index 000000000..48f939f10
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerParseData.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2016 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 "DebuggerParseData.h"
+
+#include "Parser.h"
+
+namespace JSC {
+
+std::optional<JSTextPosition> DebuggerPausePositions::breakpointLocationForLineColumn(int line, int column)
+{
+ unsigned start = 0;
+ unsigned end = m_positions.size();
+ while (start != end) {
+ unsigned middle = start + ((end - start) / 2);
+ DebuggerPausePosition& pausePosition = m_positions[middle];
+ int pauseLine = pausePosition.position.line;
+ int pauseColumn = pausePosition.position.offset - pausePosition.position.lineStartOffset;
+
+ if (line < pauseLine) {
+ end = middle;
+ continue;
+ }
+ if (line > pauseLine) {
+ start = middle + 1;
+ continue;
+ }
+
+ if (column == pauseColumn) {
+ // Found an exact position match. Roll forward if this was a function Entry.
+ // We are guarenteed to have a Leave for an Entry so we don't need to bounds check.
+ while (true) {
+ if (pausePosition.type != DebuggerPausePositionType::Enter)
+ return std::optional<JSTextPosition>(pausePosition.position);
+ pausePosition = m_positions[middle++];
+ }
+ }
+
+ if (column < pauseColumn)
+ end = middle;
+ else
+ start = middle + 1;
+ }
+
+ // Past the end, no possible pause locations.
+ if (start >= m_positions.size())
+ return std::nullopt;
+
+ // If the next location is a function Entry we will need to decide if we should go into
+ // the function or go past the function. We decide to go into the function if the
+ // input is on the same line as the function entry. For example:
+ //
+ // 1. x;
+ // 2.
+ // 3. function foo() {
+ // 4. x;
+ // 5. }
+ // 6.
+ // 7. x;
+ //
+ // If the input was line 2, skip past functions to pause on line 7.
+ // If the input was line 3, go into the function to pause on line 4.
+
+ // Valid pause location. Use it.
+ DebuggerPausePosition& firstSlidePosition = m_positions[start];
+ if (firstSlidePosition.type != DebuggerPausePositionType::Enter)
+ return std::optional<JSTextPosition>(firstSlidePosition.position);
+
+ // Determine if we should enter this function or skip past it.
+ // If entryStackSize is > 0 we are skipping functions.
+ bool shouldEnterFunction = firstSlidePosition.position.line == line;
+ int entryStackSize = shouldEnterFunction ? 0 : 1;
+ for (unsigned i = start + 1; i < m_positions.size(); ++i) {
+ DebuggerPausePosition& slidePosition = m_positions[i];
+ ASSERT(entryStackSize >= 0);
+
+ // Already skipping functions.
+ if (entryStackSize) {
+ if (slidePosition.type == DebuggerPausePositionType::Enter)
+ entryStackSize++;
+ else if (slidePosition.type == DebuggerPausePositionType::Leave)
+ entryStackSize--;
+ continue;
+ }
+
+ // Start skipping functions.
+ if (slidePosition.type == DebuggerPausePositionType::Enter) {
+ entryStackSize++;
+ continue;
+ }
+
+ // Found pause position.
+ return std::optional<JSTextPosition>(slidePosition.position);
+ }
+
+ // No pause positions found.
+ return std::nullopt;
+}
+
+void DebuggerPausePositions::sort()
+{
+ std::sort(m_positions.begin(), m_positions.end(), [] (const DebuggerPausePosition& a, const DebuggerPausePosition& b) {
+ return a.position.offset < b.position.offset;
+ });
+}
+
+typedef enum { Program, Module } DebuggerParseInfoTag;
+template <DebuggerParseInfoTag T> struct DebuggerParseInfo { };
+
+template <> struct DebuggerParseInfo<Program> {
+ typedef JSC::ProgramNode RootNode;
+ static const SourceParseMode parseMode = SourceParseMode::ProgramMode;
+ static const JSParserStrictMode strictMode = JSParserStrictMode::NotStrict;
+ static const JSParserScriptMode scriptMode = JSParserScriptMode::Classic;
+};
+
+template <> struct DebuggerParseInfo<Module> {
+ typedef JSC::ModuleProgramNode RootNode;
+ static const SourceParseMode parseMode = SourceParseMode::ModuleEvaluateMode;
+ static const JSParserStrictMode strictMode = JSParserStrictMode::Strict;
+ static const JSParserScriptMode scriptMode = JSParserScriptMode::Module;
+};
+
+template <DebuggerParseInfoTag T>
+bool gatherDebuggerParseData(VM& vm, const SourceCode& source, DebuggerParseData& debuggerParseData)
+{
+ typedef typename DebuggerParseInfo<T>::RootNode RootNode;
+ SourceParseMode parseMode = DebuggerParseInfo<T>::parseMode;
+ JSParserStrictMode strictMode = DebuggerParseInfo<T>::strictMode;
+ JSParserScriptMode scriptMode = DebuggerParseInfo<T>::scriptMode;
+
+ ParserError error;
+ std::unique_ptr<RootNode> rootNode = parse<RootNode>(&vm, source, Identifier(),
+ JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, parseMode, SuperBinding::NotNeeded,
+ error, nullptr, ConstructorKind::None, DerivedContextType::None, EvalContextType::None,
+ &debuggerParseData);
+ if (!rootNode)
+ return false;
+
+ debuggerParseData.pausePositions.sort();
+
+ return true;
+}
+
+bool gatherDebuggerParseDataForSource(VM& vm, SourceProvider* provider, DebuggerParseData& debuggerParseData)
+{
+ ASSERT(provider);
+ int startLine = provider->startPosition().m_line.oneBasedInt();
+ int startColumn = provider->startPosition().m_column.oneBasedInt();
+ SourceCode completeSource(*provider, startLine, startColumn);
+
+ switch (provider->sourceType()) {
+ case SourceProviderSourceType::Program:
+ return gatherDebuggerParseData<Program>(vm, completeSource, debuggerParseData);
+ case SourceProviderSourceType::Module:
+ return gatherDebuggerParseData<Module>(vm, completeSource, debuggerParseData);
+ default:
+ return false;
+ }
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerParseData.h b/Source/JavaScriptCore/debugger/DebuggerParseData.h
new file mode 100644
index 000000000..ecc78d0d4
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerParseData.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "ParserTokens.h"
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class SourceProvider;
+class VM;
+
+enum class DebuggerPausePositionType { Enter, Leave, Pause };
+struct DebuggerPausePosition {
+ DebuggerPausePositionType type;
+ JSTextPosition position;
+};
+
+class DebuggerPausePositions {
+public:
+ DebuggerPausePositions() { }
+ ~DebuggerPausePositions() { }
+
+ void appendPause(const JSTextPosition& position)
+ {
+ m_positions.append({ DebuggerPausePositionType::Pause, position });
+ }
+
+ void appendEntry(const JSTextPosition& position)
+ {
+ m_positions.append({ DebuggerPausePositionType::Enter, position });
+ }
+
+ void appendLeave(const JSTextPosition& position)
+ {
+ m_positions.append({ DebuggerPausePositionType::Leave, position });
+ }
+
+ std::optional<JSTextPosition> breakpointLocationForLineColumn(int line, int column);
+
+ void sort();
+
+private:
+ Vector<DebuggerPausePosition> m_positions;
+};
+
+
+struct DebuggerParseData {
+ DebuggerParseData() { }
+ ~DebuggerParseData() { }
+
+ DebuggerPausePositions pausePositions;
+};
+
+bool gatherDebuggerParseDataForSource(VM&, SourceProvider*, DebuggerParseData&);
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/debugger/DebuggerPrimitives.h b/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
index 635a5bfe8..959f8bd14 100644
--- a/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
+++ b/Source/JavaScriptCore/debugger/DebuggerPrimitives.h
@@ -23,8 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DebuggerPrimitives_h
-#define DebuggerPrimitives_h
+#pragma once
+
+#include <stddef.h>
namespace JSC {
@@ -35,5 +36,3 @@ 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..bec6f9f9c
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerScope.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008-2009, 2014, 2016 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::create(VM& vm, JSScope* scope)
+{
+ Structure* structure = scope->globalObject()->debuggerScopeStructure();
+ DebuggerScope* debuggerScope = new (NotNull, allocateCell<DebuggerScope>(vm.heap)) DebuggerScope(vm, structure, scope);
+ debuggerScope->finishCreation(vm);
+ return debuggerScope;
+}
+
+DebuggerScope::DebuggerScope(VM& vm, Structure* structure, JSScope* scope)
+ : JSNonFinalObject(vm, structure)
+{
+ 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);
+ // We cannot assert that scope->isValid() because the TypeProfiler may encounter an invalidated
+ // DebuggerScope in its log entries. We just need to handle it appropriately as below.
+ 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);
+ 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;
+}
+
+bool DebuggerScope::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+{
+ DebuggerScope* scope = jsCast<DebuggerScope*>(cell);
+ ASSERT(scope->isValid());
+ if (!scope->isValid())
+ return false;
+ JSObject* thisObject = JSScope::objectAtScope(scope->jsScope());
+ slot.setThisValue(JSValue(thisObject));
+ return 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();
+}
+
+String DebuggerScope::name() const
+{
+ SymbolTable* symbolTable = m_scope->symbolTable(*vm());
+ if (!symbolTable)
+ return String();
+
+ CodeBlock* codeBlock = symbolTable->rareDataCodeBlock();
+ if (!codeBlock)
+ return String();
+
+ return String::fromUTF8(codeBlock->inferredName());
+}
+
+DebuggerLocation DebuggerScope::location() const
+{
+ SymbolTable* symbolTable = m_scope->symbolTable(*vm());
+ if (!symbolTable)
+ return DebuggerLocation();
+
+ CodeBlock* codeBlock = symbolTable->rareDataCodeBlock();
+ if (!codeBlock)
+ return DebuggerLocation();
+
+ ScriptExecutable* executable = codeBlock->ownerScriptExecutable();
+ return DebuggerLocation(executable);
+}
+
+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..479b18651
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/DebuggerScope.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008-2009, 2014, 2016 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.
+ */
+
+#pragma once
+
+#include "DebuggerLocation.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;
+
+ JS_EXPORT_PRIVATE static DebuggerScope* create(VM& vm, JSScope* scope);
+
+ static void visitChildren(JSCell*, SlotVisitor&);
+ static String className(const JSObject*);
+ static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
+ static bool 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;
+
+ String name() const;
+ DebuggerLocation location() const;
+
+ JSValue caughtValue(ExecState*) const;
+
+private:
+ DebuggerScope(VM&, Structure*, JSScope*);
+ 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
diff --git a/Source/JavaScriptCore/debugger/ScriptProfilingScope.h b/Source/JavaScriptCore/debugger/ScriptProfilingScope.h
new file mode 100644
index 000000000..e40337fe9
--- /dev/null
+++ b/Source/JavaScriptCore/debugger/ScriptProfilingScope.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#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 };
+ std::optional<double> m_startTime;
+ ProfilingReason m_reason;
+};
+
+} // namespace JSC