summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/interpreter/CallFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/interpreter/CallFrame.cpp')
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrame.cpp346
1 files changed, 201 insertions, 145 deletions
diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp
index bb61020ce..3d3897b6b 100644
--- a/Source/JavaScriptCore/interpreter/CallFrame.cpp
+++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2013, 2014 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,23 +27,74 @@
#include "CallFrame.h"
#include "CodeBlock.h"
+#include "InlineCallFrame.h"
#include "Interpreter.h"
-#include "Operations.h"
+#include "JSLexicalEnvironment.h"
+#include "JSCInlines.h"
+#include "VMEntryScope.h"
+#include <wtf/StringPrintStream.h>
namespace JSC {
-#ifndef NDEBUG
-void CallFrame::dumpCaller()
+bool CallFrame::callSiteBitsAreBytecodeOffset() const
{
- int signedLineNumber;
- intptr_t sourceID;
- String urlString;
- JSValue function;
-
- interpreter()->retrieveLastCaller(this, signedLineNumber, sourceID, urlString, function);
- dataLogF("Callpoint => %s:%d\n", urlString.utf8().data(), signedLineNumber);
+ ASSERT(codeBlock());
+ switch (codeBlock()->jitType()) {
+ case JITCode::InterpreterThunk:
+ case JITCode::BaselineJIT:
+ return true;
+ case JITCode::None:
+ case JITCode::HostCallThunk:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ default:
+ return false;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+}
+
+bool CallFrame::callSiteBitsAreCodeOriginIndex() const
+{
+ ASSERT(codeBlock());
+ switch (codeBlock()->jitType()) {
+ case JITCode::DFGJIT:
+ case JITCode::FTLJIT:
+ return true;
+ case JITCode::None:
+ case JITCode::HostCallThunk:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ default:
+ return false;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+}
+
+unsigned CallFrame::callSiteAsRawBits() const
+{
+ return this[JSStack::ArgumentCount].tag();
+}
+
+SUPPRESS_ASAN unsigned CallFrame::unsafeCallSiteAsRawBits() const
+{
+ return this[JSStack::ArgumentCount].unsafeTag();
+}
+
+CallSiteIndex CallFrame::callSiteIndex() const
+{
+ return CallSiteIndex(callSiteAsRawBits());
+}
+
+SUPPRESS_ASAN CallSiteIndex CallFrame::unsafeCallSiteIndex() const
+{
+ return CallSiteIndex(unsafeCallSiteAsRawBits());
}
+#ifndef NDEBUG
JSStack* CallFrame::stack()
{
return &interpreter()->stack();
@@ -52,170 +103,175 @@ JSStack* CallFrame::stack()
#endif
#if USE(JSVALUE32_64)
-unsigned CallFrame::bytecodeOffsetForNonDFGCode() const
+Instruction* CallFrame::currentVPC() const
{
- ASSERT(codeBlock());
- return currentVPC() - codeBlock()->instructions().begin();
+ return bitwise_cast<Instruction*>(callSiteIndex().bits());
}
-void CallFrame::setBytecodeOffsetForNonDFGCode(unsigned offset)
+void CallFrame::setCurrentVPC(Instruction* vpc)
+{
+ CallSiteIndex callSite(vpc);
+ this[JSStack::ArgumentCount].tag() = callSite.bits();
+}
+
+unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
{
ASSERT(codeBlock());
- setCurrentVPC(codeBlock()->instructions().begin() + offset);
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return currentVPC() - codeBlock()->instructions().begin();
}
-#else
+
+#else // USE(JSVALUE32_64)
Instruction* CallFrame::currentVPC() const
{
- return codeBlock()->instructions().begin() + bytecodeOffsetForNonDFGCode();
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return codeBlock()->instructions().begin() + callSiteBitsAsBytecodeOffset();
}
+
void CallFrame::setCurrentVPC(Instruction* vpc)
{
- setBytecodeOffsetForNonDFGCode(vpc - codeBlock()->instructions().begin());
+ CallSiteIndex callSite(vpc - codeBlock()->instructions().begin());
+ this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(callSite.bits());
}
-#endif
-
-#if ENABLE(DFG_JIT)
-bool CallFrame::isInlineCallFrameSlow()
+
+unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
{
- if (!callee())
- return false;
- JSCell* calleeAsFunctionCell = getJSFunction(callee());
- if (!calleeAsFunctionCell)
- return false;
- JSFunction* calleeAsFunction = jsCast<JSFunction*>(calleeAsFunctionCell);
- return calleeAsFunction->executable() != codeBlock()->ownerExecutable();
+ ASSERT(codeBlock());
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return callSiteIndex().bits();
}
-CallFrame* CallFrame::trueCallFrame(AbstractPC pc)
-{
- // Am I an inline call frame? If so, we're done.
- if (isInlineCallFrame())
- return this;
-
- // If I don't have a code block, then I'm not DFG code, so I'm the true call frame.
- CodeBlock* machineCodeBlock = codeBlock();
- if (!machineCodeBlock)
- return this;
-
- // If the code block does not have any code origins, then there was no inlining, so
- // I'm done.
- if (!machineCodeBlock->hasCodeOrigins())
- return this;
-
- // At this point the PC must be due either to the DFG, or it must be unset.
- ASSERT(pc.hasJITReturnAddress() || !pc);
+#endif
- // Try to determine the CodeOrigin. If we don't have a pc set then the only way
- // that this makes sense is if the CodeOrigin index was set in the call frame.
- // FIXME: Note that you will see "Not currently in inlined code" comments below.
- // Currently, we do not record code origins for code that is not inlined, because
- // the only thing that we use code origins for is determining the inline stack.
- // But in the future, we'll want to use this same functionality (having a code
- // origin mapping for any calls out of JIT code) to determine the PC at any point
- // in the stack even if not in inlined code. When that happens, the code below
- // will have to change the way it detects the presence of inlining: it will always
- // get a code origin, but sometimes, that code origin will not have an inline call
- // frame. In that case, this method should bail and return this.
- CodeOrigin codeOrigin;
- if (pc.isSet()) {
- ReturnAddressPtr currentReturnPC = pc.jitReturnAddress();
-
- bool hasCodeOrigin = machineCodeBlock->codeOriginForReturn(currentReturnPC, codeOrigin);
- ASSERT(hasCodeOrigin);
- if (!hasCodeOrigin) {
- // In release builds, if we find ourselves in a situation where the return PC doesn't
- // correspond to a valid CodeOrigin, we return zero instead of continuing. Some of
- // the callers of trueCallFrame() will be able to recover and do conservative things,
- // while others will crash.
- return 0;
- }
- } else {
- unsigned index = codeOriginIndexForDFG();
- ASSERT(machineCodeBlock->canGetCodeOrigin(index));
- if (!machineCodeBlock->canGetCodeOrigin(index)) {
- // See above. In release builds, we try to protect ourselves from crashing even
- // though stack walking will be goofed up.
- return 0;
+unsigned CallFrame::bytecodeOffset()
+{
+ if (!codeBlock())
+ return 0;
+#if ENABLE(DFG_JIT)
+ if (callSiteBitsAreCodeOriginIndex()) {
+ ASSERT(codeBlock());
+ CodeOrigin codeOrigin = this->codeOrigin();
+ for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) {
+ codeOrigin = inlineCallFrame->directCaller;
+ inlineCallFrame = codeOrigin.inlineCallFrame;
}
- codeOrigin = machineCodeBlock->codeOrigin(index);
+ return codeOrigin.bytecodeIndex;
}
+#endif
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return callSiteBitsAsBytecodeOffset();
+}
- if (!codeOrigin.inlineCallFrame)
- return this; // Not currently in inlined code.
-
- for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) {
- InlineCallFrame* nextInlineCallFrame = inlineCallFrame->caller.inlineCallFrame;
-
- CallFrame* inlinedCaller = this + inlineCallFrame->stackOffset;
-
- JSFunction* calleeAsFunction = inlineCallFrame->callee.get();
-
- // Fill in the inlinedCaller
- inlinedCaller->setCodeBlock(machineCodeBlock);
- if (calleeAsFunction)
- inlinedCaller->setScope(calleeAsFunction->scope());
- if (nextInlineCallFrame)
- inlinedCaller->setCallerFrame(this + nextInlineCallFrame->stackOffset);
- else
- inlinedCaller->setCallerFrame(this);
-
- inlinedCaller->setInlineCallFrame(inlineCallFrame);
- inlinedCaller->setArgumentCountIncludingThis(inlineCallFrame->arguments.size());
- if (calleeAsFunction)
- inlinedCaller->setCallee(calleeAsFunction);
-
- inlineCallFrame = nextInlineCallFrame;
+CodeOrigin CallFrame::codeOrigin()
+{
+ if (!codeBlock())
+ return CodeOrigin(0);
+#if ENABLE(DFG_JIT)
+ if (callSiteBitsAreCodeOriginIndex()) {
+ CallSiteIndex index = callSiteIndex();
+ ASSERT(codeBlock()->canGetCodeOrigin(index));
+ return codeBlock()->codeOrigin(index);
}
-
- return this + codeOrigin.inlineCallFrame->stackOffset;
+#endif
+ return CodeOrigin(callSiteBitsAsBytecodeOffset());
}
-
-CallFrame* CallFrame::trueCallerFrame()
+
+Register* CallFrame::topOfFrameInternal()
{
- if (!codeBlock())
- return callerFrame()->removeHostCallFrameFlag();
-
- // this -> The callee; this is either an inlined callee in which case it already has
- // a pointer to the true caller. Otherwise it contains current PC in the machine
- // caller.
- //
- // machineCaller -> The caller according to the machine, which may be zero or
- // more frames above the true caller due to inlining.
-
- // Am I an inline call frame? If so, we're done.
- if (isInlineCallFrame())
- return callerFrame()->removeHostCallFrameFlag();
-
- // I am a machine call frame, so the question is: is my caller a machine call frame
- // that has inlines or a machine call frame that doesn't?
- CallFrame* machineCaller = callerFrame()->removeHostCallFrameFlag();
- if (!machineCaller)
- return 0;
- ASSERT(!machineCaller->isInlineCallFrame());
-
- // Figure out how we want to get the current code location.
- if (!hasReturnPC() || returnAddressIsInCtiTrampoline(returnPC()))
- return machineCaller->trueCallFrameFromVMCode()->removeHostCallFrameFlag();
-
- return machineCaller->trueCallFrame(returnPC())->removeHostCallFrameFlag();
+ CodeBlock* codeBlock = this->codeBlock();
+ ASSERT(codeBlock);
+ return registers() + codeBlock->stackPointerOffset();
}
-CodeBlock* CallFrame::someCodeBlockForPossiblyInlinedCode()
+JSGlobalObject* CallFrame::vmEntryGlobalObject()
{
- if (!isInlineCallFrame())
- return codeBlock();
-
- return jsCast<FunctionExecutable*>(inlineCallFrame()->executable.get())->baselineCodeBlockFor(
- inlineCallFrame()->isCall ? CodeForCall : CodeForConstruct);
+ if (this == lexicalGlobalObject()->globalExec())
+ return lexicalGlobalObject();
+
+ // For any ExecState that's not a globalExec, the
+ // dynamic global object must be set since code is running
+ ASSERT(vm().entryScope);
+ return vm().entryScope->globalObject();
}
-#endif
+CallFrame* CallFrame::callerFrame(VMEntryFrame*& currVMEntryFrame)
+{
+ if (callerFrameOrVMEntryFrame() == currVMEntryFrame) {
+ VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame);
+ currVMEntryFrame = currVMEntryRecord->prevTopVMEntryFrame();
+ return currVMEntryRecord->prevTopCallFrame();
+ }
+ return static_cast<CallFrame*>(callerFrameOrVMEntryFrame());
+}
-Register* CallFrame::frameExtentInternal()
+SUPPRESS_ASAN CallFrame* CallFrame::unsafeCallerFrame(VMEntryFrame*& currVMEntryFrame)
+{
+ if (unsafeCallerFrameOrVMEntryFrame() == currVMEntryFrame) {
+ VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame);
+ currVMEntryFrame = currVMEntryRecord->unsafePrevTopVMEntryFrame();
+ return currVMEntryRecord->unsafePrevTopCallFrame();
+ }
+ return static_cast<CallFrame*>(unsafeCallerFrameOrVMEntryFrame());
+}
+
+String CallFrame::friendlyFunctionName()
{
CodeBlock* codeBlock = this->codeBlock();
- ASSERT(codeBlock);
- return registers() + codeBlock->m_numCalleeRegisters;
+ if (!codeBlock)
+ return emptyString();
+
+ switch (codeBlock->codeType()) {
+ case EvalCode:
+ return ASCIILiteral("eval code");
+ case ModuleCode:
+ return ASCIILiteral("module code");
+ case GlobalCode:
+ return ASCIILiteral("global code");
+ case FunctionCode:
+ if (callee())
+ return getCalculatedDisplayName(this, callee());
+ return emptyString();
+ }
+
+ ASSERT_NOT_REACHED();
+ return emptyString();
+}
+
+void CallFrame::dump(PrintStream& out)
+{
+ if (CodeBlock* codeBlock = this->codeBlock()) {
+ out.print(codeBlock->inferredName(), "#", codeBlock->hashAsStringIfPossible(), " [", codeBlock->jitType(), "]");
+
+ out.print("(");
+ thisValue().dumpForBacktrace(out);
+
+ for (size_t i = 0; i < argumentCount(); ++i) {
+ out.print(", ");
+ JSValue value = argument(i);
+ value.dumpForBacktrace(out);
+ }
+
+ out.print(")");
+
+ return;
+ }
+
+ out.print(returnPC());
}
+const char* CallFrame::describeFrame()
+{
+ const size_t bufferSize = 200;
+ static char buffer[bufferSize + 1];
+
+ WTF::StringPrintStream stringStream;
+
+ dump(stringStream);
+
+ strncpy(buffer, stringStream.toCString().data(), bufferSize);
+ buffer[bufferSize] = '\0';
+
+ return buffer;
}
+
+} // namespace JSC