X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/14957cd040308e3eeec43d26bae5d76da13fcd85..refs/heads/master:/interpreter/CallFrame.h?ds=sidebyside diff --git a/interpreter/CallFrame.h b/interpreter/CallFrame.h index 57b24ef..806ccea 100644 --- a/interpreter/CallFrame.h +++ b/interpreter/CallFrame.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2008, 2011, 2013, 2014 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,143 +23,292 @@ #ifndef CallFrame_h #define CallFrame_h -#include "JSGlobalData.h" +#include "AbstractPC.h" +#include "JSStack.h" #include "MacroAssemblerCodeRef.h" -#include "RegisterFile.h" +#include "Register.h" +#include "StackVisitor.h" +#include "VM.h" +#include "VMEntryRecord.h" namespace JSC { class Arguments; - class JSActivation; + class JSLexicalEnvironment; class Interpreter; - class ScopeChainNode; + class JSScope; // Represents the current state of script execution. // Passed as the first argument to most functions. class ExecState : private Register { public: - JSObject* callee() const { return this[RegisterFile::Callee].function(); } - CodeBlock* codeBlock() const { return this[RegisterFile::CodeBlock].Register::codeBlock(); } - ScopeChainNode* scopeChain() const + JSValue calleeAsValue() const { return this[JSStack::Callee].jsValue(); } + JSObject* callee() const { return this[JSStack::Callee].object(); } + CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); } + JSScope* scope(int scopeRegisterOffset) const { - ASSERT(this[RegisterFile::ScopeChain].Register::scopeChain()); - return this[RegisterFile::ScopeChain].Register::scopeChain(); + ASSERT(this[scopeRegisterOffset].Register::scope()); + return this[scopeRegisterOffset].Register::scope(); } + bool hasActivation() const; + JSLexicalEnvironment* lexicalEnvironment() const; + JSLexicalEnvironment* lexicalEnvironmentOrNullptr() const; + JSValue uncheckedActivation() const; + // Global object in which execution began. - JSGlobalObject* dynamicGlobalObject(); + JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject(); // Global object in which the currently executing code was defined. - // Differs from dynamicGlobalObject() during function calls across web browser frames. - inline JSGlobalObject* lexicalGlobalObject() const; + // Differs from vmEntryGlobalObject() during function calls across web browser frames. + JSGlobalObject* lexicalGlobalObject() const; // Differs from lexicalGlobalObject because this will have DOM window shell rather than // the actual DOM window, which can't be "this" for security reasons. - inline JSObject* globalThisValue() const; + JSObject* globalThisValue() const; - inline JSGlobalData& globalData() const; + VM& vm() const; // Convenience functions for access to global data. // It takes a few memory references to get from a call frame to the global data // pointer, so these are inefficient, and should be used sparingly in new code. // But they're used in many places in legacy code, so they're not going away any time soon. - void clearException() { globalData().exception = JSValue(); } - JSValue exception() const { return globalData().exception; } - bool hadException() const { return globalData().exception; } + void clearException() { vm().clearException(); } + + Exception* exception() const { return vm().exception(); } + bool hadException() const { return !!vm().exception(); } + + Exception* lastException() const { return vm().lastException(); } + void clearLastException() { vm().clearLastException(); } + + AtomicStringTable* atomicStringTable() const { return vm().atomicStringTable(); } + const CommonIdentifiers& propertyNames() const { return *vm().propertyNames; } + const MarkedArgumentBuffer& emptyList() const { return *vm().emptyList; } + Interpreter* interpreter() { return vm().interpreter; } + Heap* heap() { return &vm().heap; } - const CommonIdentifiers& propertyNames() const { return *globalData().propertyNames; } - const MarkedArgumentBuffer& emptyList() const { return *globalData().emptyList; } - Interpreter* interpreter() { return globalData().interpreter; } - Heap* heap() { return &globalData().heap; } -#ifndef NDEBUG - void dumpCaller(); -#endif - static const HashTable* arrayConstructorTable(CallFrame* callFrame) { return callFrame->globalData().arrayConstructorTable; } - static const HashTable* arrayPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().arrayPrototypeTable; } - static const HashTable* booleanPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().booleanPrototypeTable; } - static const HashTable* dateTable(CallFrame* callFrame) { return callFrame->globalData().dateTable; } - static const HashTable* dateConstructorTable(CallFrame* callFrame) { return callFrame->globalData().dateConstructorTable; } - static const HashTable* errorPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().errorPrototypeTable; } - static const HashTable* globalObjectTable(CallFrame* callFrame) { return callFrame->globalData().globalObjectTable; } - static const HashTable* jsonTable(CallFrame* callFrame) { return callFrame->globalData().jsonTable; } - static const HashTable* mathTable(CallFrame* callFrame) { return callFrame->globalData().mathTable; } - static const HashTable* numberConstructorTable(CallFrame* callFrame) { return callFrame->globalData().numberConstructorTable; } - static const HashTable* numberPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().numberPrototypeTable; } - static const HashTable* objectConstructorTable(CallFrame* callFrame) { return callFrame->globalData().objectConstructorTable; } - static const HashTable* objectPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().objectPrototypeTable; } - static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->globalData().regExpTable; } - static const HashTable* regExpConstructorTable(CallFrame* callFrame) { return callFrame->globalData().regExpConstructorTable; } - static const HashTable* regExpPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().regExpPrototypeTable; } - static const HashTable* stringTable(CallFrame* callFrame) { return callFrame->globalData().stringTable; } - static const HashTable* stringConstructorTable(CallFrame* callFrame) { return callFrame->globalData().stringConstructorTable; } static CallFrame* create(Register* callFrameBase) { return static_cast(callFrameBase); } Register* registers() { return this; } + const Register* registers() const { return this; } CallFrame& operator=(const Register& r) { *static_cast(this) = r; return *this; } - CallFrame* callerFrame() const { return this[RegisterFile::CallerFrame].callFrame(); } -#if ENABLE(JIT) - ReturnAddressPtr returnPC() const { return ReturnAddressPtr(this[RegisterFile::ReturnPC].vPC()); } + CallFrame* callerFrame() const { return static_cast(callerFrameOrVMEntryFrame()); } + + JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&); + + static ptrdiff_t callerFrameOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame); } + + ReturnAddressPtr returnPC() const { return ReturnAddressPtr(callerFrameAndPC().pc); } + bool hasReturnPC() const { return !!callerFrameAndPC().pc; } + void clearReturnPC() { callerFrameAndPC().pc = 0; } + static ptrdiff_t returnPCOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, pc); } + AbstractPC abstractReturnPC(VM& vm) { return AbstractPC(vm, this); } + + class Location { + public: + static inline uint32_t decode(uint32_t bits); + + static inline bool isBytecodeLocation(uint32_t bits); +#if USE(JSVALUE64) + static inline uint32_t encodeAsBytecodeOffset(uint32_t bits); +#else + static inline uint32_t encodeAsBytecodeInstruction(Instruction*); #endif -#if ENABLE(INTERPRETER) - Instruction* returnVPC() const { return this[RegisterFile::ReturnPC].vPC(); } + + static inline bool isCodeOriginIndex(uint32_t bits); + static inline uint32_t encodeAsCodeOriginIndex(uint32_t bits); + + private: + enum TypeTag { + BytecodeLocationTag = 0, + CodeOriginIndexTag = 1, + }; + + static inline uint32_t encode(TypeTag, uint32_t bits); + + static const uint32_t s_mask = 0x1; +#if USE(JSVALUE64) + static const uint32_t s_shift = 31; + static const uint32_t s_shiftedMask = s_mask << s_shift; +#else + static const uint32_t s_shift = 1; #endif + }; + + bool hasLocationAsBytecodeOffset() const; + bool hasLocationAsCodeOriginIndex() const; - void setCallerFrame(CallFrame* callerFrame) { static_cast(this)[RegisterFile::CallerFrame] = callerFrame; } - void setScopeChain(ScopeChainNode* scopeChain) { static_cast(this)[RegisterFile::ScopeChain] = scopeChain; } + unsigned locationAsRawBits() const; + unsigned locationAsBytecodeOffset() const; + unsigned locationAsCodeOriginIndex() const; - ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, ScopeChainNode* scopeChain, - CallFrame* callerFrame, int argc, JSObject* callee) + void setLocationAsRawBits(unsigned); + void setLocationAsBytecodeOffset(unsigned); + +#if ENABLE(DFG_JIT) + unsigned bytecodeOffsetFromCodeOriginIndex(); +#endif + + // This will try to get you the bytecode offset, but you should be aware that + // this bytecode offset may be bogus in the presence of inlining. This will + // also return 0 if the call frame has no notion of bytecode offsets (for + // example if it's native code). + // https://bugs.webkit.org/show_bug.cgi?id=121754 + unsigned bytecodeOffset(); + + // This will get you a CodeOrigin. It will always succeed. May return + // CodeOrigin(0) if we're in native code. + CodeOrigin codeOrigin(); + + Register* topOfFrame() + { + if (!codeBlock()) + return registers(); + return topOfFrameInternal(); + } + +#if USE(JSVALUE32_64) + Instruction* currentVPC() const { - ASSERT(callerFrame); // Use noCaller() rather than 0 for the outer host call frame caller. - ASSERT(callerFrame == noCaller() || callerFrame->removeHostCallFrameFlag()->registerFile()->end() >= this); - - setCodeBlock(codeBlock); - setScopeChain(scopeChain); - setCallerFrame(callerFrame); - setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. - setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object) - setCallee(callee); + return bitwise_cast(this[JSStack::ArgumentCount].tag()); + } + void setCurrentVPC(Instruction* vpc) + { + this[JSStack::ArgumentCount].tag() = bitwise_cast(vpc); + } +#else + Instruction* currentVPC() const; + void setCurrentVPC(Instruction* vpc); +#endif + + void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; } + void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast(this)[scopeRegisterOffset] = scope; } + void setActivation(JSLexicalEnvironment*); + + ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, + CallFrame* callerFrame, int argc, JSObject* callee) + { + ASSERT(callerFrame == noCaller() || callerFrame->stack()->containsAddress(this)); + + setCodeBlock(codeBlock); + setCallerFrame(callerFrame); + setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. + setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object) + setCallee(callee); } // Read a register from the codeframe (or constant from the CodeBlock). - inline Register& r(int); - // Read a register for a non-constant - inline Register& uncheckedR(int); + Register& r(int); + Register& r(VirtualRegister); + // Read a register for a non-constant + Register& uncheckedR(int); + Register& uncheckedR(VirtualRegister); - // Access to arguments. - int hostThisRegister() { return -RegisterFile::CallFrameHeaderSize - argumentCountIncludingThis(); } - JSValue hostThisValue() { return this[hostThisRegister()].jsValue(); } + // Access to arguments as passed. (After capture, arguments may move to a different location.) size_t argumentCount() const { return argumentCountIncludingThis() - 1; } - size_t argumentCountIncludingThis() const { return this[RegisterFile::ArgumentCount].i(); } - JSValue argument(int argumentNumber) + size_t argumentCountIncludingThis() const { return this[JSStack::ArgumentCount].payload(); } + static int argumentOffset(int argument) { return (JSStack::FirstArgument + argument); } + static int argumentOffsetIncludingThis(int argument) { return (JSStack::ThisArgument + argument); } + + // In the following (argument() and setArgument()), the 'argument' + // parameter is the index of the arguments of the target function of + // this frame. The index starts at 0 for the first arg, 1 for the + // second, etc. + // + // The arguments (in this case) do not include the 'this' value. + // arguments(0) will not fetch the 'this' value. To get/set 'this', + // use thisValue() and setThisValue() below. + + JSValue argument(size_t argument) + { + if (argument >= argumentCount()) + return jsUndefined(); + return getArgumentUnsafe(argument); + } + JSValue uncheckedArgument(size_t argument) + { + ASSERT(argument < argumentCount()); + return getArgumentUnsafe(argument); + } + void setArgument(size_t argument, JSValue value) { - int argumentIndex = -RegisterFile::CallFrameHeaderSize - this[RegisterFile::ArgumentCount].i() + argumentNumber + 1; - if (argumentIndex >= -RegisterFile::CallFrameHeaderSize) - return jsUndefined(); - return this[argumentIndex].jsValue(); + this[argumentOffset(argument)] = value; } - static CallFrame* noCaller() { return reinterpret_cast(HostCallFrameFlag); } + JSValue getArgumentUnsafe(size_t argIndex) + { + // User beware! This method does not verify that there is a valid + // argument at the specified argIndex. This is used for debugging + // and verification code only. The caller is expected to know what + // he/she is doing when calling this method. + return this[argumentOffset(argIndex)].jsValue(); + } + + static int thisArgumentOffset() { return argumentOffsetIncludingThis(0); } + JSValue thisValue() { return this[thisArgumentOffset()].jsValue(); } + void setThisValue(JSValue value) { this[thisArgumentOffset()] = value; } + + JSValue argumentAfterCapture(size_t argument); - bool hasHostCallFrameFlag() const { return reinterpret_cast(this) & HostCallFrameFlag; } - CallFrame* addHostCallFrameFlag() const { return reinterpret_cast(reinterpret_cast(this) | HostCallFrameFlag); } - CallFrame* removeHostCallFrameFlag() { return reinterpret_cast(reinterpret_cast(this) & ~HostCallFrameFlag); } + static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; } - void setArgumentCountIncludingThis(int count) { static_cast(this)[RegisterFile::ArgumentCount] = Register::withInt(count); } - void setCallee(JSObject* callee) { static_cast(this)[RegisterFile::Callee] = Register::withCallee(callee); } - void setCodeBlock(CodeBlock* codeBlock) { static_cast(this)[RegisterFile::CodeBlock] = codeBlock; } - void setReturnPC(void* value) { static_cast(this)[RegisterFile::ReturnPC] = (Instruction*)value; } + static CallFrame* noCaller() { return 0; } + + void setArgumentCountIncludingThis(int count) { static_cast(this)[JSStack::ArgumentCount].payload() = count; } + void setCallee(JSObject* callee) { static_cast(this)[JSStack::Callee] = callee; } + void setCodeBlock(CodeBlock* codeBlock) { static_cast(this)[JSStack::CodeBlock] = codeBlock; } + void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast(value); } + + // CallFrame::iterate() expects a Functor that implements the following method: + // StackVisitor::Status operator()(StackVisitor&); + + template void iterate(Functor& functor) + { + StackVisitor::visit(this, functor); + } + + void dump(PrintStream&); + JS_EXPORT_PRIVATE const char* describeFrame(); private: - static const intptr_t HostCallFrameFlag = 1; + #ifndef NDEBUG - RegisterFile* registerFile(); + JSStack* stack(); #endif ExecState(); ~ExecState(); + + Register* topOfFrameInternal(); + + // The following are for internal use in debugging and verification + // code only and not meant as an API for general usage: + + size_t argIndexForRegister(Register* reg) + { + // The register at 'offset' number of slots from the frame pointer + // i.e. + // reg = frame[offset]; + // ==> reg = frame + offset; + // ==> offset = reg - frame; + int offset = reg - this->registers(); + + // The offset is defined (based on argumentOffset()) to be: + // offset = JSStack::FirstArgument - argIndex; + // Hence: + // argIndex = JSStack::FirstArgument - offset; + size_t argIndex = offset - JSStack::FirstArgument; + return argIndex; + } + + void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; } + + CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast(this); } + const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast(this); } + + friend class JSStack; }; } // namespace JSC