DebuggerCallFrame.cpp [plain text]
#include "config.h"
#include "DebuggerCallFrame.h"
#include "CatchScope.h"
#include "CodeBlock.h"
#include "DebuggerEvalEnabler.h"
#include "DebuggerScope.h"
#include "Interpreter.h"
#include "JSCInlines.h"
#include "JSFunction.h"
#include "JSLexicalEnvironment.h"
#include "JSWithScope.h"
#include "Parser.h"
#include "ShadowChickenInlines.h"
#include "StackVisitor.h"
#include "StrongInlines.h"
namespace JSC {
class LineAndColumnFunctor {
public:
StackVisitor::Status operator()(StackVisitor& visitor) const
{
visitor->computeLineAndColumn(m_line, m_column);
return StackVisitor::Done;
}
unsigned line() const { return m_line; }
unsigned column() const { return m_column; }
private:
mutable unsigned m_line;
mutable unsigned m_column;
};
Ref<DebuggerCallFrame> DebuggerCallFrame::create(VM& vm, CallFrame* callFrame)
{
if (UNLIKELY(callFrame == callFrame->wasmAwareLexicalGlobalObject(vm)->globalExec())) {
ShadowChicken::Frame emptyFrame;
RELEASE_ASSERT(!emptyFrame.isTailDeleted);
return adoptRef(*new DebuggerCallFrame(vm, callFrame, emptyFrame));
}
Vector<ShadowChicken::Frame> frames;
vm.shadowChicken().iterate(vm, callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
frames.append(frame);
return true;
});
RELEASE_ASSERT(frames.size());
ASSERT(!frames[0].isTailDeleted);
RefPtr<DebuggerCallFrame> currentParent = nullptr;
ExecState* exec = callFrame->wasmAwareLexicalGlobalObject(vm)->globalExec();
for (unsigned i = frames.size(); i--; ) {
const ShadowChicken::Frame& frame = frames[i];
if (!frame.isTailDeleted)
exec = frame.frame;
Ref<DebuggerCallFrame> currentFrame = adoptRef(*new DebuggerCallFrame(vm, exec, frame));
currentFrame->m_caller = currentParent;
currentParent = WTFMove(currentFrame);
}
return *currentParent;
}
DebuggerCallFrame::DebuggerCallFrame(VM& vm, CallFrame* callFrame, const ShadowChicken::Frame& frame)
: m_validMachineFrame(callFrame)
, m_shadowChickenFrame(frame)
{
m_position = currentPosition(vm);
}
RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
{
ASSERT(isValid());
if (!isValid())
return nullptr;
return m_caller;
}
ExecState* DebuggerCallFrame::globalExec()
{
return scope()->globalObject()->globalExec();
}
JSC::JSGlobalObject* DebuggerCallFrame::vmEntryGlobalObject() const
{
ASSERT(isValid());
if (!isValid())
return nullptr;
return m_validMachineFrame->vmEntryGlobalObject();
}
SourceID DebuggerCallFrame::sourceID() const
{
ASSERT(isValid());
if (!isValid())
return noSourceID;
if (isTailDeleted())
return m_shadowChickenFrame.codeBlock->ownerScriptExecutable()->sourceID();
return sourceIDForCallFrame(m_validMachineFrame);
}
String DebuggerCallFrame::functionName() const
{
ASSERT(isValid());
if (!isValid())
return String();
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();
}
DebuggerScope* DebuggerCallFrame::scope()
{
ASSERT(isValid());
if (!isValid())
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
{
ASSERT(isValid());
if (!isValid())
return ProgramType;
if (isTailDeleted())
return FunctionType;
if (jsDynamicCast<JSFunction*>(m_validMachineFrame->vm(), m_validMachineFrame->jsCallee()))
return FunctionType;
return ProgramType;
}
JSValue DebuggerCallFrame::thisValue() const
{
ASSERT(isValid());
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);
}
JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
{
ASSERT(isValid());
CallFrame* callFrame = m_validMachineFrame;
if (!callFrame)
return jsUndefined();
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);
EvalContextType evalContextType;
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();
}
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()
{
RefPtr<DebuggerCallFrame> frame = this;
while (frame) {
frame->m_validMachineFrame = nullptr;
if (frame->m_scope) {
frame->m_scope->invalidateChain();
frame->m_scope.clear();
}
frame = WTFMove(frame->m_caller);
}
}
TextPosition DebuggerCallFrame::currentPosition(VM& vm)
{
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(vm, m_validMachineFrame);
}
TextPosition DebuggerCallFrame::positionForCallFrame(VM& vm, CallFrame* callFrame)
{
LineAndColumnFunctor functor;
StackVisitor::visit(callFrame, &vm, functor);
return TextPosition(OrdinalNumber::fromOneBasedInt(functor.line()), OrdinalNumber::fromOneBasedInt(functor.column()));
}
SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* callFrame)
{
ASSERT(callFrame);
CodeBlock* codeBlock = callFrame->codeBlock();
if (!codeBlock)
return noSourceID;
return codeBlock->ownerScriptExecutable()->sourceID();
}
}