// 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 #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QV4 { struct CppStackFrame; struct Q_QML_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_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(ctx); } }; struct Q_QML_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; } const QMetaType *returnAndArgTypes() const { return metaTypes; } void **returnAndArgValues() const { return returnAndArgs; } 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_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(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(&jsFrame->context); } void setContext(ExecutionContext *context) { jsFrame->context = context; } Heap::CallContext *callContext() const { return CppStackFrame::callContext(static_cast(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(this)->context(); Q_ASSERT(isMetaTypesFrame()); return static_cast(this)->context(); } struct ScopedStackFrame { ScopedStackFrame(const Scope &scope, ExecutionContext *context) : engine(scope.engine) { if (auto currentFrame = engine->currentStackFrame) { frame.init(currentFrame->v4Function, nullptr, context, nullptr, nullptr, 0); frame.instructionPointer = currentFrame->instructionPointer; } else { frame.init(nullptr, nullptr, context, nullptr, nullptr, 0); } frame.push(engine); } ~ScopedStackFrame() { frame.pop(engine); } private: ExecutionEngine *engine = nullptr; MetaTypesStackFrame frame; }; Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(JSTypesStackFrame)); Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(MetaTypesStackFrame)); Q_STATIC_ASSERT(std::is_standard_layout_v); Q_STATIC_ASSERT(std::is_standard_layout_v); Q_STATIC_ASSERT(std::is_standard_layout_v); } QT_END_NAMESPACE #endif