summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp')
-rw-r--r--Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp253
1 files changed, 180 insertions, 73 deletions
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