#include "config.h"
#include "CallFrame.h"
#include "CodeBlock.h"
#include "InlineCallFrame.h"
#include "Interpreter.h"
#include "JSCInlines.h"
#include "JSWebAssemblyInstance.h"
#include "VMEntryScope.h"
#include "WasmContext.h"
#include "WasmInstance.h"
#include <wtf/StringPrintStream.h>
namespace JSC {
void ExecState::initGlobalExec(ExecState* globalExec, JSCallee* globalCallee)
{
globalExec->setCodeBlock(nullptr);
globalExec->setCallerFrame(noCaller());
globalExec->setReturnPC(0);
globalExec->setArgumentCountIncludingThis(0);
globalExec->setCallee(globalCallee);
}
bool CallFrame::callSiteBitsAreBytecodeOffset() const
{
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[CallFrameSlot::argumentCount].tag();
}
SUPPRESS_ASAN unsigned CallFrame::unsafeCallSiteAsRawBits() const
{
return this[CallFrameSlot::argumentCount].unsafeTag();
}
CallSiteIndex CallFrame::callSiteIndex() const
{
return CallSiteIndex(callSiteAsRawBits());
}
SUPPRESS_ASAN CallSiteIndex CallFrame::unsafeCallSiteIndex() const
{
return CallSiteIndex(unsafeCallSiteAsRawBits());
}
#if USE(JSVALUE32_64)
Instruction* CallFrame::currentVPC() const
{
return bitwise_cast<Instruction*>(callSiteIndex().bits());
}
void CallFrame::setCurrentVPC(Instruction* vpc)
{
CallSiteIndex callSite(vpc);
this[CallFrameSlot::argumentCount].tag() = callSite.bits();
}
unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
{
ASSERT(codeBlock());
ASSERT(callSiteBitsAreBytecodeOffset());
return codeBlock()->bytecodeOffset(currentVPC());
}
#else // USE(JSVALUE32_64)
Instruction* CallFrame::currentVPC() const
{
ASSERT(callSiteBitsAreBytecodeOffset());
return &codeBlock()->instructions()[callSiteBitsAsBytecodeOffset()];
}
void CallFrame::setCurrentVPC(Instruction* vpc)
{
CallSiteIndex callSite(codeBlock()->bytecodeOffset(vpc));
this[CallFrameSlot::argumentCount].tag() = static_cast<int32_t>(callSite.bits());
}
unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
{
ASSERT(codeBlock());
ASSERT(callSiteBitsAreBytecodeOffset());
return callSiteIndex().bits();
}
#endif
unsigned CallFrame::bytecodeOffset()
{
ASSERT(!callee().isWasm());
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;
}
return codeOrigin.bytecodeIndex;
}
#endif
ASSERT(callSiteBitsAreBytecodeOffset());
return callSiteBitsAsBytecodeOffset();
}
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);
}
#endif
return CodeOrigin(callSiteBitsAsBytecodeOffset());
}
Register* CallFrame::topOfFrameInternal()
{
CodeBlock* codeBlock = this->codeBlock();
ASSERT(codeBlock);
return registers() + codeBlock->stackPointerOffset();
}
JSGlobalObject* CallFrame::vmEntryGlobalObject()
{
RELEASE_ASSERT(callee().isCell());
if (callee().asCell()->isObject()) {
if (this == lexicalGlobalObject()->globalExec())
return lexicalGlobalObject();
}
ASSERT(vm().entryScope);
return vm().entryScope->globalObject();
}
JSGlobalObject* CallFrame::vmEntryGlobalObject(VM& vm)
{
if (callee().isCell() && callee().asCell()->isObject()) {
if (this == lexicalGlobalObject()->globalExec())
return lexicalGlobalObject();
}
ASSERT(vm.entryScope);
return vm.entryScope->globalObject();
}
JSGlobalObject* CallFrame::wasmAwareLexicalGlobalObject(VM& vm)
{
#if ENABLE(WEBASSEMBLY)
if (!callee().isWasm())
return lexicalGlobalObject();
return vm.wasmContext.load()->owner<JSWebAssemblyInstance>()->globalObject(vm);
#else
UNUSED_PARAM(vm);
return lexicalGlobalObject();
#endif
}
bool CallFrame::isAnyWasmCallee()
{
CalleeBits callee = this->callee();
if (callee.isWasm())
return true;
ASSERT(callee.isCell());
if (!!callee.rawPtr() && isWebAssemblyToJSCallee(callee.asCell()))
return true;
return false;
}
CallFrame* CallFrame::callerFrame(EntryFrame*& currEntryFrame)
{
if (callerFrameOrEntryFrame() == currEntryFrame) {
VMEntryRecord* currVMEntryRecord = vmEntryRecord(currEntryFrame);
currEntryFrame = currVMEntryRecord->prevTopEntryFrame();
return currVMEntryRecord->prevTopCallFrame();
}
return static_cast<CallFrame*>(callerFrameOrEntryFrame());
}
SUPPRESS_ASAN CallFrame* CallFrame::unsafeCallerFrame(EntryFrame*& currEntryFrame)
{
if (unsafeCallerFrameOrEntryFrame() == currEntryFrame) {
VMEntryRecord* currVMEntryRecord = vmEntryRecord(currEntryFrame);
currEntryFrame = currVMEntryRecord->unsafePrevTopEntryFrame();
return currVMEntryRecord->unsafePrevTopCallFrame();
}
return static_cast<CallFrame*>(unsafeCallerFrameOrEntryFrame());
}
SourceOrigin CallFrame::callerSourceOrigin()
{
RELEASE_ASSERT(callee().isCell());
VM* vm = &this->vm();
SourceOrigin sourceOrigin;
bool haveSkippedFirstFrame = false;
StackVisitor::visit(this, vm, [&](StackVisitor& visitor) {
if (!std::exchange(haveSkippedFirstFrame, true))
return StackVisitor::Status::Continue;
switch (visitor->codeType()) {
case StackVisitor::Frame::CodeType::Function:
if (static_cast<FunctionExecutable*>(visitor->codeBlock()->ownerScriptExecutable())->isBuiltinFunction())
return StackVisitor::Status::Continue;
FALLTHROUGH;
case StackVisitor::Frame::CodeType::Eval:
case StackVisitor::Frame::CodeType::Module:
case StackVisitor::Frame::CodeType::Global:
sourceOrigin = visitor->codeBlock()->ownerScriptExecutable()->sourceOrigin();
return StackVisitor::Status::Done;
case StackVisitor::Frame::CodeType::Native:
return StackVisitor::Status::Continue;
case StackVisitor::Frame::CodeType::Wasm:
return StackVisitor::Status::Done;
}
RELEASE_ASSERT_NOT_REACHED();
return StackVisitor::Status::Done;
});
return sourceOrigin;
}
String CallFrame::friendlyFunctionName()
{
CodeBlock* codeBlock = this->codeBlock();
if (!codeBlock)
return emptyString();
switch (codeBlock->codeType()) {
case EvalCode:
return "eval code"_s;
case ModuleCode:
return "module code"_s;
case GlobalCode:
return "global code"_s;
case FunctionCode:
if (jsCallee())
return getCalculatedDisplayName(vm(), jsCallee());
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(), " bc#", bytecodeOffset(), "]");
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;
}
}