1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQMLENGINE_P_H
5#define QQMLENGINE_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qqmlengine.h"
19
20#include <private/qfieldlist_p.h>
21#include <private/qintrusivelist_p.h>
22#include <private/qjsengine_p.h>
23#include <private/qjsvalue_p.h>
24#include <private/qpodvector_p.h>
25#include <private/qqmldirparser_p.h>
26#include <private/qqmlimport_p.h>
27#include <private/qqmlmetatype_p.h>
28#include <private/qqmlnotifier_p.h>
29#include <private/qqmlproperty_p.h>
30#include <private/qqmltypeloader_p.h>
31#include <private/qqmlvaluetype_p.h>
32#include <private/qrecyclepool_p.h>
33#include <private/qv4engine_p.h>
34
35#include <QtQml/qqml.h>
36#include <QtQml/qqmlcontext.h>
37
38#include <QtCore/qlist.h>
39#include <QtCore/qmetaobject.h>
40#include <QtCore/qmutex.h>
41#include <QtCore/qpair.h>
42#include <QtCore/qproperty.h>
43#include <QtCore/qstack.h>
44#include <QtCore/qstring.h>
45#include <QtCore/qthread.h>
46
47#include <atomic>
48
49QT_BEGIN_NAMESPACE
50
51class QNetworkAccessManager;
52class QQmlDelayedError;
53class QQmlIncubator;
54class QQmlMetaObject;
55class QQmlNetworkAccessManagerFactory;
56class QQmlObjectCreator;
57class QQmlProfiler;
58class QQmlPropertyCapture;
59
60struct QObjectForeign {
61 Q_GADGET
62 QML_FOREIGN(QObject)
63 QML_NAMED_ELEMENT(QtObject)
64 QML_ADDED_IN_VERSION(2, 0)
65 Q_CLASSINFO("QML.OmitFromQmlTypes", "true")
66};
67
68// This needs to be declared here so that the pool for it can live in QQmlEnginePrivate.
69// The inline method definitions are in qqmljavascriptexpression_p.h
70class QQmlJavaScriptExpressionGuard : public QQmlNotifierEndpoint
71{
72public:
73 inline QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *);
74
75 static inline QQmlJavaScriptExpressionGuard *New(QQmlJavaScriptExpression *e,
76 QQmlEngine *engine);
77 inline void Delete();
78
79 QQmlJavaScriptExpression *expression;
80 QQmlJavaScriptExpressionGuard *next;
81};
82
83struct QPropertyChangeTrigger : QPropertyObserver {
84 Q_DISABLE_COPY_MOVE(QPropertyChangeTrigger)
85
86 QPropertyChangeTrigger(QQmlJavaScriptExpression *expression)
87 : QPropertyObserver(&QPropertyChangeTrigger::trigger)
88 , m_expression(expression)
89 {
90 }
91
92 QPointer<QObject> target;
93 QQmlJavaScriptExpression *m_expression;
94 int propertyIndex = 0;
95 static void trigger(QPropertyObserver *, QUntypedPropertyData *);
96
97 QMetaProperty property() const;
98};
99
100struct TriggerList : QPropertyChangeTrigger {
101 TriggerList(QQmlJavaScriptExpression *expression)
102 : QPropertyChangeTrigger(expression)
103 {}
104 TriggerList *next = nullptr;
105};
106
107class Q_QML_PRIVATE_EXPORT QQmlEnginePrivate : public QJSEnginePrivate
108{
109 Q_DECLARE_PUBLIC(QQmlEngine)
110public:
111 explicit QQmlEnginePrivate(QQmlEngine *q) : importDatabase(q), typeLoader(q) {}
112 ~QQmlEnginePrivate() override;
113
114 void init();
115 // No mutex protecting baseModulesUninitialized, because use outside QQmlEngine
116 // is just qmlClearTypeRegistrations (which can't be called while an engine exists)
117 static bool baseModulesUninitialized;
118
119 QQmlPropertyCapture *propertyCapture = nullptr;
120
121 QRecyclePool<QQmlJavaScriptExpressionGuard> jsExpressionGuardPool;
122 QRecyclePool<TriggerList> qPropertyTriggerPool;
123
124 QQmlContext *rootContext = nullptr;
125 Q_OBJECT_BINDABLE_PROPERTY(QQmlEnginePrivate, QString, translationLanguage);
126
127#if !QT_CONFIG(qml_debug)
128 static const quintptr profiler = 0;
129#else
130 QQmlProfiler *profiler = nullptr;
131#endif
132
133 bool outputWarningsToMsgLog = true;
134
135 // Bindings that have had errors during startup
136 QQmlDelayedError *erroredBindings = nullptr;
137 int inProgressCreations = 0;
138
139 QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); }
140
141#if QT_CONFIG(qml_worker_script)
142 QThread *workerScriptEngine = nullptr;
143#endif
144
145 QUrl baseUrl;
146
147 QQmlObjectCreator *activeObjectCreator = nullptr;
148#if QT_CONFIG(qml_network)
149 QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const;
150 QNetworkAccessManager *getNetworkAccessManager() const;
151 mutable QNetworkAccessManager *networkAccessManager = nullptr;
152 mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory = nullptr;
153#endif
154 mutable QRecursiveMutex imageProviderMutex;
155 QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders;
156 QSharedPointer<QQmlImageProviderBase> imageProvider(const QString &providerId) const;
157
158 QList<QQmlAbstractUrlInterceptor *> urlInterceptors;
159
160 int scarceResourcesRefCount = 0;
161 void referenceScarceResources();
162 void dereferenceScarceResources();
163
164 QQmlImportDatabase importDatabase;
165 QQmlTypeLoader typeLoader;
166
167 QString offlineStoragePath;
168
169 // Unfortunate workaround to avoid a circular dependency between
170 // qqmlengine_p.h and qqmlincubator_p.h
171 struct Incubator {
172 QIntrusiveListNode next;
173 };
174 QIntrusiveList<Incubator, &Incubator::next> incubatorList;
175 unsigned int incubatorCount = 0;
176 QQmlIncubationController *incubationController = nullptr;
177 void incubate(QQmlIncubator &, const QQmlRefPointer<QQmlContextData> &);
178
179 // These methods may be called from any thread
180 QString offlineStorageDatabaseDirectory() const;
181
182 bool isTypeLoaded(const QUrl &url) const;
183 bool isScriptLoaded(const QUrl &url) const;
184
185 template <typename T>
186 T singletonInstance(const QQmlType &type);
187
188 void sendQuit();
189 void sendExit(int retCode = 0);
190 void warning(const QQmlError &);
191 void warning(const QList<QQmlError> &);
192 static void warning(QQmlEngine *, const QQmlError &);
193 static void warning(QQmlEngine *, const QList<QQmlError> &);
194 static void warning(QQmlEnginePrivate *, const QQmlError &);
195 static void warning(QQmlEnginePrivate *, const QList<QQmlError> &);
196
197 inline static QV4::ExecutionEngine *getV4Engine(QQmlEngine *e);
198 inline static QQmlEnginePrivate *get(QQmlEngine *e);
199 inline static const QQmlEnginePrivate *get(const QQmlEngine *e);
200 inline static QQmlEnginePrivate *get(QQmlContext *c);
201 inline static QQmlEnginePrivate *get(const QQmlRefPointer<QQmlContextData> &c);
202 inline static QQmlEngine *get(QQmlEnginePrivate *p);
203 inline static QQmlEnginePrivate *get(QV4::ExecutionEngine *e);
204
205 static QList<QQmlError> qmlErrorFromDiagnostics(const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages);
206
207 static bool designerMode();
208 static void activateDesignerMode();
209
210 static std::atomic<bool> qml_debugging_enabled;
211
212 mutable QMutex networkAccessManagerMutex;
213
214 QQmlGadgetPtrWrapper *valueTypeInstance(QMetaType type)
215 {
216 int typeIndex = type.id();
217 auto it = cachedValueTypeInstances.find(key: typeIndex);
218 if (it != cachedValueTypeInstances.end())
219 return *it;
220
221 if (QQmlValueType *valueType = QQmlMetaType::valueType(metaType: type)) {
222 QQmlGadgetPtrWrapper *instance = new QQmlGadgetPtrWrapper(valueType);
223 cachedValueTypeInstances.insert(key: typeIndex, value: instance);
224 return instance;
225 }
226
227 return nullptr;
228 }
229
230 void executeRuntimeFunction(const QUrl &url, qsizetype functionIndex, QObject *thisObject,
231 int argc = 0, void **args = nullptr, QMetaType *types = nullptr);
232 void executeRuntimeFunction(const QV4::ExecutableCompilationUnit *unit, qsizetype functionIndex,
233 QObject *thisObject, int argc = 0, void **args = nullptr,
234 QMetaType *types = nullptr);
235 QV4::ExecutableCompilationUnit *compilationUnitFromUrl(const QUrl &url);
236 QQmlRefPointer<QQmlContextData>
237 createInternalContext(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
238 const QQmlRefPointer<QQmlContextData> &parentContext,
239 int subComponentIndex, bool isComponentRoot);
240 static void setInternalContext(QObject *This, const QQmlRefPointer<QQmlContextData> &context,
241 QQmlContextData::QmlObjectKind kind)
242 {
243 Q_ASSERT(This);
244 QQmlData *ddata = QQmlData::get(object: This, /*create*/ create: true);
245 // NB: copied from QQmlObjectCreator::createInstance()
246 //
247 // the if-statement logic to determine the kind is:
248 // if (static_cast<quint32>(index) == 0 || ddata->rootObjectInCreation || isInlineComponent)
249 // then QQmlContextData::DocumentRoot. here, we pass this through qmltc
250 context->installContext(ddata, kind);
251 Q_ASSERT(qmlEngine(This));
252 }
253
254private:
255 class SingletonInstances : private QHash<QQmlType, QJSValue>
256 {
257 public:
258 void convertAndInsert(QV4::ExecutionEngine *engine, const QQmlType &type, QJSValue *value)
259 {
260 QJSValuePrivate::manageStringOnV4Heap(e: engine, jsval: value);
261 insert(key: type, value: *value);
262 }
263
264 void clear()
265 {
266 const auto canDelete = [](QObject *instance, const auto &type) -> bool {
267 if (!instance)
268 return false;
269
270 if (!type.singletonInstanceInfo()->url.isEmpty())
271 return true;
272
273 const auto *ddata = QQmlData::get(object: instance, create: false);
274 return !(ddata && ddata->indestructible && ddata->explicitIndestructibleSet);
275 };
276
277 for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
278 auto *instance = it.value().toQObject();
279 if (canDelete(instance, it.key()))
280 QQmlData::markAsDeleted(instance);
281 }
282
283 for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
284 QObject *instance = it.value().toQObject();
285
286 if (canDelete(instance, it.key()))
287 delete instance;
288 }
289
290 QHash<QQmlType, QJSValue>::clear();
291 }
292
293 using QHash<QQmlType, QJSValue>::value;
294 using QHash<QQmlType, QJSValue>::take;
295 };
296
297 SingletonInstances singletonInstances;
298 QHash<int, QQmlGadgetPtrWrapper *> cachedValueTypeInstances;
299
300 static bool s_designerMode;
301
302 void cleanupScarceResources();
303};
304
305/*
306 This function should be called prior to evaluation of any js expression,
307 so that scarce resources are not freed prematurely (eg, if there is a
308 nested javascript expression).
309 */
310inline void QQmlEnginePrivate::referenceScarceResources()
311{
312 scarceResourcesRefCount += 1;
313}
314
315/*
316 This function should be called after evaluation of the js expression is
317 complete, and so the scarce resources may be freed safely.
318 */
319inline void QQmlEnginePrivate::dereferenceScarceResources()
320{
321 Q_ASSERT(scarceResourcesRefCount > 0);
322 scarceResourcesRefCount -= 1;
323
324 // if the refcount is zero, then evaluation of the "top level"
325 // expression must have completed. We can safely release the
326 // scarce resources.
327 if (Q_LIKELY(scarceResourcesRefCount == 0)) {
328 QV4::ExecutionEngine *engine = v4engine();
329 if (Q_UNLIKELY(!engine->scarceResources.isEmpty())) {
330 cleanupScarceResources();
331 }
332 }
333}
334
335QV4::ExecutionEngine *QQmlEnginePrivate::getV4Engine(QQmlEngine *e)
336{
337 Q_ASSERT(e);
338
339 return e->handle();
340}
341
342QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlEngine *e)
343{
344 Q_ASSERT(e);
345
346 return e->d_func();
347}
348
349const QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlEngine *e)
350{
351 Q_ASSERT(e);
352
353 return e ? e->d_func() : nullptr;
354}
355
356template<typename Context>
357QQmlEnginePrivate *contextEngine(const Context &context)
358{
359 if (!context)
360 return nullptr;
361 if (QQmlEngine *engine = context->engine())
362 return QQmlEnginePrivate::get(e: engine);
363 return nullptr;
364}
365
366QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlContext *c)
367{
368 return contextEngine(context: c);
369}
370
371QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlRefPointer<QQmlContextData> &c)
372{
373 return contextEngine(context: c);
374}
375
376QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p)
377{
378 Q_ASSERT(p);
379
380 return p->q_func();
381}
382
383QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e)
384{
385 QQmlEngine *qmlEngine = e->qmlEngine();
386 if (!qmlEngine)
387 return nullptr;
388 return get(e: qmlEngine);
389}
390
391template<>
392Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type);
393
394template<typename T>
395T QQmlEnginePrivate::singletonInstance(const QQmlType &type) {
396 return qobject_cast<T>(singletonInstance<QJSValue>(type).toQObject());
397}
398
399struct LoadHelper final : QQmlTypeLoader::Blob
400{
401 LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri);
402
403 struct ResolveTypeResult
404 {
405 enum Status { NoSuchModule, ModuleFound } status;
406 QQmlType type;
407 };
408
409 ResolveTypeResult resolveType(QAnyStringView typeName);
410
411protected:
412 void dataReceived(const SourceCodeData &) final { Q_UNREACHABLE(); }
413 void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *) final { Q_UNREACHABLE(); }
414
415private:
416 bool couldFindModule() const;
417 QString m_uri;
418};
419
420
421QT_END_NAMESPACE
422
423#endif // QQMLENGINE_P_H
424

source code of qtdeclarative/src/qml/qml/qqmlengine_p.h