[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / JavaScriptCore / debugger / DebuggerCallFrame.cpp
index 5884811..7f82127 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
  *
 #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 "StrongInlines.h"
 
 namespace JSC {
 
-String DebuggerCallFrame::functionName() const
+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(CallFrame* callFrame)
 {
-    if (!m_callFrame->codeBlock())
-        return String();
+    if (UNLIKELY(callFrame == callFrame->lexicalGlobalObject()->globalExec())) {
+        ShadowChicken::Frame emptyFrame;
+        RELEASE_ASSERT(!emptyFrame.isTailDeleted);
+        return adoptRef(*new DebuggerCallFrame(callFrame, emptyFrame));
+    }
 
-    if (!m_callFrame->callee())
-        return String();
+    Vector<ShadowChicken::Frame> frames;
+    callFrame->vm().shadowChicken().iterate(callFrame->vm(), callFrame, [&] (const ShadowChicken::Frame& frame) -> bool {
+        frames.append(frame);
+        return true;
+    });
 
-    JSObject* function = m_callFrame->callee();
-    if (!function || !function->inherits(&JSFunction::s_info))
-        return String();
-    return jsCast<JSFunction*>(function)->name(m_callFrame);
+    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;
 }
-    
-String DebuggerCallFrame::calculatedFunctionName() const
+
+DebuggerCallFrame::DebuggerCallFrame(CallFrame* callFrame, const ShadowChicken::Frame& frame)
+    : m_validMachineFrame(callFrame)
+    , m_shadowChickenFrame(frame)
 {
-    if (!m_callFrame->codeBlock())
-        return String();
+    m_position = currentPosition();
+}
 
-    JSObject* function = m_callFrame->callee();
+RefPtr<DebuggerCallFrame> DebuggerCallFrame::callerFrame()
+{
+    ASSERT(isValid());
+    if (!isValid())
+        return nullptr;
 
-    if (!function)
+    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();
 
-    return getCalculatedDisplayName(m_callFrame, function);
+    if (isTailDeleted()) {
+        if (JSFunction* func = jsDynamicCast<JSFunction*>(m_shadowChickenFrame.callee))
+            return func->calculatedDisplayName(m_validMachineFrame->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*>(m_validMachineFrame->callee()))
+            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
 {
-    if (m_callFrame->callee())
+    ASSERT(isValid());
+    if (!isValid())
+        return ProgramType;
+
+    if (isTailDeleted())
+        return FunctionType;
+
+    if (jsDynamicCast<JSFunction*>(m_validMachineFrame->callee()))
         return FunctionType;
 
     return ProgramType;
 }
 
-JSObject* DebuggerCallFrame::thisObject() const
+JSValue DebuggerCallFrame::thisValue() const
 {
-    CodeBlock* codeBlock = m_callFrame->codeBlock();
-    if (!codeBlock)
-        return 0;
+    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();
+    }
 
-    JSValue thisValue = m_callFrame->uncheckedR(codeBlock->thisRegister()).jsValue();
-    if (!thisValue.isObject())
-        return 0;
+    if (!thisValue)
+        return jsUndefined();
 
-    return asObject(thisValue);
+    ECMAMode ecmaMode = NotStrictMode;
+    if (codeBlock && codeBlock->isStrictMode())
+        ecmaMode = StrictMode;
+    return thisValue.toThis(m_validMachineFrame, ecmaMode);
 }
 
-JSValue DebuggerCallFrame::evaluate(const String& script, JSValue& exception) const
+// Evaluate some JavaScript code in the scope of this frame.
+JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
 {
-    if (!m_callFrame->codeBlock())
-        return JSValue();
+    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();
     
-    VM& vm = m_callFrame->vm();
-    EvalExecutable* eval = EvalExecutable::create(m_callFrame, makeSource(script), m_callFrame->codeBlock()->isStrictMode());
-    if (vm.exception) {
-        exception = vm.exception;
-        vm.exception = JSValue();
+    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);
+
+    EvalExecutable* eval = DirectEvalExecutable::create(callFrame, makeSource(script), 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 result = vm.interpreter->execute(eval, m_callFrame, thisObject(), m_callFrame->scope());
-    if (vm.exception) {
-        exception = vm.exception;
-        vm.exception = JSValue();
+    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()
+{
+    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()));
+}
+
+SourceID DebuggerCallFrame::sourceIDForCallFrame(CallFrame* callFrame)
+{
+    ASSERT(callFrame);
+    CodeBlock* codeBlock = callFrame->codeBlock();
+    if (!codeBlock)
+        return noSourceID;
+    return codeBlock->ownerScriptExecutable()->sourceID();
+}
+
 } // namespace JSC