diff options
Diffstat (limited to 'src/qml/jsruntime/qv4stackframe_p.h')
-rw-r--r-- | src/qml/jsruntime/qv4stackframe_p.h | 328 |
1 files changed, 244 insertions, 84 deletions
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index 616fa9a5a9..f24b0b2433 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 @@ -50,61 +14,179 @@ // We mean it. // +#include <private/qv4scopedvalue_p.h> #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 Q_QML_EXPORT CppStackFrame { - EngineBase *engine; - Value *savedStackTop; +struct CppStackFrame; +struct Q_QML_EXPORT CppStackFrameBase +{ + enum class Kind : quint8 { JS, Meta }; + CppStackFrame *parent; Function *v4Function; - CallData *jsFrame; - const Value *originalArguments; int originalArgumentsCount; int instructionPointer; - const char *yield; - const char *unwindHandler; - const char *unwindLabel; - int unwindLevel; - bool yieldIsIterator; - bool callerCanHandleTailCall; - bool pendingTailCall; - bool isTailCalling; - - void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc, bool callerCanHandleTailCall = false) { - this->engine = engine; + 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; - originalArguments = argv; originalArgumentsCount = argc; instructionPointer = 0; - yield = nullptr; - unwindHandler = nullptr; - unwindLabel = nullptr; - unwindLevel = 0; - yieldIsIterator = false; - this->callerCanHandleTailCall = callerCanHandleTailCall; - pendingTailCall = false; - isTailCalling = false; + this->kind = kind; } - void push() { + 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; - savedStackTop = engine->jsStackTop; } - void pop() { + void pop(EngineBase *engine) + { engine->currentStackFrame = parent; - engine->jsStackTop = savedStackTop; } + 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_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; } @@ -114,13 +196,17 @@ struct Q_QML_EXPORT CppStackFrame { 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); + 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) + + 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; @@ -134,13 +220,17 @@ struct Q_QML_EXPORT CppStackFrame { argc = nFormals; jsFrame->setArgc(argc); - memcpy(jsFrame->args, originalArguments, argc*sizeof(Value)); + // 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)); + 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 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) @@ -148,22 +238,92 @@ struct Q_QML_EXPORT CppStackFrame { } } - QString source() const; - QString function() const; - inline QV4::ExecutionContext *context() const { + ExecutionContext *context() const + { return static_cast<ExecutionContext *>(&jsFrame->context); } - int lineNumber() const; - inline QV4::Heap::CallContext *callContext() const { - Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ - while (ctx->type != Heap::ExecutionContext::Type_CallContext) - ctx = ctx->outer; - return static_cast<Heap::CallContext *>(ctx); + 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; } - ReturnedValue thisObject() const; }; +inline ExecutionContext *CppStackFrame::context() const +{ + if (isJSTypesFrame()) + return static_cast<const JSTypesStackFrame *>(this)->context(); + + Q_ASSERT(isMetaTypesFrame()); + return static_cast<const MetaTypesStackFrame *>(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<CppStackFrame>); +Q_STATIC_ASSERT(std::is_standard_layout_v<JSTypesStackFrame>); +Q_STATIC_ASSERT(std::is_standard_layout_v<MetaTypesStackFrame>); + } QT_END_NAMESPACE |