[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / JavaScriptCore / debugger / DebuggerCallFrame.cpp
index 897a246..7f82127 100644 (file)
@@ -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.
  *
  * 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 "DebuggerCallFrame.h"
 
 #include "CodeBlock.h"
+#include "DebuggerEvalEnabler.h"
+#include "DebuggerScope.h"
 #include "Interpreter.h"
-#include "JSActivation.h"
-#include "JSFunction.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)
+    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,27 +135,38 @@ String DebuggerCallFrame::functionName() const
     ASSERT(isValid());
     if (!isValid())
         return String();
-    JSObject* function = m_callFrame->callee();
-    if (!function)
-        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();
 }
 
-JSScope* DebuggerCallFrame::scope() const
+DebuggerScope* DebuggerCallFrame::scope()
 {
     ASSERT(isValid());
     if (!isValid())
-        return 0;
-
-    CodeBlock* codeBlock = m_callFrame->codeBlock();
-    if (codeBlock && codeBlock->needsActivation() && !m_callFrame->hasActivation()) {
-        JSActivation* activation = JSActivation::create(*codeBlock->vm(), m_callFrame, codeBlock);
-        m_callFrame->setActivation(activation);
-        m_callFrame->setScope(activation);
+        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_callFrame->scope();
+    return m_scope.get();
 }
 
 DebuggerCallFrame::Type DebuggerCallFrame::type() const
@@ -128,7 +175,10 @@ DebuggerCallFrame::Type DebuggerCallFrame::type() const
     if (!isValid())
         return ProgramType;
 
-    if (m_callFrame->callee())
+    if (isTailDeleted())
+        return FunctionType;
+
+    if (jsDynamicCast<JSFunction*>(m_validMachineFrame->callee()))
         return FunctionType;
 
     return ProgramType;
@@ -137,55 +187,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)
+JSValue DebuggerCallFrame::evaluateWithScopeExtension(const String& script, JSObject* scopeExtensionObject, NakedPtr<Exception>& exception)
 {
     ASSERT(isValid());
-    CallFrame* callFrame = m_callFrame;
+    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);
+
+    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();
     }
 
-    JSValue thisValue = thisValueForCallFrame(callFrame);
-    JSValue result = vm.interpreter->execute(eval, callFrame, thisValue, 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()));
@@ -197,20 +312,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