// 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 QQMLENGINE_P_H #define QQMLENGINE_P_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 "qqmlengine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QNetworkAccessManager; class QQmlDelayedError; class QQmlIncubator; class QQmlMetaObject; class QQmlNetworkAccessManagerFactory; class QQmlObjectCreator; class QQmlProfiler; class QQmlPropertyCapture; // This needs to be declared here so that the pool for it can live in QQmlEnginePrivate. // The inline method definitions are in qqmljavascriptexpression_p.h class QQmlJavaScriptExpressionGuard : public QQmlNotifierEndpoint { public: inline QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *); static inline QQmlJavaScriptExpressionGuard *New(QQmlJavaScriptExpression *e, QQmlEngine *engine); inline void Delete(); QQmlJavaScriptExpression *expression; QQmlJavaScriptExpressionGuard *next; }; struct QPropertyChangeTrigger : QPropertyObserver { Q_DISABLE_COPY_MOVE(QPropertyChangeTrigger) QPropertyChangeTrigger(QQmlJavaScriptExpression *expression) : QPropertyObserver(&QPropertyChangeTrigger::trigger) , m_expression(expression) { } QPointer target; QQmlJavaScriptExpression *m_expression; int propertyIndex = 0; static void trigger(QPropertyObserver *, QUntypedPropertyData *); QMetaProperty property() const; }; struct TriggerList : QPropertyChangeTrigger { TriggerList(QQmlJavaScriptExpression *expression) : QPropertyChangeTrigger(expression) {} TriggerList *next = nullptr; }; class Q_QML_EXPORT QQmlEnginePrivate : public QJSEnginePrivate { Q_DECLARE_PUBLIC(QQmlEngine) public: explicit QQmlEnginePrivate(QQmlEngine *q) : importDatabase(q), typeLoader(q) {} ~QQmlEnginePrivate() override; void init(); // No mutex protecting baseModulesUninitialized, because use outside QQmlEngine // is just qmlClearTypeRegistrations (which can't be called while an engine exists) static bool baseModulesUninitialized; QQmlPropertyCapture *propertyCapture = nullptr; QRecyclePool jsExpressionGuardPool; QRecyclePool qPropertyTriggerPool; QQmlContext *rootContext = nullptr; Q_OBJECT_BINDABLE_PROPERTY(QQmlEnginePrivate, QString, translationLanguage); #if !QT_CONFIG(qml_debug) static const quintptr profiler = 0; #else QQmlProfiler *profiler = nullptr; #endif bool outputWarningsToMsgLog = true; // Bindings that have had errors during startup QQmlDelayedError *erroredBindings = nullptr; int inProgressCreations = 0; QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); } #if QT_CONFIG(qml_worker_script) QThread *workerScriptEngine = nullptr; #endif QUrl baseUrl; QQmlObjectCreator *activeObjectCreator = nullptr; #if QT_CONFIG(qml_network) QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; QNetworkAccessManager *getNetworkAccessManager() const; mutable QNetworkAccessManager *networkAccessManager = nullptr; mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory = nullptr; #endif mutable QRecursiveMutex imageProviderMutex; QHash > imageProviders; QSharedPointer imageProvider(const QString &providerId) const; QList urlInterceptors; int scarceResourcesRefCount = 0; void referenceScarceResources(); void dereferenceScarceResources(); QQmlImportDatabase importDatabase; QQmlTypeLoader typeLoader; QString offlineStoragePath; // Unfortunate workaround to avoid a circular dependency between // qqmlengine_p.h and qqmlincubator_p.h struct Incubator { QIntrusiveListNode next; }; QIntrusiveList incubatorList; unsigned int incubatorCount = 0; QQmlIncubationController *incubationController = nullptr; void incubate(QQmlIncubator &, const QQmlRefPointer &); // These methods may be called from any thread QString offlineStorageDatabaseDirectory() const; bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; template T singletonInstance(const QQmlType &type); void sendQuit(); void sendExit(int retCode = 0); void warning(const QQmlError &); void warning(const QList &); static void warning(QQmlEngine *, const QQmlError &); static void warning(QQmlEngine *, const QList &); static void warning(QQmlEnginePrivate *, const QQmlError &); static void warning(QQmlEnginePrivate *, const QList &); inline static QV4::ExecutionEngine *getV4Engine(QQmlEngine *e); inline static QQmlEnginePrivate *get(QQmlEngine *e); inline static const QQmlEnginePrivate *get(const QQmlEngine *e); inline static QQmlEnginePrivate *get(QQmlContext *c); inline static QQmlEnginePrivate *get(const QQmlRefPointer &c); inline static QQmlEngine *get(QQmlEnginePrivate *p); inline static QQmlEnginePrivate *get(QV4::ExecutionEngine *e); static QList qmlErrorFromDiagnostics(const QString &fileName, const QList &diagnosticMessages); static bool designerMode(); static void activateDesignerMode(); static std::atomic qml_debugging_enabled; mutable QMutex networkAccessManagerMutex; QQmlGadgetPtrWrapper *valueTypeInstance(QMetaType type) { int typeIndex = type.id(); auto it = cachedValueTypeInstances.constFind(typeIndex); if (it != cachedValueTypeInstances.cend()) return *it; if (QQmlValueType *valueType = QQmlMetaType::valueType(type)) { QQmlGadgetPtrWrapper *instance = new QQmlGadgetPtrWrapper(valueType); cachedValueTypeInstances.insert(typeIndex, instance); return instance; } return nullptr; } void executeRuntimeFunction(const QUrl &url, qsizetype functionIndex, QObject *thisObject, int argc = 0, void **args = nullptr, QMetaType *types = nullptr); void executeRuntimeFunction(const QV4::ExecutableCompilationUnit *unit, qsizetype functionIndex, QObject *thisObject, int argc = 0, void **args = nullptr, QMetaType *types = nullptr); QV4::ExecutableCompilationUnit *compilationUnitFromUrl(const QUrl &url); QQmlRefPointer createInternalContext(const QQmlRefPointer &unit, const QQmlRefPointer &parentContext, int subComponentIndex, bool isComponentRoot); static void setInternalContext(QObject *This, const QQmlRefPointer &context, QQmlContextData::QmlObjectKind kind) { Q_ASSERT(This); QQmlData *ddata = QQmlData::get(This, /*create*/ true); // NB: copied from QQmlObjectCreator::createInstance() // // the if-statement logic to determine the kind is: // if (static_cast(index) == 0 || ddata->rootObjectInCreation || isInlineComponent) // then QQmlContextData::DocumentRoot. here, we pass this through qmltc context->installContext(ddata, kind); Q_ASSERT(qmlEngine(This)); } private: class SingletonInstances : private QHash { public: void convertAndInsert( QV4::ExecutionEngine *engine, const QQmlType::SingletonInstanceInfo::ConstPtr &type, QJSValue *value) { QJSValuePrivate::manageStringOnV4Heap(engine, value); insert(type, *value); } void clear() { const auto canDelete = [](QObject *instance, const auto &siinfo) -> bool { if (!instance) return false; if (!siinfo->url.isEmpty()) return true; const auto *ddata = QQmlData::get(instance, false); return !(ddata && ddata->indestructible && ddata->explicitIndestructibleSet); }; for (auto it = constBegin(), end = constEnd(); it != end; ++it) { auto *instance = it.value().toQObject(); if (canDelete(instance, it.key())) QQmlData::markAsDeleted(instance); } for (auto it = constBegin(), end = constEnd(); it != end; ++it) { QObject *instance = it.value().toQObject(); if (canDelete(instance, it.key())) delete instance; } QHash::clear(); } using QHash::value; using QHash::take; }; SingletonInstances singletonInstances; QHash cachedValueTypeInstances; static bool s_designerMode; void cleanupScarceResources(); }; /* This function should be called prior to evaluation of any js expression, so that scarce resources are not freed prematurely (eg, if there is a nested javascript expression). */ inline void QQmlEnginePrivate::referenceScarceResources() { scarceResourcesRefCount += 1; } /* This function should be called after evaluation of the js expression is complete, and so the scarce resources may be freed safely. */ inline void QQmlEnginePrivate::dereferenceScarceResources() { Q_ASSERT(scarceResourcesRefCount > 0); scarceResourcesRefCount -= 1; // if the refcount is zero, then evaluation of the "top level" // expression must have completed. We can safely release the // scarce resources. if (Q_LIKELY(scarceResourcesRefCount == 0)) { QV4::ExecutionEngine *engine = v4engine(); if (Q_UNLIKELY(!engine->scarceResources.isEmpty())) { cleanupScarceResources(); } } } QV4::ExecutionEngine *QQmlEnginePrivate::getV4Engine(QQmlEngine *e) { Q_ASSERT(e); return e->handle(); } QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlEngine *e) { Q_ASSERT(e); return e->d_func(); } const QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlEngine *e) { Q_ASSERT(e); return e ? e->d_func() : nullptr; } template QQmlEnginePrivate *contextEngine(const Context &context) { if (!context) return nullptr; if (QQmlEngine *engine = context->engine()) return QQmlEnginePrivate::get(engine); return nullptr; } QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlContext *c) { return contextEngine(c); } QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlRefPointer &c) { return contextEngine(c); } QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p) { Q_ASSERT(p); return p->q_func(); } QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) { QQmlEngine *qmlEngine = e->qmlEngine(); if (!qmlEngine) return nullptr; return get(qmlEngine); } template<> Q_QML_EXPORT QJSValue QQmlEnginePrivate::singletonInstance(const QQmlType &type); template T QQmlEnginePrivate::singletonInstance(const QQmlType &type) { return qobject_cast(singletonInstance(type).toQObject()); } struct LoadHelper final : QQmlTypeLoader::Blob { LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri); struct ResolveTypeResult { enum Status { NoSuchModule, ModuleFound } status; QQmlType type; }; ResolveTypeResult resolveType(QAnyStringView typeName); protected: void dataReceived(const SourceCodeData &) final { Q_UNREACHABLE(); } void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *) final { Q_UNREACHABLE(); } private: bool couldFindModule() const; QString m_uri; }; QT_END_NAMESPACE #endif // QQMLENGINE_P_H