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 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | class QNetworkAccessManager; |
52 | class QQmlDelayedError; |
53 | class QQmlIncubator; |
54 | class QQmlMetaObject; |
55 | class QQmlNetworkAccessManagerFactory; |
56 | class QQmlObjectCreator; |
57 | class QQmlProfiler; |
58 | class QQmlPropertyCapture; |
59 | |
60 | struct 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 |
70 | class QQmlJavaScriptExpressionGuard : public QQmlNotifierEndpoint |
71 | { |
72 | public: |
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 | |
83 | struct 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 | |
100 | struct TriggerList : QPropertyChangeTrigger { |
101 | TriggerList(QQmlJavaScriptExpression *expression) |
102 | : QPropertyChangeTrigger(expression) |
103 | {} |
104 | TriggerList *next = nullptr; |
105 | }; |
106 | |
107 | class Q_QML_PRIVATE_EXPORT QQmlEnginePrivate : public QJSEnginePrivate |
108 | { |
109 | Q_DECLARE_PUBLIC(QQmlEngine) |
110 | public: |
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 | |
254 | private: |
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 | */ |
310 | inline 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 | */ |
319 | inline 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 | |
335 | QV4::ExecutionEngine *QQmlEnginePrivate::getV4Engine(QQmlEngine *e) |
336 | { |
337 | Q_ASSERT(e); |
338 | |
339 | return e->handle(); |
340 | } |
341 | |
342 | QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlEngine *e) |
343 | { |
344 | Q_ASSERT(e); |
345 | |
346 | return e->d_func(); |
347 | } |
348 | |
349 | const QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlEngine *e) |
350 | { |
351 | Q_ASSERT(e); |
352 | |
353 | return e ? e->d_func() : nullptr; |
354 | } |
355 | |
356 | template<typename Context> |
357 | QQmlEnginePrivate *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 | |
366 | QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlContext *c) |
367 | { |
368 | return contextEngine(context: c); |
369 | } |
370 | |
371 | QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlRefPointer<QQmlContextData> &c) |
372 | { |
373 | return contextEngine(context: c); |
374 | } |
375 | |
376 | QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p) |
377 | { |
378 | Q_ASSERT(p); |
379 | |
380 | return p->q_func(); |
381 | } |
382 | |
383 | QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) |
384 | { |
385 | QQmlEngine *qmlEngine = e->qmlEngine(); |
386 | if (!qmlEngine) |
387 | return nullptr; |
388 | return get(e: qmlEngine); |
389 | } |
390 | |
391 | template<> |
392 | Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type); |
393 | |
394 | template<typename T> |
395 | T QQmlEnginePrivate::singletonInstance(const QQmlType &type) { |
396 | return qobject_cast<T>(singletonInstance<QJSValue>(type).toQObject()); |
397 | } |
398 | |
399 | struct 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 | |
411 | protected: |
412 | void dataReceived(const SourceCodeData &) final { Q_UNREACHABLE(); } |
413 | void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *) final { Q_UNREACHABLE(); } |
414 | |
415 | private: |
416 | bool couldFindModule() const; |
417 | QString m_uri; |
418 | }; |
419 | |
420 | |
421 | QT_END_NAMESPACE |
422 | |
423 | #endif // QQMLENGINE_P_H |
424 | |