qtdeclarative/src/qml/jsruntime/qv4stackframe_p.h

304 lines
10 KiB
C++

// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QV4STACKFRAME_H
#define QV4STACKFRAME_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qv4context_p.h>
#include <private/qv4enginebase_p.h>
#include <private/qv4calldata_p.h>
#include <private/qv4function_p.h>
#include <type_traits>
QT_BEGIN_NAMESPACE
namespace QV4 {
struct CppStackFrame;
struct Q_QML_PRIVATE_EXPORT CppStackFrameBase
{
enum class Kind : quint8 { JS, Meta };
CppStackFrame *parent;
Function *v4Function;
int originalArgumentsCount;
int instructionPointer;
QT_WARNING_PUSH
QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union
union {
struct {
Value *savedStackTop;
CallData *jsFrame;
const Value *originalArguments;
const char *yield;
const char *unwindHandler;
const char *unwindLabel;
int unwindLevel;
bool yieldIsIterator;
bool callerCanHandleTailCall;
bool pendingTailCall;
bool isTailCalling;
};
struct {
ExecutionContext *context;
QObject *thisObject;
const QMetaType *metaTypes;
void **returnAndArgs;
bool returnValueIsUndefined;
};
};
QT_WARNING_POP
Kind kind;
};
struct Q_QML_PRIVATE_EXPORT CppStackFrame : protected CppStackFrameBase
{
// We want to have those public but we can't declare them as public without making the struct
// non-standard layout. So we have this other struct with "using" in between.
using CppStackFrameBase::instructionPointer;
using CppStackFrameBase::v4Function;
void init(Function *v4Function, int argc, Kind kind) {
this->v4Function = v4Function;
originalArgumentsCount = argc;
instructionPointer = 0;
this->kind = kind;
}
bool isJSTypesFrame() const { return kind == Kind::JS; }
bool isMetaTypesFrame() const { return kind == Kind::Meta; }
QString source() const;
QString function() const;
int lineNumber() const;
int statementNumber() const;
int missingLineNumber() const;
CppStackFrame *parentFrame() const { return parent; }
void setParentFrame(CppStackFrame *parentFrame) { parent = parentFrame; }
int argc() const { return originalArgumentsCount; }
inline ExecutionContext *context() const;
Heap::CallContext *callContext() const { return callContext(context()->d()); }
ReturnedValue thisObject() const;
protected:
CppStackFrame() = default;
void push(EngineBase *engine)
{
Q_ASSERT(kind == Kind::JS || kind == Kind::Meta);
parent = engine->currentStackFrame;
engine->currentStackFrame = this;
}
void pop(EngineBase *engine)
{
engine->currentStackFrame = parent;
}
Heap::CallContext *callContext(Heap::ExecutionContext *ctx) const
{
while (ctx->type != Heap::ExecutionContext::Type_CallContext)
ctx = ctx->outer;
return static_cast<Heap::CallContext *>(ctx);
}
};
struct Q_QML_PRIVATE_EXPORT MetaTypesStackFrame : public CppStackFrame
{
using CppStackFrame::push;
using CppStackFrame::pop;
void init(Function *v4Function, QObject *thisObject, ExecutionContext *context,
void **returnAndArgs, const QMetaType *metaTypes, int argc)
{
CppStackFrame::init(v4Function, argc, Kind::Meta);
CppStackFrameBase::thisObject = thisObject;
CppStackFrameBase::context = context;
CppStackFrameBase::metaTypes = metaTypes;
CppStackFrameBase::returnAndArgs = returnAndArgs;
CppStackFrameBase::returnValueIsUndefined = false;
}
QMetaType returnType() const { return metaTypes[0]; }
void *returnValue() const { return returnAndArgs[0]; }
bool isReturnValueUndefined() const { return CppStackFrameBase::returnValueIsUndefined; }
void setReturnValueUndefined() { CppStackFrameBase::returnValueIsUndefined = true; }
const QMetaType *argTypes() const { return metaTypes + 1; }
void **argv() const { return returnAndArgs + 1; }
QObject *thisObject() const { return CppStackFrameBase::thisObject; }
ExecutionContext *context() const { return CppStackFrameBase::context; }
void setContext(ExecutionContext *context) { CppStackFrameBase::context = context; }
Heap::CallContext *callContext() const
{
return CppStackFrame::callContext(CppStackFrameBase::context->d());
}
};
struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame
{
using CppStackFrame::jsFrame;
// The JIT needs to poke directly into those using offsetof
using CppStackFrame::unwindHandler;
using CppStackFrame::unwindLabel;
using CppStackFrame::unwindLevel;
void init(Function *v4Function, const Value *argv, int argc,
bool callerCanHandleTailCall = false)
{
CppStackFrame::init(v4Function, argc, Kind::JS);
CppStackFrame::originalArguments = argv;
CppStackFrame::yield = nullptr;
CppStackFrame::unwindHandler = nullptr;
CppStackFrame::yieldIsIterator = false;
CppStackFrame::callerCanHandleTailCall = callerCanHandleTailCall;
CppStackFrame::pendingTailCall = false;
CppStackFrame::isTailCalling = false;
CppStackFrame::unwindLabel = nullptr;
CppStackFrame::unwindLevel = 0;
}
const Value *argv() const { return originalArguments; }
static uint requiredJSStackFrameSize(uint nRegisters) {
return CallData::HeaderSize() + nRegisters;
}
static uint requiredJSStackFrameSize(Function *v4Function) {
return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters;
}
uint requiredJSStackFrameSize() const {
return requiredJSStackFrameSize(v4Function);
}
void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
const Value &thisObject, const Value &newTarget = Value::undefinedValue()) {
setupJSFrame(stackSpace, function, scope, thisObject, newTarget,
v4Function->compiledFunction->nFormals,
v4Function->compiledFunction->nRegisters);
}
void setupJSFrame(
Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters)
{
jsFrame = reinterpret_cast<CallData *>(stackSpace);
jsFrame->function = function;
jsFrame->context = scope->asReturnedValue();
jsFrame->accumulator = Encode::undefined();
jsFrame->thisObject = thisObject;
jsFrame->newTarget = newTarget;
uint argc = uint(originalArgumentsCount);
if (argc > nFormals)
argc = nFormals;
jsFrame->setArgc(argc);
// memcpy requires non-null ptr, even if argc * sizeof(Value) == 0
if (originalArguments)
memcpy(jsFrame->args, originalArguments, argc * sizeof(Value));
Q_STATIC_ASSERT(Encode::undefined() == 0);
memset(jsFrame->args + argc, 0, (nRegisters - argc) * sizeof(Value));
if (v4Function && v4Function->compiledFunction) {
const int firstDeadZoneRegister
= v4Function->compiledFunction->firstTemporalDeadZoneRegister;
const int registerDeadZoneSize
= v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone;
const Value * tdzEnd = stackSpace + firstDeadZoneRegister + registerDeadZoneSize;
for (Value *v = stackSpace + firstDeadZoneRegister; v < tdzEnd; ++v)
*v = Value::emptyValue().asReturnedValue();
}
}
ExecutionContext *context() const
{
return static_cast<ExecutionContext *>(&jsFrame->context);
}
void setContext(ExecutionContext *context)
{
jsFrame->context = context;
}
Heap::CallContext *callContext() const
{
return CppStackFrame::callContext(static_cast<ExecutionContext &>(jsFrame->context).d());
}
bool isTailCalling() const { return CppStackFrame::isTailCalling; }
void setTailCalling(bool tailCalling) { CppStackFrame::isTailCalling = tailCalling; }
bool pendingTailCall() const { return CppStackFrame::pendingTailCall; }
void setPendingTailCall(bool pending) { CppStackFrame::pendingTailCall = pending; }
const char *yield() const { return CppStackFrame::yield; }
void setYield(const char *yield) { CppStackFrame::yield = yield; }
bool yieldIsIterator() const { return CppStackFrame::yieldIsIterator; }
void setYieldIsIterator(bool isIter) { CppStackFrame::yieldIsIterator = isIter; }
bool callerCanHandleTailCall() const { return CppStackFrame::callerCanHandleTailCall; }
ReturnedValue thisObject() const
{
return jsFrame->thisObject.asReturnedValue();
}
Value *framePointer() const { return savedStackTop; }
void push(EngineBase *engine) {
CppStackFrame::push(engine);
savedStackTop = engine->jsStackTop;
}
void pop(EngineBase *engine) {
CppStackFrame::pop(engine);
engine->jsStackTop = savedStackTop;
}
};
inline ExecutionContext *CppStackFrame::context() const
{
if (isJSTypesFrame())
return static_cast<const JSTypesStackFrame *>(this)->context();
Q_ASSERT(isMetaTypesFrame());
return static_cast<const MetaTypesStackFrame *>(this)->context();
}
Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(JSTypesStackFrame));
Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(MetaTypesStackFrame));
Q_STATIC_ASSERT(std::is_standard_layout_v<CppStackFrame>);
Q_STATIC_ASSERT(std::is_standard_layout_v<JSTypesStackFrame>);
Q_STATIC_ASSERT(std::is_standard_layout_v<MetaTypesStackFrame>);
}
QT_END_NAMESPACE
#endif