diff options
Diffstat (limited to 'Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp')
-rw-r--r-- | Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp | 253 |
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 |