]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - interpreter/StackVisitor.cpp
JavaScriptCore-7600.1.4.9.tar.gz
[apple/javascriptcore.git] / interpreter / StackVisitor.cpp
diff --git a/interpreter/StackVisitor.cpp b/interpreter/StackVisitor.cpp
new file mode 100644 (file)
index 0000000..19f95b9
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "StackVisitor.h"
+
+#include "Arguments.h"
+#include "CallFrameInlines.h"
+#include "Executable.h"
+#include "Interpreter.h"
+#include "JSCInlines.h"
+#include <wtf/DataLog.h>
+
+namespace JSC {
+
+StackVisitor::StackVisitor(CallFrame* startFrame)
+{
+    m_frame.m_index = 0;
+    readFrame(startFrame);
+}
+
+void StackVisitor::gotoNextFrame()
+{
+#if ENABLE(DFG_JIT)
+    if (m_frame.isInlinedFrame()) {
+        InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame();
+        CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller;
+        readInlinedFrame(m_frame.callFrame(), callerCodeOrigin);
+
+    } else
+#endif // ENABLE(DFG_JIT)
+        readFrame(m_frame.callerFrame());
+}
+
+void StackVisitor::readFrame(CallFrame* callFrame)
+{
+    ASSERT(!callFrame->isVMEntrySentinel());
+    if (!callFrame) {
+        m_frame.setToEnd();
+        return;
+    }
+
+#if !ENABLE(DFG_JIT)
+    readNonInlinedFrame(callFrame);
+
+#else // !ENABLE(DFG_JIT)
+    // If the frame doesn't have a code block, then it's not a DFG frame.
+    // Hence, we're not at an inlined frame.
+    CodeBlock* codeBlock = callFrame->codeBlock();
+    if (!codeBlock) {
+        readNonInlinedFrame(callFrame);
+        return;
+    }
+
+    // If the code block does not have any code origins, then there's no
+    // inlining. Hence, we're not at an inlined frame.
+    if (!codeBlock->hasCodeOrigins()) {
+        readNonInlinedFrame(callFrame);
+        return;
+    }
+
+    unsigned index = callFrame->locationAsCodeOriginIndex();
+    ASSERT(codeBlock->canGetCodeOrigin(index));
+    if (!codeBlock->canGetCodeOrigin(index)) {
+        // See assertion above. In release builds, we try to protect ourselves
+        // from crashing even though stack walking will be goofed up.
+        m_frame.setToEnd();
+        return;
+    }
+
+    CodeOrigin codeOrigin = codeBlock->codeOrigin(index);
+    if (!codeOrigin.inlineCallFrame) {
+        readNonInlinedFrame(callFrame, &codeOrigin);
+        return;
+    }
+
+    readInlinedFrame(callFrame, &codeOrigin);
+#endif // !ENABLE(DFG_JIT)
+}
+
+void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
+{
+    m_frame.m_callFrame = callFrame;
+    m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis();
+    m_frame.m_callerFrame = callFrame->callerFrameSkippingVMEntrySentinel();
+    m_frame.m_callee = callFrame->callee();
+    m_frame.m_scope = callFrame->scope();
+    m_frame.m_codeBlock = callFrame->codeBlock();
+    m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
+        : codeOrigin ? codeOrigin->bytecodeIndex
+        : callFrame->locationAsBytecodeOffset();
+#if ENABLE(DFG_JIT)
+    m_frame.m_inlineCallFrame = 0;
+#endif
+}
+
+#if ENABLE(DFG_JIT)
+static int inlinedFrameOffset(CodeOrigin* codeOrigin)
+{
+    InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
+    int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
+    return frameOffset;
+}
+
+void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
+{
+    ASSERT(codeOrigin);
+    ASSERT(!callFrame->isVMEntrySentinel());
+
+    int frameOffset = inlinedFrameOffset(codeOrigin);
+    bool isInlined = !!frameOffset;
+    if (isInlined) {
+        InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
+
+        m_frame.m_callFrame = callFrame;
+        m_frame.m_inlineCallFrame = inlineCallFrame;
+        m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size();
+        m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock();
+        m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex;
+
+        JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
+        m_frame.m_scope = callee->scope();
+        m_frame.m_callee = callee;
+        ASSERT(m_frame.scope());
+        ASSERT(m_frame.callee());
+
+        // The callerFrame just needs to be non-null to indicate that we
+        // haven't reached the last frame yet. Setting it to the root
+        // frame (i.e. the callFrame that this inlined frame is called from)
+        // would work just fine.
+        m_frame.m_callerFrame = callFrame;
+        return;
+    }
+
+    readNonInlinedFrame(callFrame, codeOrigin);
+}
+#endif // ENABLE(DFG_JIT)
+
+StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
+{
+    if (!isJSFrame())
+        return CodeType::Native;
+
+    switch (codeBlock()->codeType()) {
+    case EvalCode:
+        return CodeType::Eval;
+    case FunctionCode:
+        return CodeType::Function;
+    case GlobalCode:
+        return CodeType::Global;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+    return CodeType::Global;
+}
+
+String StackVisitor::Frame::functionName()
+{
+    String traceLine;
+    JSObject* callee = this->callee();
+
+    switch (codeType()) {
+    case CodeType::Eval:
+        traceLine = "eval code";
+        break;
+    case CodeType::Native:
+        if (callee)
+            traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
+        break;
+    case CodeType::Function:
+        traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
+        break;
+    case CodeType::Global:
+        traceLine = "global code";
+        break;
+    }
+    return traceLine.isNull() ? emptyString() : traceLine;
+}
+
+String StackVisitor::Frame::sourceURL()
+{
+    String traceLine;
+
+    switch (codeType()) {
+    case CodeType::Eval:
+    case CodeType::Function:
+    case CodeType::Global: {
+        String sourceURL = codeBlock()->ownerExecutable()->sourceURL();
+        if (!sourceURL.isEmpty())
+            traceLine = sourceURL.impl();
+        break;
+    }
+    case CodeType::Native:
+        traceLine = "[native code]";
+        break;
+    }
+    return traceLine.isNull() ? emptyString() : traceLine;
+}
+
+String StackVisitor::Frame::toString()
+{
+    StringBuilder traceBuild;
+    String functionName = this->functionName();
+    String sourceURL = this->sourceURL();
+    traceBuild.append(functionName);
+    if (!sourceURL.isEmpty()) {
+        if (!functionName.isEmpty())
+            traceBuild.append('@');
+        traceBuild.append(sourceURL);
+        if (isJSFrame()) {
+            unsigned line = 0;
+            unsigned column = 0;
+            computeLineAndColumn(line, column);
+            traceBuild.append(':');
+            traceBuild.appendNumber(line);
+            traceBuild.append(':');
+            traceBuild.appendNumber(column);
+        }
+    }
+    return traceBuild.toString().impl();
+}
+
+Arguments* StackVisitor::Frame::createArguments()
+{
+    ASSERT(m_callFrame);
+    CallFrame* physicalFrame = m_callFrame;
+    VM& vm = physicalFrame->vm();
+    Arguments* arguments;
+#if ENABLE(DFG_JIT)
+    if (isInlinedFrame()) {
+        ASSERT(m_inlineCallFrame);
+        arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame);
+        arguments->tearOff(physicalFrame, m_inlineCallFrame);
+    } else 
+#endif
+    {
+        arguments = Arguments::create(vm, physicalFrame);
+        arguments->tearOff(physicalFrame);
+    }
+    return arguments;
+}
+
+Arguments* StackVisitor::Frame::existingArguments()
+{
+    if (codeBlock()->codeType() != FunctionCode)
+        return 0;
+    if (!codeBlock()->usesArguments())
+        return 0;
+    
+    VirtualRegister reg;
+        
+#if ENABLE(DFG_JIT)
+    if (isInlinedFrame())
+        reg = inlineCallFrame()->argumentsRegister;
+    else
+#endif // ENABLE(DFG_JIT)
+        reg = codeBlock()->argumentsRegister();
+    
+    JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue();
+    if (!result || !result.isCell()) // Protect against Undefined in case we throw in op_enter.
+        return 0;
+    return jsCast<Arguments*>(result);
+}
+
+void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column)
+{
+    CodeBlock* codeBlock = this->codeBlock();
+    if (!codeBlock) {
+        line = 0;
+        column = 0;
+        return;
+    }
+
+    int divot = 0;
+    int unusedStartOffset = 0;
+    int unusedEndOffset = 0;
+    unsigned divotLine = 0;
+    unsigned divotColumn = 0;
+    retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
+
+    line = divotLine + codeBlock->ownerExecutable()->lineNo();
+    column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset());
+}
+
+void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
+{
+    CodeBlock* codeBlock = this->codeBlock();
+    codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column);
+    divot += codeBlock->sourceOffset();
+}
+
+void StackVisitor::Frame::setToEnd()
+{
+    m_callFrame = 0;
+#if ENABLE(DFG_JIT)
+    m_inlineCallFrame = 0;
+#endif
+}
+
+#ifndef NDEBUG
+
+static const char* jitTypeName(JITCode::JITType jitType)
+{
+    switch (jitType) {
+    case JITCode::None: return "None";
+    case JITCode::HostCallThunk: return "HostCallThunk";
+    case JITCode::InterpreterThunk: return "InterpreterThunk";
+    case JITCode::BaselineJIT: return "BaselineJIT";
+    case JITCode::DFGJIT: return "DFGJIT";
+    case JITCode::FTLJIT: return "FTLJIT";
+    }
+    return "<unknown>";
+}
+
+static void printIndents(int levels)
+{
+    while (levels--)
+        dataLogFString("   ");
+}
+
+static void printif(int indentLevels, const char* format, ...)
+{
+    va_list argList;
+    va_start(argList, format);
+
+    if (indentLevels)
+        printIndents(indentLevels);
+
+#if COMPILER(CLANG) || COMPILER(GCC)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
+#endif
+
+    WTF::dataLogFV(format, argList);
+
+#if COMPILER(CLANG) || COMPILER(GCC)
+#pragma GCC diagnostic pop
+#endif
+
+    va_end(argList);
+}
+
+void StackVisitor::Frame::print(int indentLevel)
+{
+    int i = indentLevel;
+
+    if (!this->callFrame()) {
+        printif(i, "frame 0x0\n");
+        return;
+    }
+
+    CodeBlock* codeBlock = this->codeBlock();
+    printif(i, "frame %p {\n", this->callFrame());
+
+    CallFrame* callFrame = m_callFrame;
+    CallFrame* callerFrame = this->callerFrame();
+    void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr;
+
+    printif(i, "   name '%s'\n", functionName().utf8().data());
+    printif(i, "   sourceURL '%s'\n", sourceURL().utf8().data());
+    printif(i, "   isVMEntrySentinel %d\n", callerFrame->isVMEntrySentinel());
+
+#if ENABLE(DFG_JIT)
+    printif(i, "   isInlinedFrame %d\n", isInlinedFrame());
+    if (isInlinedFrame())
+        printif(i, "   InlineCallFrame %p\n", m_inlineCallFrame);
+#endif
+
+    printif(i, "   callee %p\n", callee());
+    printif(i, "   returnPC %p\n", returnPC);
+    printif(i, "   callerFrame %p\n", callerFrame);
+    unsigned locationRawBits = callFrame->locationAsRawBits();
+    printif(i, "   rawLocationBits %u 0x%x\n", locationRawBits, locationRawBits);
+    printif(i, "   codeBlock %p\n", codeBlock);
+    if (codeBlock) {
+        JITCode::JITType jitType = codeBlock->jitType();
+        if (callFrame->hasLocationAsBytecodeOffset()) {
+            unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset();
+            printif(i, "      bytecodeOffset %u %p / %zu\n", bytecodeOffset, reinterpret_cast<void*>(bytecodeOffset), codeBlock->instructions().size());
+#if ENABLE(DFG_JIT)
+        } else {
+            unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex();
+            printif(i, "      codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size());
+#endif
+        }
+        unsigned line = 0;
+        unsigned column = 0;
+        computeLineAndColumn(line, column);
+        printif(i, "      line %d\n", line);
+        printif(i, "      column %d\n", column);
+        printif(i, "      jitType %d <%s> isOptimizingJIT %d\n", jitType, jitTypeName(jitType), JITCode::isOptimizingJIT(jitType));
+#if ENABLE(DFG_JIT)
+        printif(i, "      hasCodeOrigins %d\n", codeBlock->hasCodeOrigins());
+        if (codeBlock->hasCodeOrigins()) {
+            JITCode* jitCode = codeBlock->jitCode().get();
+            printif(i, "         jitCode %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end());
+        }
+#endif
+    }
+    printif(i, "}\n");
+}
+
+#endif // NDEBUG
+
+} // namespace JSC
+
+#ifndef NDEBUG
+using JSC::StackVisitor;
+
+// For debugging use
+JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*);
+JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame);
+
+class DebugPrintFrameFunctor {
+public:
+    enum Action {
+        PrintOne,
+        PrintAll
+    };
+
+    DebugPrintFrameFunctor(Action action)
+        : m_action(action)
+    {
+    }
+
+    StackVisitor::Status operator()(StackVisitor& visitor)
+    {
+        visitor->print(2);
+        return m_action == PrintAll ? StackVisitor::Continue : StackVisitor::Done;
+    }
+
+private:
+    Action m_action;
+};
+
+void debugPrintCallFrame(JSC::CallFrame* callFrame)
+{
+    if (!callFrame)
+        return;
+    DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne);
+    callFrame->iterate(functor);
+}
+
+void debugPrintStack(JSC::CallFrame* topCallFrame)
+{
+    if (!topCallFrame)
+        return;
+    DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll);
+    topCallFrame->iterate(functor);
+}
+
+#endif // !NDEBUG