X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/ba379fdc102753d6be2c4d937058fe40257329fe..refs/heads/master:/interpreter/CallFrame.h?ds=sidebyside diff --git a/interpreter/CallFrame.h b/interpreter/CallFrame.h index 8126d74..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 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,126 +23,292 @@ #ifndef CallFrame_h #define CallFrame_h -#include "JSGlobalData.h" -#include "RegisterFile.h" -#include "ScopeChain.h" +#include "AbstractPC.h" +#include "JSStack.h" +#include "MacroAssemblerCodeRef.h" +#include "Register.h" +#include "StackVisitor.h" +#include "VM.h" +#include "VMEntryRecord.h" namespace JSC { class Arguments; - class JSActivation; + class JSLexicalEnvironment; class Interpreter; + class JSScope; // Represents the current state of script execution. // Passed as the first argument to most functions. class ExecState : private Register { public: - JSFunction* callee() const { return this[RegisterFile::Callee].function(); } - CodeBlock* codeBlock() const { return this[RegisterFile::CodeBlock].Register::codeBlock(); } - ScopeChainNode* scopeChain() const { return this[RegisterFile::ScopeChain].Register::scopeChain(); } - int argumentCount() const { return this[RegisterFile::ArgumentCount].i(); } + 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[scopeRegisterOffset].Register::scope()); + return this[scopeRegisterOffset].Register::scope(); + } - JSValue thisValue(); + 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. - JSGlobalObject* lexicalGlobalObject() const - { - return scopeChain()->globalObject(); - } + // 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. - JSObject* globalThisValue() const - { - return scopeChain()->globalThisObject(); - } + JSObject* globalThisValue() const; - // FIXME: Elsewhere, we use JSGlobalData* rather than JSGlobalData&. - // We should make this more uniform and either use a reference everywhere - // or a pointer everywhere. - JSGlobalData& globalData() const - { - return *scopeChain()->globalData; - } + 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 setException(JSValue exception) { globalData().exception = exception; } - void clearException() { globalData().exception = JSValue(); } - JSValue exception() const { return globalData().exception; } - JSValue* exceptionSlot() { 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* arrayTable(CallFrame* callFrame) { return callFrame->globalData().arrayTable; } - static const HashTable* dateTable(CallFrame* callFrame) { return callFrame->globalData().dateTable; } - static const HashTable* jsonTable(CallFrame* callFrame) { return callFrame->globalData().jsonTable; } - static const HashTable* mathTable(CallFrame* callFrame) { return callFrame->globalData().mathTable; } - static const HashTable* numberTable(CallFrame* callFrame) { return callFrame->globalData().numberTable; } - static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->globalData().regExpTable; } - static const HashTable* regExpConstructorTable(CallFrame* callFrame) { return callFrame->globalData().regExpConstructorTable; } - static const HashTable* stringTable(CallFrame* callFrame) { return callFrame->globalData().stringTable; } 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(); } - Arguments* optionalCalleeArguments() const { return this[RegisterFile::OptionalCalleeArguments].arguments(); } - Instruction* returnPC() const { return this[RegisterFile::ReturnPC].vPC(); } + CallFrame* callerFrame() const { return static_cast(callerFrameOrVMEntryFrame()); } + + JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&); - void setCalleeArguments(JSValue arguments) { static_cast(this)[RegisterFile::OptionalCalleeArguments] = arguments; } - void setCallerFrame(CallFrame* callerFrame) { static_cast(this)[RegisterFile::CallerFrame] = callerFrame; } - void setScopeChain(ScopeChainNode* scopeChain) { static_cast(this)[RegisterFile::ScopeChain] = scopeChain; } + 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 - ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, ScopeChainNode* scopeChain, - CallFrame* callerFrame, int returnValueRegister, int argc, JSFunction* function) + 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; + + unsigned locationAsRawBits() const; + unsigned locationAsBytecodeOffset() const; + unsigned locationAsCodeOriginIndex() const; + + 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 + { + return bitwise_cast(this[JSStack::ArgumentCount].tag()); + } + void setCurrentVPC(Instruction* vpc) { - ASSERT(callerFrame); // Use noCaller() rather than 0 for the outer host call frame caller. - - setCodeBlock(codeBlock); - setScopeChain(scopeChain); - setCallerFrame(callerFrame); - static_cast(this)[RegisterFile::ReturnPC] = vPC; // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. - static_cast(this)[RegisterFile::ReturnValueRegister] = Register::withInt(returnValueRegister); - setArgumentCount(argc); // original argument count (for the sake of the "arguments" object) - setCallee(function); - setCalleeArguments(JSValue()); + 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); + Register& r(int); + Register& r(VirtualRegister); + // Read a register for a non-constant + Register& uncheckedR(int); + Register& uncheckedR(VirtualRegister); - static CallFrame* noCaller() { return reinterpret_cast(HostCallFrameFlag); } - int returnValueRegister() const { return this[RegisterFile::ReturnValueRegister].i(); } + // 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[JSStack::ArgumentCount].payload(); } + static int argumentOffset(int argument) { return (JSStack::FirstArgument + argument); } + static int argumentOffsetIncludingThis(int argument) { return (JSStack::ThisArgument + 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); } + // 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. - private: - void setArgumentCount(int count) { static_cast(this)[RegisterFile::ArgumentCount] = Register::withInt(count); } - void setCallee(JSFunction* callee) { static_cast(this)[RegisterFile::Callee] = callee; } - void setCodeBlock(CodeBlock* codeBlock) { static_cast(this)[RegisterFile::CodeBlock] = codeBlock; } + 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) + { + this[argumentOffset(argument)] = value; + } + + 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); + + static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; } + + 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); } - static const intptr_t HostCallFrameFlag = 1; + // 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: + +#ifndef NDEBUG + 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