diff options
Diffstat (limited to 'Source/JavaScriptCore/interpreter/CallFrame.h')
-rw-r--r-- | Source/JavaScriptCore/interpreter/CallFrame.h | 319 |
1 files changed, 129 insertions, 190 deletions
diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h index 48fbcd779..6b70a0ec5 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.h +++ b/Source/JavaScriptCore/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, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2017 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 @@ -20,34 +20,83 @@ * */ -#ifndef CallFrame_h -#define CallFrame_h +#pragma once #include "AbstractPC.h" -#include "VM.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 ExecState; class Interpreter; + class JSCallee; class JSScope; + struct Instruction; + + typedef ExecState CallFrame; + + struct CallSiteIndex { + CallSiteIndex() + : m_bits(UINT_MAX) + { + } + + explicit CallSiteIndex(uint32_t bits) + : m_bits(bits) + { } +#if USE(JSVALUE32_64) + explicit CallSiteIndex(Instruction* instruction) + : m_bits(bitwise_cast<uint32_t>(instruction)) + { } +#endif + + explicit operator bool() const { return m_bits != UINT_MAX; } + bool operator==(const CallSiteIndex& other) const { return m_bits == other.m_bits; } + + inline uint32_t bits() const { return m_bits; } + + private: + uint32_t m_bits; + }; + + struct CallerFrameAndPC { + CallFrame* callerFrame; + Instruction* pc; + static const int sizeInRegisters = 2 * sizeof(void*) / sizeof(Register); + }; + static_assert(CallerFrameAndPC::sizeInRegisters == sizeof(CallerFrameAndPC) / sizeof(Register), "CallerFrameAndPC::sizeInRegisters is incorrect."); + + struct CallFrameSlot { + static const int codeBlock = CallerFrameAndPC::sizeInRegisters; + static const int callee = codeBlock + 1; + static const int argumentCount = callee + 1; + static const int thisArgument = argumentCount + 1; + static const int firstArgument = thisArgument + 1; + }; + // Represents the current state of script execution. // Passed as the first argument to most functions. class ExecState : private Register { public: - JSValue calleeAsValue() const { return this[JSStack::Callee].jsValue(); } - JSObject* callee() const { return this[JSStack::Callee].function(); } - CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); } - JSScope* scope() const + static const int headerSizeInRegisters = CallFrameSlot::argumentCount + 1; + + JSValue calleeAsValue() const { return this[CallFrameSlot::callee].jsValue(); } + JSObject* jsCallee() const { return this[CallFrameSlot::callee].object(); } + JSCell* callee() const { return this[CallFrameSlot::callee].unboxedCell(); } + SUPPRESS_ASAN JSValue unsafeCallee() const { return this[CallFrameSlot::callee].asanUnsafeJSValue(); } + CodeBlock* codeBlock() const { return this[CallFrameSlot::codeBlock].Register::codeBlock(); } + CodeBlock** addressOfCodeBlock() const { return bitwise_cast<CodeBlock**>(this + CallFrameSlot::codeBlock); } + SUPPRESS_ASAN CodeBlock* unsafeCodeBlock() const { return this[CallFrameSlot::codeBlock].Register::asanUnsafeCodeBlock(); } + JSScope* scope(int scopeRegisterOffset) const { - ASSERT(this[JSStack::ScopeChain].Register::scope()); - return this[JSStack::ScopeChain].Register::scope(); + ASSERT(this[scopeRegisterOffset].Register::scope()); + return this[scopeRegisterOffset].Register::scope(); } // Global object in which execution began. @@ -68,50 +117,28 @@ namespace JSC { // 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() { vm().clearException(); } - void clearSupplementaryExceptionInfo() - { - vm().clearExceptionStack(); - } - - JSValue exception() const { return vm().exception(); } - bool hadException() const { return !vm().exception().isEmpty(); } - + AtomicStringTable* atomicStringTable() const { return vm().atomicStringTable(); } const CommonIdentifiers& propertyNames() const { return *vm().propertyNames; } - const MarkedArgumentBuffer& emptyList() const { return *vm().emptyList; } + const ArgList& emptyList() const { return *vm().emptyList; } Interpreter* interpreter() { return vm().interpreter; } Heap* heap() { return &vm().heap; } -#ifndef NDEBUG - void dumpCaller(); -#endif - static const HashTable& arrayConstructorTable(VM& vm) { return *vm.arrayConstructorTable; } - static const HashTable& arrayPrototypeTable(VM& vm) { return *vm.arrayPrototypeTable; } - static const HashTable& booleanPrototypeTable(VM& vm) { return *vm.booleanPrototypeTable; } - static const HashTable& dataViewTable(VM& vm) { return *vm.dataViewTable; } - static const HashTable& dateTable(VM& vm) { return *vm.dateTable; } - static const HashTable& dateConstructorTable(VM& vm) { return *vm.dateConstructorTable; } - static const HashTable& errorPrototypeTable(VM& vm) { return *vm.errorPrototypeTable; } - static const HashTable& globalObjectTable(VM& vm) { return *vm.globalObjectTable; } - static const HashTable& jsonTable(VM& vm) { return *vm.jsonTable; } - static const HashTable& numberConstructorTable(VM& vm) { return *vm.numberConstructorTable; } - static const HashTable& numberPrototypeTable(VM& vm) { return *vm.numberPrototypeTable; } - static const HashTable& objectConstructorTable(VM& vm) { return *vm.objectConstructorTable; } - static const HashTable& privateNamePrototypeTable(VM& vm) { return *vm.privateNamePrototypeTable; } - static const HashTable& regExpTable(VM& vm) { return *vm.regExpTable; } - static const HashTable& regExpConstructorTable(VM& vm) { return *vm.regExpConstructorTable; } - static const HashTable& regExpPrototypeTable(VM& vm) { return *vm.regExpPrototypeTable; } - static const HashTable& stringConstructorTable(VM& vm) { return *vm.stringConstructorTable; } -#if ENABLE(PROMISES) - static const HashTable& promisePrototypeTable(VM& vm) { return *vm.promisePrototypeTable; } - static const HashTable& promiseConstructorTable(VM& vm) { return *vm.promiseConstructorTable; } -#endif + static CallFrame* create(Register* callFrameBase) { return static_cast<CallFrame*>(callFrameBase); } Register* registers() { return this; } + const Register* registers() const { return this; } CallFrame& operator=(const Register& r) { *static_cast<Register*>(this) = r; return *this; } - CallFrame* callerFrame() const { return callerFrameAndPC().callerFrame; } + CallFrame* callerFrame() const { return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); } + void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; } + SUPPRESS_ASAN void* unsafeCallerFrameOrVMEntryFrame() const { return unsafeCallerFrameAndPC().callerFrame; } + + CallFrame* unsafeCallerFrame(VMEntryFrame*&); + JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&); + + JS_EXPORT_PRIVATE SourceOrigin callerSourceOrigin(); + static ptrdiff_t callerFrameOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame); } ReturnAddressPtr returnPC() const { return ReturnAddressPtr(callerFrameAndPC().pc); } @@ -120,51 +147,17 @@ namespace JSC { 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 - - 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; + bool callSiteBitsAreBytecodeOffset() const; + bool callSiteBitsAreCodeOriginIndex() const; - void setLocationAsRawBits(unsigned); - void setLocationAsBytecodeOffset(unsigned); + unsigned callSiteAsRawBits() const; + unsigned unsafeCallSiteAsRawBits() const; + CallSiteIndex callSiteIndex() const; + CallSiteIndex unsafeCallSiteIndex() const; + private: + unsigned callSiteBitsAsBytecodeOffset() const; + public: -#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 @@ -174,59 +167,35 @@ namespace JSC { // This will get you a CodeOrigin. It will always succeed. May return // CodeOrigin(0) if we're in native code. - CodeOrigin codeOrigin(); + JS_EXPORT_PRIVATE CodeOrigin codeOrigin(); - Register* frameExtent() + Register* topOfFrame() { - if (isVMEntrySentinel() || !codeBlock()) - return registers() - 1; - return frameExtentInternal(); + if (!codeBlock()) + return registers(); + return topOfFrameInternal(); } - Register* frameExtentInternal(); - -#if USE(JSVALUE32_64) - Instruction* currentVPC() const - { - ASSERT(!isVMEntrySentinel()); - return bitwise_cast<Instruction*>(this[JSStack::ArgumentCount].tag()); - } - void setCurrentVPC(Instruction* vpc) - { - ASSERT(!isVMEntrySentinel()); - this[JSStack::ArgumentCount].tag() = bitwise_cast<int32_t>(vpc); - } -#else - Instruction* currentVPC() const; + Instruction* currentVPC() const; // This only makes sense in the LLInt and baseline. void setCurrentVPC(Instruction* vpc); -#endif void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; } - void setScope(JSScope* scope) { static_cast<Register*>(this)[JSStack::ScopeChain] = scope; } + void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; } - ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, JSScope* scope, - CallFrame* callerFrame, int argc, JSObject* callee) - { - ASSERT(callerFrame == noCaller() || callerFrame->isVMEntrySentinel() || callerFrame->stack()->containsAddress(this)); - - setCodeBlock(codeBlock); - setScope(scope); - 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); - } + static void initGlobalExec(ExecState* globalExec, JSCallee* globalCallee); // Read a register from the codeframe (or constant from the CodeBlock). Register& r(int); + Register& r(VirtualRegister); // Read a register for a non-constant Register& uncheckedR(int); + Register& uncheckedR(VirtualRegister); // 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); } + size_t argumentCountIncludingThis() const { return this[CallFrameSlot::argumentCount].payload(); } + static int argumentOffset(int argument) { return (CallFrameSlot::firstArgument + argument); } + static int argumentOffsetIncludingThis(int argument) { return (CallFrameSlot::thisArgument + argument); } // In the following (argument() and setArgument()), the 'argument' // parameter is the index of the arguments of the target function of @@ -237,6 +206,7 @@ namespace JSC { // arguments(0) will not fetch the 'this' value. To get/set 'this', // use thisValue() and setThisValue() below. + JSValue* addressOfArgumentsStart() const { return bitwise_cast<JSValue*>(this + argumentOffset(0)); } JSValue argument(size_t argument) { if (argument >= argumentCount()) @@ -253,71 +223,55 @@ namespace JSC { 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); + // Under the constructor implemented in C++, thisValue holds the newTarget instead of the automatically constructed value. + // The result of this function is only effective under the "construct" context. + JSValue newTarget() { return thisValue(); } - static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; } + JSValue argumentAfterCapture(size_t argument); - // FIXME: Remove these. - int hostThisRegister() { return thisArgumentOffset(); } - JSValue hostThisValue() { return thisValue(); } + static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + CallFrameSlot::thisArgument - 1; } static CallFrame* noCaller() { return 0; } - bool isVMEntrySentinel() const - { - return !!this && codeBlock() == vmEntrySentinelCodeBlock(); - } - - CallFrame* vmEntrySentinelCallerFrame() const - { - ASSERT(isVMEntrySentinel()); - return this[JSStack::ScopeChain].callFrame(); - } - - void initializeVMEntrySentinelFrame(CallFrame* callFrame) - { - setCallerFrame(noCaller()); - setReturnPC(0); - setCodeBlock(vmEntrySentinelCodeBlock()); - static_cast<Register*>(this)[JSStack::ScopeChain] = callFrame; - setCallee(0); - setArgumentCountIncludingThis(0); - } - - CallFrame* callerFrameSkippingVMEntrySentinel() - { - CallFrame* caller = callerFrame(); - if (caller->isVMEntrySentinel()) - return caller->vmEntrySentinelCallerFrame(); - return caller; - } - - void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[JSStack::ArgumentCount].payload() = count; } - void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = Register::withCallee(callee); } - void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[JSStack::CodeBlock] = codeBlock; } + void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[CallFrameSlot::argumentCount].payload() = count; } + void setCallee(JSObject* callee) { static_cast<Register*>(this)[CallFrameSlot::callee] = callee; } + void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[CallFrameSlot::codeBlock] = codeBlock; } void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast<Instruction*>(value); } - // CallFrame::iterate() expects a Functor that implements the following method: - // StackVisitor::Status operator()(StackVisitor&); + String friendlyFunctionName(); - template <typename Functor> void iterate(Functor& functor) + // CallFrame::iterate() expects a Functor that implements the following method: + // StackVisitor::Status operator()(StackVisitor&) const; + // FIXME: This method is improper. We rely on the fact that we can call it with a null + // receiver. We should always be using StackVisitor directly. + template <typename Functor> void iterate(const Functor& functor) { StackVisitor::visit<Functor>(this, functor); } + void dump(PrintStream&); + JS_EXPORT_PRIVATE const char* describeFrame(); + private: - static const intptr_t s_VMEntrySentinel = 1; -#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: @@ -331,31 +285,16 @@ namespace JSC { int offset = reg - this->registers(); // The offset is defined (based on argumentOffset()) to be: - // offset = JSStack::FirstArgument - argIndex; + // offset = CallFrameSlot::firstArgument - argIndex; // Hence: - // argIndex = JSStack::FirstArgument - offset; - size_t argIndex = offset - JSStack::FirstArgument; + // argIndex = CallFrameSlot::firstArgument - offset; + size_t argIndex = offset - CallFrameSlot::firstArgument; return argIndex; } - 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(); - } - CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast<CallerFrameAndPC*>(this); } const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); } - - static CodeBlock* vmEntrySentinelCodeBlock() { return reinterpret_cast<CodeBlock*>(s_VMEntrySentinel); } - - friend class JSStack; - friend class VMInspector; + SUPPRESS_ASAN const CallerFrameAndPC& unsafeCallerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); } }; } // namespace JSC - -#endif // CallFrame_h |