diff options
Diffstat (limited to 'Source/JavaScriptCore/interpreter/CallFrame.cpp')
-rw-r--r-- | Source/JavaScriptCore/interpreter/CallFrame.cpp | 346 |
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 |