diff options
Diffstat (limited to 'src/qml/jsruntime/qv4engine_p.h')
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 303 |
1 files changed, 219 insertions, 84 deletions
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 6c3d2d4436..8e1bd24f6b 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 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 QV4ENGINE_H #define QV4ENGINE_H @@ -50,20 +14,21 @@ // We mean it. // -#include "qv4global_p.h" -#include "qv4managed_p.h" -#include "qv4context_p.h" -#include "qv4stackframe_p.h" #include <private/qintrusivelist_p.h> -#include "qv4enginebase_p.h" -#include <private/qqmlrefcount_p.h> #include <private/qqmldelayedcallqueue_p.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qmutex.h> - -#include "qv4function_p.h" +#include <private/qqmlrefcount_p.h> #include <private/qv4compileddata_p.h> +#include <private/qv4context_p.h> +#include <private/qv4enginebase_p.h> #include <private/qv4executablecompilationunit_p.h> +#include <private/qv4function_p.h> +#include <private/qv4global_p.h> +#include <private/qv4stacklimits_p.h> + +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qmutex.h> +#include <QtCore/qprocessordetection.h> +#include <QtCore/qset.h> namespace WTF { class BumpPointerAllocator; @@ -110,7 +75,7 @@ namespace QV4 { struct QObjectMethod; } // class MyClass : public QObject { // Q_OBJECT // ... -// Q_INVOKABLE void myMethod(QQmlV4Function*); +// Q_INVOKABLE void myMethod(QQmlV4FunctionPtr); // }; // The QQmlV8Function - and consequently the arguments and return value - only remains // valid during the call. If the return value isn't set within myMethod(), the will return @@ -144,6 +109,7 @@ class QQmlError; class QJSEngine; class QQmlEngine; class QQmlContextData; +class QQmlTypeLoader; namespace QV4 { namespace Debugging { @@ -169,12 +135,24 @@ class ReactionHandler; struct Q_QML_EXPORT ExecutionEngine : public EngineBase { private: - static qint32 maxCallDepth; - friend struct ExecutionContextSaver; friend struct ExecutionContext; friend struct Heap::ExecutionContext; public: + enum class DiskCache { + Disabled = 0, + AotByteCode = 1 << 0, + AotNative = 1 << 1, + QmlcRead = 1 << 2, + QmlcWrite = 1 << 3, + Aot = AotByteCode | AotNative, + Qmlc = QmlcRead | QmlcWrite, + Enabled = Aot | Qmlc, + + }; + + Q_DECLARE_FLAGS(DiskCacheOptions, DiskCache); + ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; @@ -196,6 +174,14 @@ public: QQmlEngine *qmlEngine() const { return m_qmlEngine; } QJSEngine *publicEngine; + template<typename TypeLoader = QQmlTypeLoader> + TypeLoader *typeLoader() + { + if (m_qmlEngine) + return TypeLoader::get(m_qmlEngine); + return nullptr; + } + enum JSObjects { RootContext, ScriptContext, @@ -221,9 +207,7 @@ public: URIErrorProto, PromiseProto, VariantProto, -#if QT_CONFIG(qml_sequence_object) SequenceProto, -#endif SharedArrayBufferProto, ArrayBufferProto, DataViewProto, @@ -344,9 +328,7 @@ public: Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); } Object *promisePrototype() const { return reinterpret_cast<Object *>(jsObjects + PromiseProto); } Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); } -#if QT_CONFIG(qml_sequence_object) Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); } -#endif Object *sharedArrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + SharedArrayBufferProto); } Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); } @@ -514,8 +496,6 @@ public: Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); } Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); } - QIntrusiveList<ExecutableCompilationUnit, &ExecutableCompilationUnit::nextCompilationUnit> compilationUnits; - quint32 m_engineId; RegExpCache *regExpCache; @@ -528,7 +508,8 @@ public: // calling preserve() on the object which removes it from this scarceResource list. class ScarceResourceData { public: - ScarceResourceData(const QVariant &data = QVariant()) : data(data) {} + ScarceResourceData() = default; + ScarceResourceData(const QMetaType type, const void *data) : data(type, data) {} QVariant data; QIntrusiveListNode node; }; @@ -561,7 +542,14 @@ public: void setProfiler(Profiling::Profiler *profiler); #endif // QT_CONFIG(qml_debug) - ExecutionContext *currentContext() const; + // We don't want to #include <private/qv4stackframe_p.h> here, but we still want + // currentContext() to be inline. Therefore we shift the requirement to provide the + // complete type of CppStackFrame to the caller by making this a template. + template<typename StackFrame = CppStackFrame> + ExecutionContext *currentContext() const + { + return static_cast<const StackFrame *>(currentStackFrame)->context(); + } // ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast quintptr newProtoId() { return (protoIdCount += 2); } @@ -571,6 +559,7 @@ public: Heap::Object *newObject(); Heap::Object *newObject(Heap::InternalClass *internalClass); + Heap::String *newString(char16_t c) { return newString(QChar(c)); } Heap::String *newString(const QString &s = QString()); Heap::String *newIdentifier(const QString &text); @@ -587,9 +576,11 @@ public: Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array); Heap::ArrayBuffer *newArrayBuffer(size_t length); - Heap::DateObject *newDateObject(const Value &value); - Heap::DateObject *newDateObject(const QDateTime &dt); - Heap::DateObject *newDateObjectFromTime(QTime t); + Heap::DateObject *newDateObject(double dateTime); + Heap::DateObject *newDateObject(const QDateTime &dateTime); + Heap::DateObject *newDateObject(QDate date, Heap::Object *parent, int index, uint flags); + Heap::DateObject *newDateObject(QTime time, Heap::Object *parent, int index, uint flags); + Heap::DateObject *newDateObject(QDateTime dateTime, Heap::Object *parent, int index, uint flags); Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); Heap::RegExpObject *newRegExpObject(RegExp *re); @@ -598,6 +589,7 @@ public: #endif Heap::UrlObject *newUrlObject(); + Heap::UrlObject *newUrlObject(const QUrl &url); Heap::UrlSearchParamsObject *newUrlSearchParamsObject(); Heap::Object *newErrorObject(const Value &value); @@ -616,13 +608,32 @@ public: Heap::Object *newPromiseObject(const QV4::FunctionObject *thisObject, const QV4::PromiseCapability *capability); Promise::ReactionHandler *getPromiseReactionHandler(); - Heap::Object *newVariantObject(const QVariant &v); + Heap::Object *newVariantObject(const QMetaType type, const void *data); Heap::Object *newForInIteratorObject(Object *o); Heap::Object *newSetIteratorObject(Object *o); Heap::Object *newMapIteratorObject(Object *o); Heap::Object *newArrayIteratorObject(Object *o); + static Heap::ExecutionContext *qmlContext(Heap::ExecutionContext *ctx) + { + Heap::ExecutionContext *outer = ctx->outer; + + if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !outer) + return nullptr; + + while (outer && outer->type != Heap::ExecutionContext::Type_GlobalContext) { + ctx = outer; + outer = ctx->outer; + } + + Q_ASSERT(ctx); + if (ctx->type != Heap::ExecutionContext::Type_QmlContext) + return nullptr; + + return ctx; + } + Heap::QmlContext *qmlContext() const; QObject *qmlScopeObject() const; QQmlRefPointer<QQmlContextData> callingQmlContext() const; @@ -659,18 +670,23 @@ public: QQmlError catchExceptionAsQmlError(); // variant conversions - QVariant toVariant(const QV4::Value &value, int typeHint, bool createJSValueForObjects = true); + static QVariant toVariant( + const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols = true); + static QVariant toVariantLossy(const QV4::Value &value); QV4::ReturnedValue fromVariant(const QVariant &); + QV4::ReturnedValue fromVariant( + const QVariant &variant, Heap::Object *parent, int property, uint flags); - QVariantMap variantMapFromJS(const QV4::Object *o); + static QVariantMap variantMapFromJS(const QV4::Object *o); - static bool metaTypeFromJS(const Value &value, int type, void *data); + static bool metaTypeFromJS(const Value &value, QMetaType type, void *data); QV4::ReturnedValue metaTypeToJS(QMetaType type, const void *data); int maxJSStackSize() const; int maxGCStackSize() const; bool checkStackLimits(); + int safeForAllocLength(qint64 len64); bool canJIT(Function *f = nullptr) { @@ -678,8 +694,9 @@ public: if (!m_canAllocateExecutableMemory) return false; if (f) { - return !f->aotFunction && !f->isGenerator() - && f->interpreterCallCount >= jitCallCountThreshold; + return f->kind != Function::AotCompiled + && !f->isGenerator() + && f->interpreterCallCount >= s_jitCallCountThreshold; } return true; #else @@ -694,6 +711,7 @@ public: void createQtObject(); void freezeObject(const QV4::Value &value); + void lockObject(const QV4::Value &value); // Return the list of illegal id names (the names of the properties on the global object) const QSet<QString> &illegalNames() const; @@ -723,7 +741,7 @@ public: void setExtensionData(int, Deletable *); Deletable *extensionData(int index) const { - if (index < m_extensionData.count()) + if (index < m_extensionData.size()) return m_extensionData[index]; else return nullptr; @@ -735,24 +753,117 @@ public: QQmlRefPointer<ExecutableCompilationUnit> compileModule( const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp); - mutable QMutex moduleMutex; - QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules; - void injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit); - QQmlRefPointer<ExecutableCompilationUnit> moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const; - QQmlRefPointer<ExecutableCompilationUnit> loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr); + QQmlRefPointer<ExecutableCompilationUnit> compilationUnitForUrl(const QUrl &url) const; + + QQmlRefPointer<ExecutableCompilationUnit> executableCompilationUnit( + QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit); + + QQmlRefPointer<ExecutableCompilationUnit> insertCompilationUnit( + QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit); - bool diskCacheEnabled() const; + QMultiHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> compilationUnits() const + { + return m_compilationUnits; + } + void clearCompilationUnits() { m_compilationUnits.clear(); } + void trimCompilationUnits(); + + QV4::Value *registerNativeModule(const QUrl &url, const QV4::Value &module); + + struct Module { + QQmlRefPointer<ExecutableCompilationUnit> compiled; + + // We can pass a raw value pointer here, but nowhere else. See below. + Value *native = nullptr; + }; - ReturnedValue callInContext(Function *function, QObject *self, - QQmlRefPointer<QQmlContextData> ctxtdata, int argc, void **args, QMetaType *types); + Module moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const; + Module loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr); + + DiskCacheOptions diskCacheOptions() const; + + void callInContext(QV4::Function *function, QObject *self, QV4::ExecutionContext *ctxt, + int argc, void **args, QMetaType *types); + QV4::ReturnedValue callInContext(QV4::Function *function, QObject *self, + QV4::ExecutionContext *ctxt, int argc, const QV4::Value *argv); + + QV4::ReturnedValue fromData( + QMetaType type, const void *ptr, + Heap::Object *parent = nullptr, int property = -1, uint flags = 0); + + + static void setMaxCallDepth(int maxCallDepth) { s_maxCallDepth = maxCallDepth; } + static int maxCallDepth() { return s_maxCallDepth; } + + template<typename Value> + static QJSPrimitiveValue createPrimitive(const Value &v) + { + if (v->isUndefined()) + return QJSPrimitiveValue(QJSPrimitiveUndefined()); + if (v->isNull()) + return QJSPrimitiveValue(QJSPrimitiveNull()); + if (v->isBoolean()) + return QJSPrimitiveValue(v->toBoolean()); + if (v->isInteger()) + return QJSPrimitiveValue(v->integerValue()); + if (v->isDouble()) + return QJSPrimitiveValue(v->doubleValue()); + bool ok; + const QString result = v->toQString(&ok); + return ok ? QJSPrimitiveValue(result) : QJSPrimitiveValue(QJSPrimitiveUndefined()); + } private: + template<int Frames> + friend struct ExecutionEngineCallDepthRecorder; + + static void initializeStaticMembers(); + + bool inStack(const void *current) const + { +#if Q_STACK_GROWTH_DIRECTION > 0 + return current < cppStackLimit && current >= cppStackBase; +#else + return current > cppStackLimit && current <= cppStackBase; +#endif + } + + bool hasCppStackOverflow() + { + if (s_maxCallDepth >= 0) + return callDepth >= s_maxCallDepth; + + if (inStack(currentStackPointer())) + return false; + + // Double check the stack limits on failure. + // We may have moved to a different thread. + const StackProperties stack = stackProperties(); + cppStackBase = stack.base; + cppStackLimit = stack.softLimit; + return !inStack(currentStackPointer()); + } + + bool hasJsStackOverflow() const + { + return jsStackTop > jsStackLimit; + } + + bool hasStackOverflow() + { + return hasJsStackOverflow() || hasCppStackOverflow(); + } + + static int s_maxCallDepth; + static int s_jitCallCountThreshold; + static int s_maxJSStackSize; + static int s_maxGCStackSize; + #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; QScopedPointer<QV4::Profiling::Profiler> m_profiler; #endif QSet<QString> m_illegalNames; - int jitCallCountThreshold; // used by generated Promise objects to handle 'then' events QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler; @@ -772,24 +883,46 @@ private: QVector<Deletable *> m_extensionData; - int m_maxJSStackSize = 4 * 1024 * 1024; - int m_maxGCStackSize = 2 * 1024 * 1024; + QMultiHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> m_compilationUnits; + + // QV4::PersistentValue would be preferred, but using QHash will create copies, + // and QV4::PersistentValue doesn't like creating copies. + // Instead, we allocate a raw pointer using the same manual memory management + // technique in QV4::PersistentValue. + QHash<QUrl, Value *> nativeModules; }; -#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ +#define CHECK_STACK_LIMITS(v4) \ + if (v4->checkStackLimits()) \ + return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); +template<int Frames = 1> struct ExecutionEngineCallDepthRecorder { ExecutionEngine *ee; - ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; } - ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } + ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) + { + if (ExecutionEngine::s_maxCallDepth >= 0) + ee->callDepth += Frames; + } + + ~ExecutionEngineCallDepthRecorder() + { + if (ExecutionEngine::s_maxCallDepth >= 0) + ee->callDepth -= Frames; + } + + bool hasOverflow() const + { + return ee->hasCppStackOverflow(); + } }; inline bool ExecutionEngine::checkStackLimits() { - if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) { + if (Q_UNLIKELY(hasStackOverflow())) { throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); return true; } @@ -797,6 +930,8 @@ inline bool ExecutionEngine::checkStackLimits() return false; } +Q_DECLARE_OPERATORS_FOR_FLAGS(ExecutionEngine::DiskCacheOptions); + } // namespace QV4 QT_END_NAMESPACE |