#pragma once
#include "AbstractPC.h"
#include "CalleeBits.h"
#include "MacroAssemblerCodeRef.h"
#include "Register.h"
#include "StackVisitor.h"
#include "VM.h"
#include "VMEntryRecord.h"
namespace JSC {
class Arguments;
class ExecState;
class Interpreter;
class JSCallee;
class JSScope;
class SourceOrigin;
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;
};
class ExecState : private Register {
public:
static const int headerSizeInRegisters = CallFrameSlot::argumentCount + 1;
JSValue guaranteedJSValueCallee() const
{
ASSERT(!callee().isWasm());
return this[CallFrameSlot::callee].jsValue();
}
JSObject* jsCallee() const
{
ASSERT(!callee().isWasm());
return this[CallFrameSlot::callee].object();
}
CalleeBits callee() const { return CalleeBits(this[CallFrameSlot::callee].pointer()); }
SUPPRESS_ASAN CalleeBits unsafeCallee() const { return CalleeBits(this[CallFrameSlot::callee].pointer()); }
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[scopeRegisterOffset].Register::scope());
return this[scopeRegisterOffset].Register::scope();
}
JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject();
JSGlobalObject* vmEntryGlobalObject(VM&);
JSGlobalObject* wasmAwareLexicalGlobalObject(VM&);
bool isAnyWasmCallee();
JSGlobalObject* lexicalGlobalObject() const;
JSObject* globalThisValue() const;
VM& vm() const;
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 static_cast<CallFrame*>(callerFrameOrEntryFrame()); }
void* callerFrameOrEntryFrame() const { return callerFrameAndPC().callerFrame; }
SUPPRESS_ASAN void* unsafeCallerFrameOrEntryFrame() const { return unsafeCallerFrameAndPC().callerFrame; }
CallFrame* unsafeCallerFrame(EntryFrame*&);
JS_EXPORT_PRIVATE CallFrame* callerFrame(EntryFrame*&);
JS_EXPORT_PRIVATE SourceOrigin callerSourceOrigin();
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); }
bool callSiteBitsAreBytecodeOffset() const;
bool callSiteBitsAreCodeOriginIndex() const;
unsigned callSiteAsRawBits() const;
unsigned unsafeCallSiteAsRawBits() const;
CallSiteIndex callSiteIndex() const;
CallSiteIndex unsafeCallSiteIndex() const;
private:
unsigned callSiteBitsAsBytecodeOffset() const;
public:
unsigned bytecodeOffset();
JS_EXPORT_PRIVATE CodeOrigin codeOrigin();
Register* topOfFrame()
{
if (!codeBlock())
return registers();
return topOfFrameInternal();
}
Instruction* currentVPC() const; void setCurrentVPC(Instruction* vpc);
void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; }
void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; }
static void initGlobalExec(ExecState* globalExec, JSCallee* globalCallee);
Register& r(int);
Register& r(VirtualRegister);
Register& uncheckedR(int);
Register& uncheckedR(VirtualRegister);
size_t argumentCount() const { return argumentCountIncludingThis() - 1; }
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); }
JSValue* addressOfArgumentsStart() const { return bitwise_cast<JSValue*>(this + argumentOffset(0)); }
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)
{
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 newTarget() { return thisValue(); }
JSValue argumentAfterCapture(size_t argument);
static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + CallFrameSlot::thisArgument - 1; }
static CallFrame* noCaller() { return 0; }
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); }
String friendlyFunctionName();
template <typename Functor> void iterate(const Functor& functor)
{
VM* vm;
void* rawThis = this;
if (!!rawThis) {
RELEASE_ASSERT(callee().isCell());
vm = &this->vm();
} else
vm = nullptr;
StackVisitor::visit<Functor>(this, vm, functor);
}
void dump(PrintStream&);
JS_EXPORT_PRIVATE const char* describeFrame();
private:
ExecState();
~ExecState();
Register* topOfFrameInternal();
size_t argIndexForRegister(Register* reg)
{
int offset = reg - this->registers();
size_t argIndex = offset - CallFrameSlot::firstArgument;
return argIndex;
}
CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast<CallerFrameAndPC*>(this); }
const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); }
SUPPRESS_ASAN const CallerFrameAndPC& unsafeCallerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); }
};
}