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 | #include "qqmlengine_p.h" |
5 | #include "qqmlengine.h" |
6 | |
7 | #include "qqmlcontext_p.h" |
8 | #include "qqml.h" |
9 | #include "qqmlcontext.h" |
10 | #include "qqmlscriptstring.h" |
11 | #include "qqmlglobal_p.h" |
12 | #include "qqmlnotifier_p.h" |
13 | #include "qqmlincubator.h" |
14 | #include "qqmlabstracturlinterceptor.h" |
15 | |
16 | #include <private/qqmldirparser_p.h> |
17 | #include <private/qqmlboundsignal_p.h> |
18 | #include <private/qqmljsdiagnosticmessage_p.h> |
19 | #include <private/qqmltype_p_p.h> |
20 | #include <private/qqmlpluginimporter_p.h> |
21 | #include <QtCore/qstandardpaths.h> |
22 | #include <QtCore/qmetaobject.h> |
23 | #include <QDebug> |
24 | #include <QtCore/qcoreapplication.h> |
25 | #include <QtCore/qcryptographichash.h> |
26 | #include <QtCore/qdir.h> |
27 | #include <QtCore/qmutex.h> |
28 | #include <QtCore/qthread.h> |
29 | #include <private/qthread_p.h> |
30 | #include <private/qqmlscriptdata_p.h> |
31 | #include <QtQml/private/qqmlcomponentattached_p.h> |
32 | #include <QtQml/private/qqmlsourcecoordinate_p.h> |
33 | #include <QtQml/private/qqmlcomponent_p.h> |
34 | |
35 | #if QT_CONFIG(qml_network) |
36 | #include "qqmlnetworkaccessmanagerfactory.h" |
37 | #include <QNetworkAccessManager> |
38 | #endif |
39 | |
40 | #include <private/qobject_p.h> |
41 | #include <private/qmetaobject_p.h> |
42 | #if QT_CONFIG(qml_locale) |
43 | #include <private/qqmllocale_p.h> |
44 | #endif |
45 | #include <private/qqmlbind_p.h> |
46 | #include <private/qqmlconnections_p.h> |
47 | #if QT_CONFIG(qml_animation) |
48 | #include <private/qqmltimer_p.h> |
49 | #endif |
50 | #include <private/qqmlplatform_p.h> |
51 | #include <private/qqmlloggingcategory_p.h> |
52 | #include <private/qv4sequenceobject_p.h> |
53 | |
54 | #ifdef Q_OS_WIN // for %APPDATA% |
55 | # include <qt_windows.h> |
56 | # include <shlobj.h> |
57 | # include <qlibrary.h> |
58 | # ifndef CSIDL_APPDATA |
59 | # define CSIDL_APPDATA 0x001a // <username>\Application Data |
60 | # endif |
61 | #endif // Q_OS_WIN |
62 | |
63 | QT_BEGIN_NAMESPACE |
64 | |
65 | /*! |
66 | \qmltype QtObject |
67 | \instantiates QObject |
68 | \inqmlmodule QtQml |
69 | \ingroup qml-utility-elements |
70 | \brief A basic QML type. |
71 | |
72 | The QtObject type is a non-visual element which contains only the |
73 | objectName property. |
74 | |
75 | It can be useful to create a QtObject if you need an extremely |
76 | lightweight type to enclose a set of custom properties: |
77 | |
78 | \snippet qml/qtobject.qml 0 |
79 | |
80 | It can also be useful for C++ integration, as it is just a plain |
81 | QObject. See the QObject documentation for further details. |
82 | */ |
83 | /*! |
84 | \qmlproperty string QtObject::objectName |
85 | This property holds the QObject::objectName for this specific object instance. |
86 | |
87 | This allows a C++ application to locate an item within a QML component |
88 | using the QObject::findChild() method. For example, the following C++ |
89 | application locates the child \l Rectangle item and dynamically changes its |
90 | \c color value: |
91 | |
92 | \qml |
93 | // MyRect.qml |
94 | |
95 | import QtQuick 2.0 |
96 | |
97 | Item { |
98 | width: 200; height: 200 |
99 | |
100 | Rectangle { |
101 | anchors.fill: parent |
102 | color: "red" |
103 | objectName: "myRect" |
104 | } |
105 | } |
106 | \endqml |
107 | |
108 | \code |
109 | // main.cpp |
110 | |
111 | QQuickView view; |
112 | view.setSource(QUrl::fromLocalFile("MyRect.qml")); |
113 | view.show(); |
114 | |
115 | QQuickItem *item = view.rootObject()->findChild<QQuickItem*>("myRect"); |
116 | if (item) |
117 | item->setProperty("color", QColor(Qt::yellow)); |
118 | \endcode |
119 | */ |
120 | |
121 | Q_CONSTINIT std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false}; |
122 | bool QQmlEnginePrivate::s_designerMode = false; |
123 | |
124 | bool QQmlEnginePrivate::designerMode() |
125 | { |
126 | return s_designerMode; |
127 | } |
128 | |
129 | void QQmlEnginePrivate::activateDesignerMode() |
130 | { |
131 | s_designerMode = true; |
132 | } |
133 | |
134 | |
135 | /*! |
136 | \class QQmlImageProviderBase |
137 | \brief The QQmlImageProviderBase class is used to register image providers in the QML engine. |
138 | \inmodule QtQml |
139 | |
140 | Image providers must be registered with the QML engine. The only information the QML |
141 | engine knows about image providers is the type of image data they provide. To use an |
142 | image provider to acquire image data, you must cast the QQmlImageProviderBase pointer |
143 | to a QQuickImageProvider pointer. |
144 | |
145 | \sa QQuickImageProvider, QQuickTextureFactory |
146 | */ |
147 | |
148 | /*! |
149 | \enum QQmlImageProviderBase::ImageType |
150 | |
151 | Defines the type of image supported by this image provider. |
152 | |
153 | \value Image The Image Provider provides QImage images. |
154 | The QQuickImageProvider::requestImage() method will be called for all image requests. |
155 | \value Pixmap The Image Provider provides QPixmap images. |
156 | The QQuickImageProvider::requestPixmap() method will be called for all image requests. |
157 | \value Texture The Image Provider provides QSGTextureProvider based images. |
158 | The QQuickImageProvider::requestTexture() method will be called for all image requests. |
159 | \value ImageResponse The Image provider provides QQuickTextureFactory based images. |
160 | Should only be used in QQuickAsyncImageProvider or its subclasses. |
161 | The QQuickAsyncImageProvider::requestImageResponse() method will be called for all image requests. |
162 | Since Qt 5.6 |
163 | \omitvalue Invalid |
164 | */ |
165 | |
166 | /*! |
167 | \enum QQmlImageProviderBase::Flag |
168 | |
169 | Defines specific requirements or features of this image provider. |
170 | |
171 | \value ForceAsynchronousImageLoading Ensures that image requests to the provider are |
172 | run in a separate thread, which allows the provider to spend as much time as needed |
173 | on producing the image without blocking the main thread. |
174 | */ |
175 | |
176 | /*! |
177 | \fn QQmlImageProviderBase::imageType() const |
178 | |
179 | Implement this method to return the image type supported by this image provider. |
180 | */ |
181 | |
182 | /*! |
183 | \fn QQmlImageProviderBase::flags() const |
184 | |
185 | Implement this to return the properties of this image provider. |
186 | */ |
187 | |
188 | /*! \internal */ |
189 | QQmlImageProviderBase::QQmlImageProviderBase() |
190 | { |
191 | } |
192 | |
193 | /*! \internal */ |
194 | QQmlImageProviderBase::~QQmlImageProviderBase() |
195 | { |
196 | } |
197 | |
198 | QQmlEnginePrivate::~QQmlEnginePrivate() |
199 | { |
200 | if (inProgressCreations) |
201 | qWarning() << QQmlEngine::tr(s: "There are still \"%1\" items in the process of being created at engine destruction." ).arg(a: inProgressCreations); |
202 | |
203 | if (incubationController) incubationController->d = nullptr; |
204 | incubationController = nullptr; |
205 | |
206 | QQmlMetaType::freeUnusedTypesAndCaches(); |
207 | |
208 | #if QT_CONFIG(qml_debug) |
209 | delete profiler; |
210 | #endif |
211 | qDeleteAll(c: cachedValueTypeInstances); |
212 | } |
213 | |
214 | void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) |
215 | { |
216 | QObjectPrivate *p = QObjectPrivate::get(o); |
217 | if (QQmlData *d = QQmlData::get(priv: p)) { |
218 | if (d->ownContext) { |
219 | for (QQmlRefPointer<QQmlContextData> lc = d->ownContext->linkedContext(); lc; |
220 | lc = lc->linkedContext()) { |
221 | lc->invalidate(); |
222 | if (lc->contextObject() == o) |
223 | lc->setContextObject(nullptr); |
224 | } |
225 | d->ownContext->invalidate(); |
226 | if (d->ownContext->contextObject() == o) |
227 | d->ownContext->setContextObject(nullptr); |
228 | d->ownContext.reset(); |
229 | d->context = nullptr; |
230 | } |
231 | |
232 | if (d->outerContext && d->outerContext->contextObject() == o) |
233 | d->outerContext->setContextObject(nullptr); |
234 | |
235 | if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) { |
236 | // This is somewhat dangerous because another thread might concurrently |
237 | // try to resolve the dynamic metaobject. In practice this will then |
238 | // lead to either the code path that still returns the interceptor |
239 | // metaobject or the code path that returns the string casted one. Both |
240 | // is fine if you cannot actually touch the object itself. Since the |
241 | // other thread is obviously not synchronized to this one, it can't. |
242 | // |
243 | // In particular we do this when delivering the frameSwapped() signal |
244 | // in QQuickWindow. The handler for frameSwapped() is written in a way |
245 | // that is thread safe as long as QQuickWindow's dtor hasn't finished. |
246 | // QQuickWindow's dtor does synchronize with the render thread, but it |
247 | // runs _after_ qdeclarativeelement_destructor. |
248 | static_cast<QQmlInterceptorMetaObject *>(p->metaObject)->invalidate(); |
249 | d->hasVMEMetaObject = d->hasInterceptorMetaObject = false; |
250 | } |
251 | |
252 | // Mark this object as in the process of deletion to |
253 | // prevent it resolving in bindings |
254 | QQmlData::markAsDeleted(o); |
255 | } |
256 | } |
257 | |
258 | QQmlData::QQmlData(Ownership ownership) |
259 | : ownMemory(ownership == OwnsMemory), indestructible(true), explicitIndestructibleSet(false), |
260 | hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), |
261 | hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false), |
262 | bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr), |
263 | bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr), |
264 | lineNumber(0), columnNumber(0), jsEngineId(0), |
265 | guards(nullptr), extendedData(nullptr) |
266 | { |
267 | memset(s: bindingBitsValue, c: 0, n: sizeof(bindingBitsValue)); |
268 | init(); |
269 | } |
270 | |
271 | QQmlData::~QQmlData() |
272 | { |
273 | } |
274 | |
275 | void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o) |
276 | { |
277 | QQmlData *ddata = static_cast<QQmlData *>(d); |
278 | ddata->destroyed(o); |
279 | } |
280 | |
281 | |
282 | class QQmlThreadNotifierProxyObject : public QObject |
283 | { |
284 | public: |
285 | QPointer<QObject> target; |
286 | |
287 | int qt_metacall(QMetaObject::Call, int methodIndex, void **a) override { |
288 | if (!target) |
289 | return -1; |
290 | |
291 | QMetaMethod method = target->metaObject()->method(index: methodIndex); |
292 | Q_ASSERT(method.methodType() == QMetaMethod::Signal); |
293 | int signalIndex = QMetaObjectPrivate::signalIndex(m: method); |
294 | QQmlData *ddata = QQmlData::get(object: target, create: false); |
295 | QQmlNotifierEndpoint *ep = ddata->notify(index: signalIndex); |
296 | if (ep) QQmlNotifier::emitNotify(ep, a); |
297 | |
298 | delete this; |
299 | |
300 | return -1; |
301 | } |
302 | }; |
303 | |
304 | void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int index, void **a) |
305 | { |
306 | QQmlData *ddata = QQmlData::get(object, create: false); |
307 | if (!ddata) return; // Probably being deleted |
308 | |
309 | // In general, QML only supports QObject's that live on the same thread as the QQmlEngine |
310 | // that they're exposed to. However, to make writing "worker objects" that calculate data |
311 | // in a separate thread easier, QML allows a QObject that lives in the same thread as the |
312 | // QQmlEngine to emit signals from a different thread. These signals are then automatically |
313 | // marshalled back onto the QObject's thread and handled by QML from there. This is tested |
314 | // by the qqmlecmascript::threadSignal() autotest. |
315 | |
316 | // Relaxed semantics here. If we're on a different thread we might schedule a useless event, |
317 | // but that should be rare. |
318 | if (!ddata->notifyList.loadRelaxed()) |
319 | return; |
320 | |
321 | auto objectThreadData = QObjectPrivate::get(o: object)->threadData.loadRelaxed(); |
322 | if (QThread::currentThreadId() != objectThreadData->threadId.loadRelaxed()) { |
323 | if (!objectThreadData->thread.loadAcquire()) |
324 | return; |
325 | |
326 | QMetaMethod m = QMetaObjectPrivate::signal(m: object->metaObject(), signal_index: index); |
327 | QList<QByteArray> parameterTypes = m.parameterTypes(); |
328 | |
329 | auto ev = std::make_unique<QMetaCallEvent>(args: m.methodIndex(), args: 0, args: nullptr, |
330 | args&: object, args&: index, |
331 | args: parameterTypes.size() + 1); |
332 | |
333 | void **args = ev->args(); |
334 | QMetaType *types = ev->types(); |
335 | |
336 | for (int ii = 0; ii < parameterTypes.size(); ++ii) { |
337 | const QByteArray &typeName = parameterTypes.at(i: ii); |
338 | if (typeName.endsWith(c: '*')) |
339 | types[ii + 1] = QMetaType(QMetaType::VoidStar); |
340 | else |
341 | types[ii + 1] = QMetaType::fromName(name: typeName); |
342 | |
343 | if (!types[ii + 1].isValid()) { |
344 | qWarning(msg: "QObject::connect: Cannot queue arguments of type '%s'\n" |
345 | "(Make sure '%s' is registered using qRegisterMetaType().)" , |
346 | typeName.constData(), typeName.constData()); |
347 | return; |
348 | } |
349 | |
350 | args[ii + 1] = types[ii + 1].create(copy: a[ii + 1]); |
351 | } |
352 | |
353 | QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject; |
354 | mpo->target = object; |
355 | mpo->moveToThread(thread: objectThreadData->thread.loadAcquire()); |
356 | QCoreApplication::postEvent(receiver: mpo, event: ev.release()); |
357 | |
358 | } else { |
359 | QQmlNotifierEndpoint *ep = ddata->notify(index); |
360 | if (ep) QQmlNotifier::emitNotify(ep, a); |
361 | } |
362 | } |
363 | |
364 | int QQmlData::receivers(QAbstractDeclarativeData *d, const QObject *, int index) |
365 | { |
366 | QQmlData *ddata = static_cast<QQmlData *>(d); |
367 | return ddata->endpointCount(index); |
368 | } |
369 | |
370 | bool QQmlData::isSignalConnected(QAbstractDeclarativeData *d, const QObject *, int index) |
371 | { |
372 | QQmlData *ddata = static_cast<QQmlData *>(d); |
373 | return ddata->signalHasEndpoint(index); |
374 | } |
375 | |
376 | int QQmlData::endpointCount(int index) |
377 | { |
378 | int count = 0; |
379 | QQmlNotifierEndpoint *ep = notify(index); |
380 | if (!ep) |
381 | return count; |
382 | ++count; |
383 | while (ep->next) { |
384 | ++count; |
385 | ep = ep->next; |
386 | } |
387 | return count; |
388 | } |
389 | |
390 | void QQmlData::markAsDeleted(QObject *o) |
391 | { |
392 | QVarLengthArray<QObject *> workStack; |
393 | workStack.push_back(t: o); |
394 | while (!workStack.isEmpty()) { |
395 | auto currentObject = workStack.last(); |
396 | workStack.pop_back(); |
397 | QQmlData::setQueuedForDeletion(currentObject); |
398 | auto currentObjectPriv = QObjectPrivate::get(o: currentObject); |
399 | for (QObject *child: std::as_const(t&: currentObjectPriv->children)) |
400 | workStack.push_back(t: child); |
401 | } |
402 | } |
403 | |
404 | void QQmlData::setQueuedForDeletion(QObject *object) |
405 | { |
406 | if (object) { |
407 | if (QQmlData *ddata = QQmlData::get(object)) { |
408 | if (ddata->ownContext) { |
409 | Q_ASSERT(ddata->ownContext.data() == ddata->context); |
410 | ddata->context->emitDestruction(); |
411 | if (ddata->ownContext->contextObject() == object) |
412 | ddata->ownContext->setContextObject(nullptr); |
413 | ddata->ownContext.reset(); |
414 | ddata->context = nullptr; |
415 | } |
416 | ddata->isQueuedForDeletion = true; |
417 | |
418 | // Disconnect the notifiers now - during object destruction this would be too late, |
419 | // since the disconnect call wouldn't be able to call disconnectNotify(), as it isn't |
420 | // possible to get the metaobject anymore. |
421 | // Also, there is no point in evaluating bindings in order to set properties on |
422 | // half-deleted objects. |
423 | ddata->disconnectNotifiers(doDelete: DeleteNotifyList::No); |
424 | } |
425 | } |
426 | } |
427 | |
428 | void QQmlData::flushPendingBinding(int coreIndex) |
429 | { |
430 | clearPendingBindingBit(coreIndex); |
431 | |
432 | // Find the binding |
433 | QQmlAbstractBinding *b = bindings; |
434 | while (b && (b->targetPropertyIndex().coreIndex() != coreIndex || |
435 | b->targetPropertyIndex().hasValueTypeIndex())) |
436 | b = b->nextBinding(); |
437 | |
438 | if (b && b->targetPropertyIndex().coreIndex() == coreIndex && |
439 | !b->targetPropertyIndex().hasValueTypeIndex()) |
440 | b->setEnabled(e: true, f: QQmlPropertyData::BypassInterceptor | |
441 | QQmlPropertyData::DontRemoveBinding); |
442 | } |
443 | |
444 | QQmlData::DeferredData::DeferredData() = default; |
445 | QQmlData::DeferredData::~DeferredData() = default; |
446 | |
447 | template<> |
448 | int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName) |
449 | { |
450 | QQmlPrivate::RegisterType type = { |
451 | .structVersion: QQmlPrivate::RegisterType::CurrentVersion, |
452 | .typeId: QMetaType(), |
453 | .listId: QMetaType(), |
454 | .objectSize: 0, .create: nullptr, .userdata: nullptr, |
455 | .noCreationReason: QString(), |
456 | .createValueType: nullptr, |
457 | .uri: uri, |
458 | .version: QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor), |
459 | .elementName: qmlName, |
460 | .metaObject: nullptr, |
461 | .attachedPropertiesFunction: nullptr, |
462 | .attachedPropertiesMetaObject: nullptr, |
463 | .parserStatusCast: -1, |
464 | .valueSourceCast: -1, |
465 | .valueInterceptorCast: -1, |
466 | .extensionObjectCreate: nullptr, |
467 | .extensionMetaObject: nullptr, |
468 | .customParser: nullptr, |
469 | .revision: QTypeRevision::zero(), |
470 | .finalizerCast: -1, |
471 | .creationMethod: QQmlPrivate::ValueTypeCreationMethod::None, |
472 | }; |
473 | |
474 | return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); |
475 | } |
476 | |
477 | bool QQmlEnginePrivate::baseModulesUninitialized = true; |
478 | void QQmlEnginePrivate::init() |
479 | { |
480 | Q_Q(QQmlEngine); |
481 | |
482 | if (baseModulesUninitialized) { |
483 | // Named builtins |
484 | qmlRegisterType<void>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "void" ); |
485 | |
486 | const int varId = qmlRegisterType<QVariant>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "var" ); |
487 | QQmlMetaType::registerTypeAlias(typeId: varId, name: QLatin1String("variant" )); |
488 | qmlRegisterAnonymousSequentialContainer<QList<QVariant>>(uri: "QML" , versionMajor: 1); |
489 | |
490 | qmlRegisterType<QObject>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "QtObject" ); |
491 | qmlRegisterType<QQmlComponent>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "Component" ); |
492 | |
493 | qmlRegisterType<int>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "int" ); |
494 | qmlRegisterAnonymousSequentialContainer<QList<int>>(uri: "QML" , versionMajor: 1); |
495 | |
496 | const int realId = qmlRegisterType<double>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "real" ); |
497 | QQmlMetaType::registerTypeAlias(typeId: realId, name: QLatin1String("double" )); |
498 | qmlRegisterAnonymousSequentialContainer<QList<double>>(uri: "QML" , versionMajor: 1); |
499 | |
500 | qmlRegisterType<QString>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "string" ); |
501 | qmlRegisterAnonymousSequentialContainer<QList<QString>>(uri: "QML" , versionMajor: 1); |
502 | |
503 | qmlRegisterType<bool>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "bool" ); |
504 | qmlRegisterAnonymousSequentialContainer<QList<bool>>(uri: "QML" , versionMajor: 1); |
505 | |
506 | qmlRegisterType<QDateTime>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "date" ); |
507 | qmlRegisterAnonymousSequentialContainer<QList<QDateTime>>(uri: "QML" , versionMajor: 1); |
508 | |
509 | qmlRegisterType<QUrl>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "url" ); |
510 | qmlRegisterAnonymousSequentialContainer<QList<QUrl>>(uri: "QML" , versionMajor: 1); |
511 | |
512 | #if QT_CONFIG(regularexpression) |
513 | qmlRegisterType<QRegularExpression>(uri: "QML" , versionMajor: 1, versionMinor: 0, qmlName: "regexp" ); |
514 | qmlRegisterAnonymousSequentialContainer<QList<QRegularExpression>>(uri: "QML" , versionMajor: 1); |
515 | #else |
516 | qmlRegisterType<void>("QML" , 1, 0, "regexp" ); |
517 | #endif |
518 | |
519 | // Anonymous builtins |
520 | qmlRegisterAnonymousType<std::nullptr_t>(uri: "QML" , versionMajor: 1); |
521 | qmlRegisterAnonymousType<QVariantMap>(uri: "QML" , versionMajor: 1); |
522 | |
523 | qmlRegisterAnonymousType<QJSValue>(uri: "QML" , versionMajor: 1); |
524 | qmlRegisterAnonymousSequentialContainer<QList<QJSValue>>(uri: "QML" , versionMajor: 1); |
525 | |
526 | qmlRegisterAnonymousType<qint8>(uri: "QML" , versionMajor: 1); |
527 | qmlRegisterAnonymousSequentialContainer<QList<qint8>>(uri: "QML" , versionMajor: 1); |
528 | |
529 | qmlRegisterAnonymousType<quint8>(uri: "QML" , versionMajor: 1); |
530 | qmlRegisterAnonymousSequentialContainer<QList<quint8>>(uri: "QML" , versionMajor: 1); |
531 | |
532 | qmlRegisterAnonymousType<short>(uri: "QML" , versionMajor: 1); |
533 | qmlRegisterAnonymousSequentialContainer<QList<short>>(uri: "QML" , versionMajor: 1); |
534 | |
535 | qmlRegisterAnonymousType<ushort>(uri: "QML" , versionMajor: 1); |
536 | qmlRegisterAnonymousSequentialContainer<QList<ushort>>(uri: "QML" , versionMajor: 1); |
537 | |
538 | qmlRegisterAnonymousType<uint>(uri: "QML" , versionMajor: 1); |
539 | qmlRegisterAnonymousSequentialContainer<QList<uint>>(uri: "QML" , versionMajor: 1); |
540 | |
541 | qmlRegisterAnonymousType<qlonglong>(uri: "QML" , versionMajor: 1); |
542 | qmlRegisterAnonymousSequentialContainer<QList<qlonglong>>(uri: "QML" , versionMajor: 1); |
543 | |
544 | qmlRegisterAnonymousType<qulonglong>(uri: "QML" , versionMajor: 1); |
545 | qmlRegisterAnonymousSequentialContainer<QList<qulonglong>>(uri: "QML" , versionMajor: 1); |
546 | |
547 | qmlRegisterAnonymousType<float>(uri: "QML" , versionMajor: 1); |
548 | qmlRegisterAnonymousSequentialContainer<QList<float>>(uri: "QML" , versionMajor: 1); |
549 | |
550 | qmlRegisterAnonymousType<QChar>(uri: "QML" , versionMajor: 1); |
551 | qmlRegisterAnonymousSequentialContainer<QList<QChar>>(uri: "QML" , versionMajor: 1); |
552 | |
553 | qmlRegisterAnonymousType<QDate>(uri: "QML" , versionMajor: 1); |
554 | qmlRegisterAnonymousSequentialContainer<QList<QDate>>(uri: "QML" , versionMajor: 1); |
555 | |
556 | qmlRegisterAnonymousType<QTime>(uri: "QML" , versionMajor: 1); |
557 | qmlRegisterAnonymousSequentialContainer<QList<QTime>>(uri: "QML" , versionMajor: 1); |
558 | |
559 | qmlRegisterAnonymousType<QByteArray>(uri: "QML" , versionMajor: 1); |
560 | qmlRegisterAnonymousSequentialContainer<QList<QByteArray>>(uri: "QML" , versionMajor: 1); |
561 | |
562 | // No need to specifically register those. |
563 | static_assert(std::is_same_v<QStringList, QList<QString>>); |
564 | static_assert(std::is_same_v<QVariantList, QList<QVariant>>); |
565 | |
566 | qRegisterMetaType<QQmlScriptString>(); |
567 | qRegisterMetaType<QQmlComponent::Status>(); |
568 | qRegisterMetaType<QList<QObject*> >(); |
569 | qRegisterMetaType<QQmlBinding*>(); |
570 | |
571 | QQmlData::init(); |
572 | baseModulesUninitialized = false; |
573 | } |
574 | |
575 | q->handle()->setQmlEngine(q); |
576 | |
577 | rootContext = new QQmlContext(q,true); |
578 | } |
579 | |
580 | /*! |
581 | \class QQmlEngine |
582 | \since 5.0 |
583 | \inmodule QtQml |
584 | \brief The QQmlEngine class provides an environment for instantiating QML components. |
585 | |
586 | Each QML component is instantiated in a QQmlContext. |
587 | QQmlContext's are essential for passing data to QML |
588 | components. In QML, contexts are arranged hierarchically and this |
589 | hierarchy is managed by the QQmlEngine. |
590 | |
591 | Prior to creating any QML components, an application must have |
592 | created a QQmlEngine to gain access to a QML context. The |
593 | following example shows how to create a simple Text item. |
594 | |
595 | \code |
596 | QQmlEngine engine; |
597 | QQmlComponent component(&engine); |
598 | component.setData("import QtQuick 2.0\nText { text: \"Hello world!\" }", QUrl()); |
599 | QQuickItem *item = qobject_cast<QQuickItem *>(component.create()); |
600 | |
601 | //add item to view, etc |
602 | ... |
603 | \endcode |
604 | |
605 | In this case, the Text item will be created in the engine's |
606 | \l {QQmlEngine::rootContext()}{root context}. |
607 | |
608 | \sa QQmlComponent, QQmlContext, {QML Global Object} |
609 | */ |
610 | |
611 | /*! |
612 | Create a new QQmlEngine with the given \a parent. |
613 | */ |
614 | QQmlEngine::QQmlEngine(QObject *parent) |
615 | : QJSEngine(*new QQmlEnginePrivate(this), parent) |
616 | { |
617 | Q_D(QQmlEngine); |
618 | d->init(); |
619 | QJSEnginePrivate::addToDebugServer(q: this); |
620 | } |
621 | |
622 | /*! |
623 | * \internal |
624 | */ |
625 | QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent) |
626 | : QJSEngine(dd, parent) |
627 | { |
628 | Q_D(QQmlEngine); |
629 | d->init(); |
630 | } |
631 | |
632 | /*! |
633 | Destroys the QQmlEngine. |
634 | |
635 | Any QQmlContext's created on this engine will be |
636 | invalidated, but not destroyed (unless they are parented to the |
637 | QQmlEngine object). |
638 | |
639 | See ~QJSEngine() for details on cleaning up the JS engine. |
640 | */ |
641 | QQmlEngine::~QQmlEngine() |
642 | { |
643 | Q_D(QQmlEngine); |
644 | QJSEnginePrivate::removeFromDebugServer(q: this); |
645 | |
646 | // Emit onDestruction signals for the root context before |
647 | // we destroy the contexts, engine, Singleton Types etc. that |
648 | // may be required to handle the destruction signal. |
649 | QQmlContextPrivate::get(context: rootContext())->emitDestruction(); |
650 | |
651 | // clean up all singleton type instances which we own. |
652 | // we do this here and not in the private dtor since otherwise a crash can |
653 | // occur (if we are the QObject parent of the QObject singleton instance) |
654 | // XXX TODO: performance -- store list of singleton types separately? |
655 | d->singletonInstances.clear(); |
656 | |
657 | delete d->rootContext; |
658 | d->rootContext = nullptr; |
659 | |
660 | d->typeLoader.invalidate(); |
661 | } |
662 | |
663 | /*! \fn void QQmlEngine::quit() |
664 | This signal is emitted when the QML loaded by the engine would like to quit. |
665 | |
666 | \sa exit() |
667 | */ |
668 | |
669 | /*! \fn void QQmlEngine::exit(int retCode) |
670 | This signal is emitted when the QML loaded by the engine would like to exit |
671 | from the event loop with the specified return code \a retCode. |
672 | |
673 | \since 5.8 |
674 | \sa quit() |
675 | */ |
676 | |
677 | |
678 | /*! \fn void QQmlEngine::warnings(const QList<QQmlError> &warnings) |
679 | This signal is emitted when \a warnings messages are generated by QML. |
680 | */ |
681 | |
682 | /*! |
683 | Clears the engine's internal component cache. |
684 | |
685 | This function causes the property metadata of all components previously |
686 | loaded by the engine to be destroyed. All previously loaded components and |
687 | the property bindings for all extant objects created from those components will |
688 | cease to function. |
689 | |
690 | This function returns the engine to a state where it does not contain any loaded |
691 | component data. This may be useful in order to reload a smaller subset of the |
692 | previous component set, or to load a new version of a previously loaded component. |
693 | |
694 | Once the component cache has been cleared, components must be loaded before |
695 | any new objects can be created. |
696 | |
697 | \note Any existing objects created from QML components retain their types, |
698 | even if you clear the component cache. This includes singleton objects. If you |
699 | create more objects from the same QML code after clearing the cache, the new |
700 | objects will be of different types than the old ones. Assigning such a new |
701 | object to a property of its declared type belonging to an object created |
702 | before clearing the cache won't work. |
703 | |
704 | As a general rule of thumb, make sure that no objects created from QML |
705 | components are alive when you clear the component cache. |
706 | |
707 | \sa trimComponentCache(), clearSingletons() |
708 | */ |
709 | void QQmlEngine::clearComponentCache() |
710 | { |
711 | Q_D(QQmlEngine); |
712 | d->typeLoader.lock(); |
713 | d->typeLoader.clearCache(); |
714 | d->typeLoader.unlock(); |
715 | } |
716 | |
717 | /*! |
718 | Trims the engine's internal component cache. |
719 | |
720 | This function causes the property metadata of any loaded components which are |
721 | not currently in use to be destroyed. |
722 | |
723 | A component is considered to be in use if there are any extant instances of |
724 | the component itself, any instances of other components that use the component, |
725 | or any objects instantiated by any of those components. |
726 | |
727 | \sa clearComponentCache() |
728 | */ |
729 | void QQmlEngine::trimComponentCache() |
730 | { |
731 | Q_D(QQmlEngine); |
732 | d->typeLoader.trimCache(); |
733 | } |
734 | |
735 | /*! |
736 | Clears all singletons the engine owns. |
737 | |
738 | This function drops all singleton instances, deleting any QObjects owned by |
739 | the engine among them. This is useful to make sure that no QML-created objects |
740 | are left before calling clearComponentCache(). |
741 | |
742 | QML properties holding QObject-based singleton instances become null if the |
743 | engine owns the singleton or retain their value if the engine doesn't own it. |
744 | The singletons are not automatically re-created by accessing existing |
745 | QML-created objects. Only when new components are instantiated, the singletons |
746 | are re-created. |
747 | |
748 | \sa clearComponentCache() |
749 | */ |
750 | void QQmlEngine::clearSingletons() |
751 | { |
752 | Q_D(QQmlEngine); |
753 | d->singletonInstances.clear(); |
754 | } |
755 | |
756 | /*! |
757 | Returns the engine's root context. |
758 | |
759 | The root context is automatically created by the QQmlEngine. |
760 | Data that should be available to all QML component instances |
761 | instantiated by the engine should be put in the root context. |
762 | |
763 | Additional data that should only be available to a subset of |
764 | component instances should be added to sub-contexts parented to the |
765 | root context. |
766 | */ |
767 | QQmlContext *QQmlEngine::rootContext() const |
768 | { |
769 | Q_D(const QQmlEngine); |
770 | return d->rootContext; |
771 | } |
772 | |
773 | #if QT_DEPRECATED_SINCE(6, 0) |
774 | /*! |
775 | \internal |
776 | \deprecated |
777 | This API is private for 5.1 |
778 | |
779 | Returns the last QQmlAbstractUrlInterceptor. It must not be modified outside |
780 | the GUI thread. |
781 | */ |
782 | QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const |
783 | { |
784 | Q_D(const QQmlEngine); |
785 | return d->urlInterceptors.last(); |
786 | } |
787 | #endif |
788 | |
789 | /*! |
790 | Adds a \a urlInterceptor to be used when resolving URLs in QML. |
791 | This also applies to URLs used for loading script files and QML types. |
792 | The URL interceptors should not be modifed while the engine is loading files, |
793 | or URL selection may be inconsistent. Multiple URL interceptors, when given, |
794 | will be called in the order they were added for each URL. |
795 | |
796 | QQmlEngine does not take ownership of the interceptor and won't delete it. |
797 | */ |
798 | void QQmlEngine::addUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor) |
799 | { |
800 | Q_D(QQmlEngine); |
801 | d->urlInterceptors.append(t: urlInterceptor); |
802 | } |
803 | |
804 | /*! |
805 | Remove a \a urlInterceptor that was previously added using |
806 | \l addUrlInterceptor. The URL interceptors should not be modifed while the |
807 | engine is loading files, or URL selection may be inconsistent. |
808 | |
809 | This does not delete the interceptor, but merely removes it from the engine. |
810 | You can re-use it on the same or a different engine afterwards. |
811 | */ |
812 | void QQmlEngine::removeUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor) |
813 | { |
814 | Q_D(QQmlEngine); |
815 | d->urlInterceptors.removeOne(t: urlInterceptor); |
816 | } |
817 | |
818 | /*! |
819 | Run the current URL interceptors on the given \a url of the given \a type and |
820 | return the result. |
821 | */ |
822 | QUrl QQmlEngine::interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataType type) const |
823 | { |
824 | Q_D(const QQmlEngine); |
825 | QUrl result = url; |
826 | for (QQmlAbstractUrlInterceptor *interceptor : d->urlInterceptors) |
827 | result = interceptor->intercept(path: result, type); |
828 | return result; |
829 | } |
830 | |
831 | /*! |
832 | Returns the list of currently active URL interceptors. |
833 | */ |
834 | QList<QQmlAbstractUrlInterceptor *> QQmlEngine::urlInterceptors() const |
835 | { |
836 | Q_D(const QQmlEngine); |
837 | return d->urlInterceptors; |
838 | } |
839 | |
840 | QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const |
841 | { |
842 | const QString providerIdLower = providerId.toLower(); |
843 | QMutexLocker locker(&imageProviderMutex); |
844 | return imageProviders.value(key: providerIdLower); |
845 | } |
846 | |
847 | #if QT_CONFIG(qml_network) |
848 | /*! |
849 | Sets the \a factory to use for creating QNetworkAccessManager(s). |
850 | |
851 | QNetworkAccessManager is used for all network access by QML. By |
852 | implementing a factory it is possible to create custom |
853 | QNetworkAccessManager with specialized caching, proxy and cookie |
854 | support. |
855 | |
856 | The factory must be set before executing the engine. |
857 | |
858 | \note QQmlEngine does not take ownership of the factory. |
859 | */ |
860 | void QQmlEngine::setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *factory) |
861 | { |
862 | Q_D(QQmlEngine); |
863 | QMutexLocker locker(&d->networkAccessManagerMutex); |
864 | d->networkAccessManagerFactory = factory; |
865 | } |
866 | |
867 | /*! |
868 | Returns the current QQmlNetworkAccessManagerFactory. |
869 | |
870 | \sa setNetworkAccessManagerFactory() |
871 | */ |
872 | QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const |
873 | { |
874 | Q_D(const QQmlEngine); |
875 | return d->networkAccessManagerFactory; |
876 | } |
877 | |
878 | QNetworkAccessManager *QQmlEnginePrivate::createNetworkAccessManager(QObject *parent) const |
879 | { |
880 | QMutexLocker locker(&networkAccessManagerMutex); |
881 | QNetworkAccessManager *nam; |
882 | if (networkAccessManagerFactory) { |
883 | nam = networkAccessManagerFactory->create(parent); |
884 | } else { |
885 | nam = new QNetworkAccessManager(parent); |
886 | } |
887 | |
888 | return nam; |
889 | } |
890 | |
891 | QNetworkAccessManager *QQmlEnginePrivate::getNetworkAccessManager() const |
892 | { |
893 | Q_Q(const QQmlEngine); |
894 | if (!networkAccessManager) |
895 | networkAccessManager = createNetworkAccessManager(parent: const_cast<QQmlEngine*>(q)); |
896 | return networkAccessManager; |
897 | } |
898 | |
899 | /*! |
900 | Returns a common QNetworkAccessManager which can be used by any QML |
901 | type instantiated by this engine. |
902 | |
903 | If a QQmlNetworkAccessManagerFactory has been set and a |
904 | QNetworkAccessManager has not yet been created, the |
905 | QQmlNetworkAccessManagerFactory will be used to create the |
906 | QNetworkAccessManager; otherwise the returned QNetworkAccessManager |
907 | will have no proxy or cache set. |
908 | |
909 | \sa setNetworkAccessManagerFactory() |
910 | */ |
911 | QNetworkAccessManager *QQmlEngine::networkAccessManager() const |
912 | { |
913 | Q_D(const QQmlEngine); |
914 | return d->getNetworkAccessManager(); |
915 | } |
916 | #endif // qml_network |
917 | |
918 | /*! |
919 | |
920 | Sets the \a provider to use for images requested via the \e |
921 | image: url scheme, with host \a providerId. The QQmlEngine |
922 | takes ownership of \a provider. |
923 | |
924 | Image providers enable support for pixmap and threaded image |
925 | requests. See the QQuickImageProvider documentation for details on |
926 | implementing and using image providers. |
927 | |
928 | All required image providers should be added to the engine before any |
929 | QML sources files are loaded. |
930 | |
931 | \sa removeImageProvider(), QQuickImageProvider, QQmlImageProviderBase |
932 | */ |
933 | void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBase *provider) |
934 | { |
935 | Q_D(QQmlEngine); |
936 | QString providerIdLower = providerId.toLower(); |
937 | QSharedPointer<QQmlImageProviderBase> sp(provider); |
938 | QMutexLocker locker(&d->imageProviderMutex); |
939 | d->imageProviders.insert(key: std::move(providerIdLower), value: std::move(sp)); |
940 | } |
941 | |
942 | /*! |
943 | Returns the image provider set for \a providerId if found; otherwise returns \nullptr. |
944 | |
945 | \sa QQuickImageProvider |
946 | */ |
947 | QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) const |
948 | { |
949 | Q_D(const QQmlEngine); |
950 | const QString providerIdLower = providerId.toLower(); |
951 | QMutexLocker locker(&d->imageProviderMutex); |
952 | return d->imageProviders.value(key: providerIdLower).data(); |
953 | } |
954 | |
955 | /*! |
956 | Removes the image provider for \a providerId. |
957 | |
958 | \sa addImageProvider(), QQuickImageProvider |
959 | */ |
960 | void QQmlEngine::removeImageProvider(const QString &providerId) |
961 | { |
962 | Q_D(QQmlEngine); |
963 | const QString providerIdLower = providerId.toLower(); |
964 | QMutexLocker locker(&d->imageProviderMutex); |
965 | d->imageProviders.take(key: providerIdLower); |
966 | } |
967 | |
968 | /*! |
969 | Return the base URL for this engine. The base URL is only used to |
970 | resolve components when a relative URL is passed to the |
971 | QQmlComponent constructor. |
972 | |
973 | If a base URL has not been explicitly set, this method returns the |
974 | application's current working directory. |
975 | |
976 | \sa setBaseUrl() |
977 | */ |
978 | QUrl QQmlEngine::baseUrl() const |
979 | { |
980 | Q_D(const QQmlEngine); |
981 | if (d->baseUrl.isEmpty()) { |
982 | const QString currentPath = QDir::currentPath(); |
983 | const QString rootPath = QDir::rootPath(); |
984 | return QUrl::fromLocalFile(localfile: (currentPath == rootPath) ? rootPath : (currentPath + QDir::separator())); |
985 | } else { |
986 | return d->baseUrl; |
987 | } |
988 | } |
989 | |
990 | /*! |
991 | Set the base URL for this engine to \a url. |
992 | |
993 | \sa baseUrl() |
994 | */ |
995 | void QQmlEngine::setBaseUrl(const QUrl &url) |
996 | { |
997 | Q_D(QQmlEngine); |
998 | d->baseUrl = url; |
999 | } |
1000 | |
1001 | /*! |
1002 | Returns true if warning messages will be output to stderr in addition |
1003 | to being emitted by the warnings() signal, otherwise false. |
1004 | |
1005 | The default value is true. |
1006 | */ |
1007 | bool QQmlEngine::outputWarningsToStandardError() const |
1008 | { |
1009 | Q_D(const QQmlEngine); |
1010 | return d->outputWarningsToMsgLog; |
1011 | } |
1012 | |
1013 | /*! |
1014 | Set whether warning messages will be output to stderr to \a enabled. |
1015 | |
1016 | If \a enabled is true, any warning messages generated by QML will be |
1017 | output to stderr and emitted by the warnings() signal. If \a enabled |
1018 | is false, only the warnings() signal will be emitted. This allows |
1019 | applications to handle warning output themselves. |
1020 | |
1021 | The default value is true. |
1022 | */ |
1023 | void QQmlEngine::setOutputWarningsToStandardError(bool enabled) |
1024 | { |
1025 | Q_D(QQmlEngine); |
1026 | d->outputWarningsToMsgLog = enabled; |
1027 | } |
1028 | |
1029 | |
1030 | /*! |
1031 | \since 6.6 |
1032 | If this method is called inside of a function that is part of |
1033 | a binding in QML, the binding will be treated as a translation binding. |
1034 | |
1035 | \code |
1036 | class I18nAwareClass : public QObject { |
1037 | |
1038 | //... |
1039 | |
1040 | QString text() const |
1041 | { |
1042 | if (auto engine = qmlEngine(this)) |
1043 | engine->markCurrentFunctionAsTranslationBinding(); |
1044 | return tr("Hello, world!"); |
1045 | } |
1046 | }; |
1047 | \endcode |
1048 | |
1049 | \note This function is mostly useful if you wish to provide your |
1050 | own alternative to the qsTr function. To ensure that properties |
1051 | exposed from C++ classes are updated on language changes, it is |
1052 | instead recommended to react to \c LanguageChange events. That |
1053 | is a more general mechanism which also works when the class is |
1054 | used in a non-QML context, and has slightly less overhead. However, |
1055 | using \c markCurrentFunctionAsTranslationBinding can be acceptable |
1056 | when the class is already closely tied to the QML engine. |
1057 | For more details, see \l {Prepare for Dynamic Language Changes} |
1058 | |
1059 | \sa QQmlEngine::retranslate |
1060 | */ |
1061 | void QQmlEngine::markCurrentFunctionAsTranslationBinding() |
1062 | { |
1063 | Q_D(QQmlEngine); |
1064 | if (auto propertyCapture = d->propertyCapture) |
1065 | propertyCapture->captureTranslation(); |
1066 | } |
1067 | |
1068 | /*! |
1069 | \internal |
1070 | |
1071 | Capture the given property as part of a binding. |
1072 | */ |
1073 | void QQmlEngine::captureProperty(QObject *object, const QMetaProperty &property) const |
1074 | { |
1075 | Q_D(const QQmlEngine); |
1076 | if (d->propertyCapture && !property.isConstant()) { |
1077 | d->propertyCapture->captureProperty( |
1078 | object, property.propertyIndex(), |
1079 | QMetaObjectPrivate::signalIndex(m: property.notifySignal())); |
1080 | } |
1081 | } |
1082 | |
1083 | /*! |
1084 | \qmlproperty string Qt::uiLanguage |
1085 | \since 5.15 |
1086 | |
1087 | The uiLanguage holds the name of the language to be used for user interface |
1088 | string translations. It is exposed in C++ as QQmlEngine::uiLanguage property. |
1089 | |
1090 | You can set the value freely and use it in bindings. It is recommended to set it |
1091 | after installing translators in your application. By convention, an empty string |
1092 | means no translation from the language used in the source code is intended to occur. |
1093 | |
1094 | If you're using QQmlApplicationEngine and the value changes, QQmlEngine::retranslate() |
1095 | will be called. |
1096 | */ |
1097 | |
1098 | /*! |
1099 | \fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId) |
1100 | |
1101 | Returns the instance of a singleton type that was registered under \a qmlTypeId. |
1102 | |
1103 | The template argument \e T may be either QJSValue or a pointer to a QObject-derived |
1104 | type and depends on how the singleton was registered. If no instance of \e T has been |
1105 | created yet, it is created now. If \a qmlTypeId does not represent a valid singleton |
1106 | type, either a default constructed QJSValue or a \c nullptr is returned. |
1107 | |
1108 | QObject* example: |
1109 | |
1110 | \snippet code/src_qml_qqmlengine.cpp 0 |
1111 | \codeline |
1112 | \snippet code/src_qml_qqmlengine.cpp 1 |
1113 | \codeline |
1114 | \snippet code/src_qml_qqmlengine.cpp 2 |
1115 | |
1116 | QJSValue example: |
1117 | |
1118 | \snippet code/src_qml_qqmlengine.cpp 3 |
1119 | \codeline |
1120 | \snippet code/src_qml_qqmlengine.cpp 4 |
1121 | |
1122 | It is recommended to store the QML type id, e.g. as a static member in the |
1123 | singleton class. The lookup via qmlTypeId() is costly. |
1124 | |
1125 | \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId() |
1126 | \since 5.12 |
1127 | */ |
1128 | template<> |
1129 | QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId) |
1130 | { |
1131 | Q_D(QQmlEngine); |
1132 | QQmlType type = QQmlMetaType::qmlTypeById(qmlTypeId); |
1133 | |
1134 | if (!type.isValid() || !type.isSingleton()) |
1135 | return QJSValue(); |
1136 | |
1137 | return d->singletonInstance<QJSValue>(type); |
1138 | } |
1139 | |
1140 | |
1141 | /*! |
1142 | \fn template<typename T> T QQmlEngine::singletonInstance(QAnyStringView uri, QAnyStringView typeName) |
1143 | |
1144 | \overload |
1145 | Returns the instance of a singleton type named \a typeName from the module specified by \a uri. |
1146 | |
1147 | This method can be used as an alternative to calling qmlTypeId followed by the id based overload of |
1148 | singletonInstance. This is convenient when one only needs to do a one time setup of a |
1149 | singleton; if repeated access to the singleton is required, caching its typeId will allow |
1150 | faster subsequent access via the |
1151 | \l {QQmlEngine::singletonInstance(int qmlTypeId)}{type-id based overload}. |
1152 | |
1153 | The template argument \e T may be either QJSValue or a pointer to a QObject-derived |
1154 | type and depends on how the singleton was registered. If no instance of \e T has been |
1155 | created yet, it is created now. If \a typeName does not represent a valid singleton |
1156 | type, either a default constructed QJSValue or a \c nullptr is returned. |
1157 | |
1158 | \snippet code/src_qml_qqmlengine.cpp 5 |
1159 | |
1160 | \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId() |
1161 | \since 6.5 |
1162 | */ |
1163 | template<> |
1164 | QJSValue QQmlEngine::singletonInstance<QJSValue>(QAnyStringView uri, QAnyStringView typeName) |
1165 | { |
1166 | Q_D(QQmlEngine); |
1167 | |
1168 | auto loadHelper = QQml::makeRefPointer<LoadHelper>(args: &d->typeLoader, args&: uri); |
1169 | |
1170 | auto [moduleStatus, type] = loadHelper->resolveType(typeName); |
1171 | |
1172 | if (moduleStatus == LoadHelper::ResolveTypeResult::NoSuchModule) |
1173 | return {}; |
1174 | if (!type.isValid()) |
1175 | return {}; |
1176 | if (!type.isSingleton()) |
1177 | return {}; |
1178 | |
1179 | return d->singletonInstance<QJSValue>(type); |
1180 | } |
1181 | |
1182 | /*! |
1183 | Refreshes all binding expressions that use strings marked for translation. |
1184 | |
1185 | Call this function after you have installed a new translator with |
1186 | QCoreApplication::installTranslator, to ensure that your user-interface |
1187 | shows up-to-date translations. |
1188 | |
1189 | \since 5.10 |
1190 | */ |
1191 | void QQmlEngine::retranslate() |
1192 | { |
1193 | Q_D(QQmlEngine); |
1194 | d->translationLanguage.notify(); |
1195 | } |
1196 | |
1197 | /*! |
1198 | Returns the QQmlContext for the \a object, or nullptr if no |
1199 | context has been set. |
1200 | |
1201 | When the QQmlEngine instantiates a QObject, an internal context is assigned |
1202 | to it automatically. Such internal contexts are read-only. You cannot set |
1203 | context properties on them. |
1204 | |
1205 | \sa qmlContext(), qmlEngine(), QQmlContext::setContextProperty() |
1206 | */ |
1207 | QQmlContext *QQmlEngine::contextForObject(const QObject *object) |
1208 | { |
1209 | if(!object) |
1210 | return nullptr; |
1211 | |
1212 | QQmlData *data = QQmlData::get(object); |
1213 | if (data && data->outerContext) |
1214 | return data->outerContext->asQQmlContext(); |
1215 | |
1216 | return nullptr; |
1217 | } |
1218 | |
1219 | /*! |
1220 | Sets the QQmlContext for the \a object to \a context. |
1221 | If the \a object already has a context, a warning is |
1222 | output, but the context is not changed. |
1223 | |
1224 | When the QQmlEngine instantiates a QObject, the context is |
1225 | set automatically. |
1226 | */ |
1227 | void QQmlEngine::setContextForObject(QObject *object, QQmlContext *context) |
1228 | { |
1229 | if (!object || !context) |
1230 | return; |
1231 | |
1232 | QQmlData *data = QQmlData::get(object, create: true); |
1233 | if (data->context) { |
1234 | qWarning(msg: "QQmlEngine::setContextForObject(): Object already has a QQmlContext" ); |
1235 | return; |
1236 | } |
1237 | |
1238 | QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context); |
1239 | Q_ASSERT(data->context == nullptr); |
1240 | data->context = contextData.data(); |
1241 | contextData->addOwnedObject(ownedObject: data); |
1242 | } |
1243 | |
1244 | /*! |
1245 | \reimp |
1246 | */ |
1247 | bool QQmlEngine::event(QEvent *e) |
1248 | { |
1249 | if (e->type() == QEvent::LanguageChange) { |
1250 | retranslate(); |
1251 | } |
1252 | |
1253 | return QJSEngine::event(event: e); |
1254 | } |
1255 | |
1256 | class QQmlDataExtended { |
1257 | public: |
1258 | QQmlDataExtended(); |
1259 | ~QQmlDataExtended(); |
1260 | |
1261 | QHash<QQmlAttachedPropertiesFunc, QObject *> attachedProperties; |
1262 | }; |
1263 | |
1264 | QQmlDataExtended::QQmlDataExtended() |
1265 | { |
1266 | } |
1267 | |
1268 | QQmlDataExtended::~QQmlDataExtended() |
1269 | { |
1270 | } |
1271 | |
1272 | void QQmlData::NotifyList::layout(QQmlNotifierEndpoint *endpoint) |
1273 | { |
1274 | // Add a temporary sentinel at beginning of list. This will be overwritten |
1275 | // when the end point is inserted into the notifies further down. |
1276 | endpoint->prev = nullptr; |
1277 | |
1278 | while (endpoint->next) { |
1279 | Q_ASSERT(reinterpret_cast<QQmlNotifierEndpoint *>(endpoint->next->prev) == endpoint); |
1280 | endpoint = endpoint->next; |
1281 | } |
1282 | |
1283 | while (endpoint) { |
1284 | QQmlNotifierEndpoint *ep = (QQmlNotifierEndpoint *) endpoint->prev; |
1285 | |
1286 | int index = endpoint->sourceSignal; |
1287 | index = qMin(a: index, b: 0xFFFF - 1); |
1288 | |
1289 | endpoint->next = notifies[index]; |
1290 | if (endpoint->next) endpoint->next->prev = &endpoint->next; |
1291 | endpoint->prev = ¬ifies[index]; |
1292 | notifies[index] = endpoint; |
1293 | |
1294 | endpoint = ep; |
1295 | } |
1296 | } |
1297 | |
1298 | void QQmlData::NotifyList::layout() |
1299 | { |
1300 | Q_ASSERT(maximumTodoIndex >= notifiesSize); |
1301 | |
1302 | if (todo) { |
1303 | QQmlNotifierEndpoint **old = notifies; |
1304 | const int reallocSize = (maximumTodoIndex + 1) * sizeof(QQmlNotifierEndpoint*); |
1305 | notifies = (QQmlNotifierEndpoint**)realloc(ptr: notifies, size: reallocSize); |
1306 | const int memsetSize = (maximumTodoIndex - notifiesSize + 1) * |
1307 | sizeof(QQmlNotifierEndpoint*); |
1308 | memset(s: notifies + notifiesSize, c: 0, n: memsetSize); |
1309 | |
1310 | if (notifies != old) { |
1311 | for (int ii = 0; ii < notifiesSize; ++ii) |
1312 | if (notifies[ii]) |
1313 | notifies[ii]->prev = ¬ifies[ii]; |
1314 | } |
1315 | |
1316 | notifiesSize = maximumTodoIndex + 1; |
1317 | |
1318 | layout(endpoint: todo); |
1319 | } |
1320 | |
1321 | maximumTodoIndex = 0; |
1322 | todo = nullptr; |
1323 | } |
1324 | |
1325 | void QQmlData::deferData( |
1326 | int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, |
1327 | const QQmlRefPointer<QQmlContextData> &context) |
1328 | { |
1329 | QQmlData::DeferredData *deferData = new QQmlData::DeferredData; |
1330 | deferData->deferredIdx = objectIndex; |
1331 | deferData->compilationUnit = compilationUnit; |
1332 | deferData->context = context; |
1333 | |
1334 | const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(index: objectIndex); |
1335 | const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(i: objectIndex); |
1336 | |
1337 | const QV4::CompiledData::Binding *binding = compiledObject->bindingTable(); |
1338 | for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) { |
1339 | const QQmlPropertyData *property = propertyData.at(i); |
1340 | if (binding->hasFlag(flag: QV4::CompiledData::Binding::IsDeferredBinding)) |
1341 | deferData->bindings.insert(key: property ? property->coreIndex() : -1, value: binding); |
1342 | } |
1343 | |
1344 | deferredData.append(t: deferData); |
1345 | } |
1346 | |
1347 | void QQmlData::releaseDeferredData() |
1348 | { |
1349 | auto it = deferredData.begin(); |
1350 | while (it != deferredData.end()) { |
1351 | DeferredData *deferData = *it; |
1352 | if (deferData->bindings.isEmpty()) { |
1353 | delete deferData; |
1354 | it = deferredData.erase(pos: it); |
1355 | } else { |
1356 | ++it; |
1357 | } |
1358 | } |
1359 | } |
1360 | |
1361 | void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint) |
1362 | { |
1363 | // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. |
1364 | |
1365 | NotifyList *list = notifyList.loadRelaxed(); |
1366 | |
1367 | if (!list) { |
1368 | list = new NotifyList; |
1369 | // We don't really care when this change takes effect on other threads. The notifyList can |
1370 | // only become non-null once in the life time of a QQmlData. It becomes null again when the |
1371 | // underlying QObject is deleted. At that point any interaction with the QQmlData is UB |
1372 | // anyway. So, for all intents and purposese, the list becomes non-null once and then stays |
1373 | // non-null "forever". We can apply relaxed semantics. |
1374 | notifyList.storeRelaxed(newValue: list); |
1375 | } |
1376 | |
1377 | Q_ASSERT(!endpoint->isConnected()); |
1378 | |
1379 | index = qMin(a: index, b: 0xFFFF - 1); |
1380 | |
1381 | // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other |
1382 | // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying |
1383 | // the conenctionMask in the presence of concurrent modification, any result is correct. |
1384 | list->connectionMask.storeRelaxed( |
1385 | newValue: list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64))); |
1386 | |
1387 | if (index < list->notifiesSize) { |
1388 | endpoint->next = list->notifies[index]; |
1389 | if (endpoint->next) endpoint->next->prev = &endpoint->next; |
1390 | endpoint->prev = &list->notifies[index]; |
1391 | list->notifies[index] = endpoint; |
1392 | } else { |
1393 | list->maximumTodoIndex = qMax(a: int(list->maximumTodoIndex), b: index); |
1394 | |
1395 | endpoint->next = list->todo; |
1396 | if (endpoint->next) endpoint->next->prev = &endpoint->next; |
1397 | endpoint->prev = &list->todo; |
1398 | list->todo = endpoint; |
1399 | } |
1400 | } |
1401 | |
1402 | void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete) |
1403 | { |
1404 | // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. |
1405 | if (NotifyList *list = notifyList.loadRelaxed()) { |
1406 | while (QQmlNotifierEndpoint *todo = list->todo) |
1407 | todo->disconnect(); |
1408 | for (int ii = 0; ii < list->notifiesSize; ++ii) { |
1409 | while (QQmlNotifierEndpoint *ep = list->notifies[ii]) |
1410 | ep->disconnect(); |
1411 | } |
1412 | free(ptr: list->notifies); |
1413 | |
1414 | if (doDelete == DeleteNotifyList::Yes) { |
1415 | // We can only get here from QQmlData::destroyed(), and that can only come from the |
1416 | // the QObject dtor. If you're still sending signals at that point you have UB already |
1417 | // without any threads. Therefore, it's enough to apply relaxed semantics. |
1418 | notifyList.storeRelaxed(newValue: nullptr); |
1419 | delete list; |
1420 | } else { |
1421 | // We can use relaxed semantics here. The worst thing that can happen is that some |
1422 | // signal is falsely reported as connected. Signal connectedness across threads |
1423 | // is not quite deterministic anyway. |
1424 | list->connectionMask.storeRelaxed(newValue: 0); |
1425 | list->maximumTodoIndex = 0; |
1426 | list->notifiesSize = 0; |
1427 | list->notifies = nullptr; |
1428 | |
1429 | } |
1430 | } |
1431 | } |
1432 | |
1433 | QHash<QQmlAttachedPropertiesFunc, QObject *> *QQmlData::attachedProperties() const |
1434 | { |
1435 | if (!extendedData) extendedData = new QQmlDataExtended; |
1436 | return &extendedData->attachedProperties; |
1437 | } |
1438 | |
1439 | void QQmlData::destroyed(QObject *object) |
1440 | { |
1441 | if (nextContextObject) |
1442 | nextContextObject->prevContextObject = prevContextObject; |
1443 | if (prevContextObject) |
1444 | *prevContextObject = nextContextObject; |
1445 | else if (outerContext && outerContext->ownedObjects() == this) |
1446 | outerContext->setOwnedObjects(nextContextObject); |
1447 | |
1448 | QQmlAbstractBinding *binding = bindings; |
1449 | while (binding) { |
1450 | binding->setAddedToObject(false); |
1451 | binding = binding->nextBinding(); |
1452 | } |
1453 | if (bindings && !bindings->ref.deref()) |
1454 | delete bindings; |
1455 | |
1456 | compilationUnit.reset(); |
1457 | |
1458 | qDeleteAll(c: deferredData); |
1459 | deferredData.clear(); |
1460 | |
1461 | QQmlBoundSignal *signalHandler = signalHandlers; |
1462 | while (signalHandler) { |
1463 | if (signalHandler->isNotifying()) { |
1464 | // The object is being deleted during signal handler evaluation. |
1465 | // This will cause a crash due to invalid memory access when the |
1466 | // evaluation has completed. |
1467 | // Abort with a friendly message instead. |
1468 | QString locationString; |
1469 | QQmlBoundSignalExpression *expr = signalHandler->expression(); |
1470 | if (expr) { |
1471 | QQmlSourceLocation location = expr->sourceLocation(); |
1472 | if (location.sourceFile.isEmpty()) |
1473 | location.sourceFile = QStringLiteral("<Unknown File>" ); |
1474 | locationString.append(s: location.sourceFile); |
1475 | locationString.append(QStringLiteral(":%0: " ).arg(a: location.line)); |
1476 | QString source = expr->expression(); |
1477 | if (source.size() > 100) { |
1478 | source.truncate(pos: 96); |
1479 | source.append(s: QLatin1String(" ..." )); |
1480 | } |
1481 | locationString.append(s: source); |
1482 | } else { |
1483 | locationString = QStringLiteral("<Unknown Location>" ); |
1484 | } |
1485 | qFatal(msg: "Object %p destroyed while one of its QML signal handlers is in progress.\n" |
1486 | "Most likely the object was deleted synchronously (use QObject::deleteLater() " |
1487 | "instead), or the application is running a nested event loop.\n" |
1488 | "This behavior is NOT supported!\n" |
1489 | "%s" , object, qPrintable(locationString)); |
1490 | } |
1491 | |
1492 | QQmlBoundSignal *next = signalHandler->m_nextSignal; |
1493 | signalHandler->m_prevSignal = nullptr; |
1494 | signalHandler->m_nextSignal = nullptr; |
1495 | delete signalHandler; |
1496 | signalHandler = next; |
1497 | } |
1498 | |
1499 | if (bindingBitsArraySize > InlineBindingArraySize) |
1500 | free(ptr: bindingBits); |
1501 | |
1502 | if (propertyCache) |
1503 | propertyCache.reset(); |
1504 | |
1505 | ownContext.reset(); |
1506 | |
1507 | while (guards) { |
1508 | auto *guard = guards; |
1509 | guard->setObject(nullptr); |
1510 | if (guard->objectDestroyed) |
1511 | guard->objectDestroyed(guard); |
1512 | } |
1513 | |
1514 | disconnectNotifiers(doDelete: DeleteNotifyList::Yes); |
1515 | |
1516 | if (extendedData) |
1517 | delete extendedData; |
1518 | |
1519 | // Dispose the handle. |
1520 | jsWrapper.clear(); |
1521 | |
1522 | if (ownMemory) |
1523 | delete this; |
1524 | else |
1525 | this->~QQmlData(); |
1526 | } |
1527 | |
1528 | QQmlData::BindingBitsType *QQmlData::growBits(QObject *obj, int bit) |
1529 | { |
1530 | BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; |
1531 | int props = QQmlMetaObject(obj).propertyCount(); |
1532 | Q_ASSERT(bit < 2 * props); |
1533 | Q_UNUSED(bit); // .. for Q_NO_DEBUG mode when the assert above expands to empty |
1534 | |
1535 | uint arraySize = (2 * static_cast<uint>(props) + BitsPerType - 1) / BitsPerType; |
1536 | Q_ASSERT(arraySize > 1); |
1537 | Q_ASSERT(arraySize <= 0xffff); // max for bindingBitsArraySize |
1538 | |
1539 | BindingBitsType *newBits = static_cast<BindingBitsType *>(malloc(size: arraySize*sizeof(BindingBitsType))); |
1540 | memcpy(dest: newBits, src: bits, n: bindingBitsArraySize * sizeof(BindingBitsType)); |
1541 | memset(s: newBits + bindingBitsArraySize, c: 0, n: sizeof(BindingBitsType) * (arraySize - bindingBitsArraySize)); |
1542 | |
1543 | if (bindingBitsArraySize > InlineBindingArraySize) |
1544 | free(ptr: bits); |
1545 | bindingBits = newBits; |
1546 | bits = newBits; |
1547 | bindingBitsArraySize = arraySize; |
1548 | return bits; |
1549 | } |
1550 | |
1551 | QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv) |
1552 | { |
1553 | Q_ASSERT(priv); |
1554 | Q_ASSERT(!priv->isDeletingChildren); |
1555 | priv->declarativeData = new QQmlData(OwnsMemory); |
1556 | return static_cast<QQmlData *>(priv->declarativeData); |
1557 | } |
1558 | |
1559 | QQmlPropertyCache::ConstPtr QQmlData::createPropertyCache(QObject *object) |
1560 | { |
1561 | QQmlData *ddata = QQmlData::get(object, /*create*/true); |
1562 | ddata->propertyCache = QQmlMetaType::propertyCache(object, version: QTypeRevision {}); |
1563 | return ddata->propertyCache; |
1564 | } |
1565 | |
1566 | void QQmlEnginePrivate::sendQuit() |
1567 | { |
1568 | Q_Q(QQmlEngine); |
1569 | emit q->quit(); |
1570 | if (q->receivers(SIGNAL(quit())) == 0) { |
1571 | qWarning(msg: "Signal QQmlEngine::quit() emitted, but no receivers connected to handle it." ); |
1572 | } |
1573 | } |
1574 | |
1575 | void QQmlEnginePrivate::sendExit(int retCode) |
1576 | { |
1577 | Q_Q(QQmlEngine); |
1578 | if (q->receivers(SIGNAL(exit(int))) == 0) |
1579 | qWarning(msg: "Signal QQmlEngine::exit() emitted, but no receivers connected to handle it." ); |
1580 | emit q->exit(retCode); |
1581 | } |
1582 | |
1583 | static void dumpwarning(const QQmlError &error) |
1584 | { |
1585 | switch (error.messageType()) { |
1586 | case QtDebugMsg: |
1587 | QMessageLogger(error.url().toString().toLatin1().constData(), |
1588 | error.line(), nullptr).debug().noquote().nospace() |
1589 | << error.toString(); |
1590 | break; |
1591 | case QtInfoMsg: |
1592 | QMessageLogger(error.url().toString().toLatin1().constData(), |
1593 | error.line(), nullptr).info().noquote().nospace() |
1594 | << error.toString(); |
1595 | break; |
1596 | case QtWarningMsg: |
1597 | case QtFatalMsg: // fatal does not support streaming, and furthermore, is actually fatal. Probably not desirable for QML. |
1598 | QMessageLogger(error.url().toString().toLatin1().constData(), |
1599 | error.line(), nullptr).warning().noquote().nospace() |
1600 | << error.toString(); |
1601 | break; |
1602 | case QtCriticalMsg: |
1603 | QMessageLogger(error.url().toString().toLatin1().constData(), |
1604 | error.line(), nullptr).critical().noquote().nospace() |
1605 | << error.toString(); |
1606 | break; |
1607 | } |
1608 | } |
1609 | |
1610 | static void dumpwarning(const QList<QQmlError> &errors) |
1611 | { |
1612 | for (int ii = 0; ii < errors.size(); ++ii) |
1613 | dumpwarning(error: errors.at(i: ii)); |
1614 | } |
1615 | |
1616 | void QQmlEnginePrivate::warning(const QQmlError &error) |
1617 | { |
1618 | Q_Q(QQmlEngine); |
1619 | emit q->warnings(warnings: QList<QQmlError>({error})); |
1620 | if (outputWarningsToMsgLog) |
1621 | dumpwarning(error); |
1622 | } |
1623 | |
1624 | void QQmlEnginePrivate::warning(const QList<QQmlError> &errors) |
1625 | { |
1626 | Q_Q(QQmlEngine); |
1627 | emit q->warnings(warnings: errors); |
1628 | if (outputWarningsToMsgLog) |
1629 | dumpwarning(errors); |
1630 | } |
1631 | |
1632 | void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error) |
1633 | { |
1634 | if (engine) |
1635 | QQmlEnginePrivate::get(e: engine)->warning(error); |
1636 | else |
1637 | dumpwarning(error); |
1638 | } |
1639 | |
1640 | void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList<QQmlError> &error) |
1641 | { |
1642 | if (engine) |
1643 | QQmlEnginePrivate::get(e: engine)->warning(errors: error); |
1644 | else |
1645 | dumpwarning(errors: error); |
1646 | } |
1647 | |
1648 | void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error) |
1649 | { |
1650 | if (engine) |
1651 | engine->warning(error); |
1652 | else |
1653 | dumpwarning(error); |
1654 | } |
1655 | |
1656 | void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError> &error) |
1657 | { |
1658 | if (engine) |
1659 | engine->warning(errors: error); |
1660 | else |
1661 | dumpwarning(errors: error); |
1662 | } |
1663 | |
1664 | QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics( |
1665 | const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages) |
1666 | { |
1667 | QList<QQmlError> errors; |
1668 | for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { |
1669 | if (m.isWarning()) { |
1670 | qWarning(msg: "%s:%d : %s" , qPrintable(fileName), m.loc.startLine, qPrintable(m.message)); |
1671 | continue; |
1672 | } |
1673 | |
1674 | QQmlError error; |
1675 | error.setUrl(QUrl(fileName)); |
1676 | error.setDescription(m.message); |
1677 | error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: m.loc.startLine)); |
1678 | error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: m.loc.startColumn)); |
1679 | errors << error; |
1680 | } |
1681 | return errors; |
1682 | } |
1683 | |
1684 | void QQmlEnginePrivate::cleanupScarceResources() |
1685 | { |
1686 | // iterate through the list and release them all. |
1687 | // note that the actual SRD is owned by the JS engine, |
1688 | // so we cannot delete the SRD; but we can free the |
1689 | // memory used by the variant in the SRD. |
1690 | QV4::ExecutionEngine *engine = v4engine(); |
1691 | while (QV4::ExecutionEngine::ScarceResourceData *sr = engine->scarceResources.first()) { |
1692 | sr->data = QVariant(); |
1693 | engine->scarceResources.remove(n: sr); |
1694 | } |
1695 | } |
1696 | |
1697 | /*! |
1698 | Adds \a path as a directory where the engine searches for |
1699 | installed modules in a URL-based directory structure. |
1700 | |
1701 | The \a path may be a local filesystem directory, a |
1702 | \l {The Qt Resource System}{Qt Resource} path (\c {:/imports}), a |
1703 | \l {The Qt Resource System}{Qt Resource} url (\c {qrc:/imports}) or a URL. |
1704 | |
1705 | The \a path will be converted into canonical form before it |
1706 | is added to the import path list. |
1707 | |
1708 | The newly added \a path will be first in the importPathList(). |
1709 | |
1710 | \b {See also} \l setImportPathList(), \l {QML Modules}, |
1711 | and \l [QtQml] {QML Import Path} |
1712 | */ |
1713 | void QQmlEngine::addImportPath(const QString& path) |
1714 | { |
1715 | Q_D(QQmlEngine); |
1716 | d->importDatabase.addImportPath(dir: path); |
1717 | } |
1718 | |
1719 | /*! |
1720 | Returns the list of directories where the engine searches for |
1721 | installed modules in a URL-based directory structure. |
1722 | |
1723 | For example, if \c /opt/MyApp/lib/imports is in the path, then QML that |
1724 | imports \c com.mycompany.Feature will cause the QQmlEngine to look |
1725 | in \c /opt/MyApp/lib/imports/com/mycompany/Feature/ for the components |
1726 | provided by that module. A \c qmldir file is required for defining the |
1727 | type version mapping and possibly QML extensions plugins. |
1728 | |
1729 | By default, this list contains the paths mentioned in |
1730 | \l {QML Import Path}. |
1731 | |
1732 | \sa addImportPath(), setImportPathList() |
1733 | */ |
1734 | QStringList QQmlEngine::importPathList() const |
1735 | { |
1736 | Q_D(const QQmlEngine); |
1737 | return d->importDatabase.importPathList(); |
1738 | } |
1739 | |
1740 | /*! |
1741 | Sets \a paths as the list of directories where the engine searches for |
1742 | installed modules in a URL-based directory structure. |
1743 | |
1744 | By default, this list contains the paths mentioned in |
1745 | \l {QML Import Path}. |
1746 | |
1747 | \warning Calling setImportPathList does not preserve the default |
1748 | import paths. |
1749 | |
1750 | \sa importPathList(), addImportPath() |
1751 | */ |
1752 | void QQmlEngine::setImportPathList(const QStringList &paths) |
1753 | { |
1754 | Q_D(QQmlEngine); |
1755 | d->importDatabase.setImportPathList(paths); |
1756 | } |
1757 | |
1758 | |
1759 | /*! |
1760 | Adds \a path as a directory where the engine searches for |
1761 | native plugins for imported modules (referenced in the \c qmldir file). |
1762 | |
1763 | By default, the list contains only \c ., i.e. the engine searches |
1764 | in the directory of the \c qmldir file itself. |
1765 | |
1766 | The newly added \a path will be first in the pluginPathList(). |
1767 | |
1768 | \sa setPluginPathList() |
1769 | */ |
1770 | void QQmlEngine::addPluginPath(const QString& path) |
1771 | { |
1772 | Q_D(QQmlEngine); |
1773 | d->importDatabase.addPluginPath(path); |
1774 | } |
1775 | |
1776 | /*! |
1777 | Returns the list of directories where the engine searches for |
1778 | native plugins for imported modules (referenced in the \c qmldir file). |
1779 | |
1780 | By default, the list contains only \c ., i.e. the engine searches |
1781 | in the directory of the \c qmldir file itself. |
1782 | |
1783 | \sa addPluginPath(), setPluginPathList() |
1784 | */ |
1785 | QStringList QQmlEngine::pluginPathList() const |
1786 | { |
1787 | Q_D(const QQmlEngine); |
1788 | return d->importDatabase.pluginPathList(); |
1789 | } |
1790 | |
1791 | /*! |
1792 | Sets the list of directories where the engine searches for |
1793 | native plugins for imported modules (referenced in the \c qmldir file) |
1794 | to \a paths. |
1795 | |
1796 | By default, the list contains only \c ., i.e. the engine searches |
1797 | in the directory of the \c qmldir file itself. |
1798 | |
1799 | \sa pluginPathList(), addPluginPath() |
1800 | */ |
1801 | void QQmlEngine::setPluginPathList(const QStringList &paths) |
1802 | { |
1803 | Q_D(QQmlEngine); |
1804 | d->importDatabase.setPluginPathList(paths); |
1805 | } |
1806 | |
1807 | #if QT_CONFIG(library) |
1808 | #if QT_DEPRECATED_SINCE(6, 4) |
1809 | /*! |
1810 | \deprecated [6.4] Import the module from QML with an "import" statement instead. |
1811 | |
1812 | Imports the plugin named \a filePath with the \a uri provided. |
1813 | Returns true if the plugin was successfully imported; otherwise returns false. |
1814 | |
1815 | On failure and if non-null, the \a errors list will have any errors which occurred prepended to it. |
1816 | |
1817 | The plugin has to be a Qt plugin which implements the QQmlEngineExtensionPlugin interface. |
1818 | |
1819 | \note Directly loading plugins like this can confuse the module import logic. In order to make |
1820 | the import logic load plugins from a specific place, you can use \l addPluginPath(). Each |
1821 | plugin should be part of a QML module that you can import using the "import" statement. |
1822 | */ |
1823 | bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors) |
1824 | { |
1825 | Q_D(QQmlEngine); |
1826 | QQmlTypeLoaderQmldirContent qmldir; |
1827 | QQmlPluginImporter importer( |
1828 | uri, QTypeRevision(), &d->importDatabase, &qmldir, &d->typeLoader, errors); |
1829 | return importer.importDynamicPlugin(filePath, pluginId: uri, optional: false).isValid(); |
1830 | } |
1831 | #endif |
1832 | #endif |
1833 | |
1834 | /*! |
1835 | \property QQmlEngine::offlineStoragePath |
1836 | \brief the directory for storing offline user data |
1837 | |
1838 | Returns the directory where SQL and other offline |
1839 | storage is placed. |
1840 | |
1841 | The SQL databases created with \c openDatabaseSync() are stored here. |
1842 | |
1843 | The default is QML/OfflineStorage in the platform-standard |
1844 | user application data directory. |
1845 | |
1846 | Note that the path may not currently exist on the filesystem, so |
1847 | callers wanting to \e create new files at this location should create |
1848 | it first - see QDir::mkpath(). |
1849 | |
1850 | \sa {Qt Quick Local Storage QML Types} |
1851 | */ |
1852 | |
1853 | /*! |
1854 | \fn void QQmlEngine::offlineStoragePathChanged() |
1855 | This signal is emitted when \l offlineStoragePath changes. |
1856 | \since 6.5 |
1857 | */ |
1858 | |
1859 | void QQmlEngine::setOfflineStoragePath(const QString& dir) |
1860 | { |
1861 | Q_D(QQmlEngine); |
1862 | if (dir == d->offlineStoragePath) |
1863 | return; |
1864 | d->offlineStoragePath = dir; |
1865 | Q_EMIT offlineStoragePathChanged(); |
1866 | } |
1867 | |
1868 | QString QQmlEngine::offlineStoragePath() const |
1869 | { |
1870 | Q_D(const QQmlEngine); |
1871 | |
1872 | if (d->offlineStoragePath.isEmpty()) { |
1873 | QString dataLocation = QStandardPaths::writableLocation(type: QStandardPaths::AppDataLocation); |
1874 | QQmlEnginePrivate *e = const_cast<QQmlEnginePrivate *>(d); |
1875 | if (!dataLocation.isEmpty()) { |
1876 | e->offlineStoragePath = dataLocation.replace(before: QLatin1Char('/'), after: QDir::separator()) |
1877 | + QDir::separator() + QLatin1String("QML" ) |
1878 | + QDir::separator() + QLatin1String("OfflineStorage" ); |
1879 | Q_EMIT e->q_func()->offlineStoragePathChanged(); |
1880 | } |
1881 | } |
1882 | |
1883 | return d->offlineStoragePath; |
1884 | } |
1885 | |
1886 | /*! |
1887 | Returns the file path where a \l{QtQuick.LocalStorage}{Local Storage} |
1888 | database with the identifier \a databaseName is (or would be) located. |
1889 | |
1890 | \sa {openDatabaseSync}{LocalStorage.openDatabaseSync()} |
1891 | \since 5.9 |
1892 | */ |
1893 | QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) const |
1894 | { |
1895 | Q_D(const QQmlEngine); |
1896 | QCryptographicHash md5(QCryptographicHash::Md5); |
1897 | md5.addData(data: databaseName.toUtf8()); |
1898 | return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex()); |
1899 | } |
1900 | |
1901 | QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const |
1902 | { |
1903 | Q_Q(const QQmlEngine); |
1904 | return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases" ) + QDir::separator(); |
1905 | } |
1906 | |
1907 | template<> |
1908 | QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type) |
1909 | { |
1910 | Q_Q(QQmlEngine); |
1911 | |
1912 | QJSValue value = singletonInstances.value(key: type); |
1913 | if (!value.isUndefined()) { |
1914 | return value; |
1915 | } |
1916 | |
1917 | QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); |
1918 | Q_ASSERT(siinfo != nullptr); |
1919 | |
1920 | if (siinfo->scriptCallback) { |
1921 | value = siinfo->scriptCallback(q, q); |
1922 | if (value.isQObject()) { |
1923 | QObject *o = value.toQObject(); |
1924 | // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) |
1925 | // should behave identically to QML singleton types. |
1926 | q->setContextForObject(object: o, context: new QQmlContext(q->rootContext(), q)); |
1927 | } |
1928 | singletonInstances.convertAndInsert(engine: v4engine(), type, value: &value); |
1929 | |
1930 | } else if (siinfo->qobjectCallback) { |
1931 | QObject *o = siinfo->qobjectCallback(q, q); |
1932 | if (!o) { |
1933 | QQmlError error; |
1934 | error.setMessageType(QtMsgType::QtCriticalMsg); |
1935 | error.setDescription(QString::asprintf(format: "qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer." , |
1936 | qPrintable(QString::fromUtf8(type.typeName())))); |
1937 | warning(error); |
1938 | } else { |
1939 | type.createProxy(instance: o); |
1940 | |
1941 | // if this object can use a property cache, create it now |
1942 | QQmlData::ensurePropertyCache(object: o); |
1943 | |
1944 | // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) |
1945 | // should behave identically to QML singleton types. You can, however, manually |
1946 | // assign a context; and clearSingletons() retains the contexts, in which case |
1947 | // we don't want to see warnings about the object already having a context. |
1948 | QQmlData *data = QQmlData::get(object: o, create: true); |
1949 | if (!data->context) { |
1950 | auto contextData = QQmlContextData::get(context: new QQmlContext(q->rootContext(), q)); |
1951 | data->context = contextData.data(); |
1952 | contextData->addOwnedObject(ownedObject: data); |
1953 | } |
1954 | } |
1955 | |
1956 | value = q->newQObject(object: o); |
1957 | singletonInstances.convertAndInsert(engine: v4engine(), type, value: &value); |
1958 | } else if (!siinfo->url.isEmpty()) { |
1959 | QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous); |
1960 | if (component.isError()) { |
1961 | warning(errors: component.errors()); |
1962 | v4engine()->throwError(message: QLatin1String("Due to the preceding error(s), Singleton \"%1\" could not be loaded." ).arg(args: QString::fromUtf8(ba: type.typeName()))); |
1963 | |
1964 | return QJSValue(QJSValue::UndefinedValue); |
1965 | } |
1966 | QObject *o = component.beginCreate(q->rootContext()); |
1967 | value = q->newQObject(object: o); |
1968 | singletonInstances.convertAndInsert(engine: v4engine(), type, value: &value); |
1969 | component.completeCreate(); |
1970 | } |
1971 | |
1972 | return value; |
1973 | } |
1974 | |
1975 | bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const |
1976 | { |
1977 | return typeLoader.isTypeLoaded(url); |
1978 | } |
1979 | |
1980 | bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const |
1981 | { |
1982 | return typeLoader.isScriptLoaded(url); |
1983 | } |
1984 | |
1985 | void QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex, |
1986 | QObject *thisObject, int argc, void **args, |
1987 | QMetaType *types) |
1988 | { |
1989 | const auto unit = compilationUnitFromUrl(url); |
1990 | if (!unit) |
1991 | return; |
1992 | executeRuntimeFunction(unit, functionIndex, thisObject, argc, args, types); |
1993 | } |
1994 | |
1995 | void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationUnit *unit, |
1996 | qsizetype functionIndex, QObject *thisObject, |
1997 | int argc, void **args, QMetaType *types) |
1998 | { |
1999 | Q_ASSERT(unit); |
2000 | Q_ASSERT((functionIndex >= 0) && (functionIndex < unit->runtimeFunctions.size())); |
2001 | Q_ASSERT(thisObject); |
2002 | |
2003 | QQmlData *ddata = QQmlData::get(object: thisObject); |
2004 | Q_ASSERT(ddata && ddata->outerContext); |
2005 | |
2006 | QV4::Function *function = unit->runtimeFunctions[functionIndex]; |
2007 | Q_ASSERT(function); |
2008 | Q_ASSERT(function->compiledFunction); |
2009 | |
2010 | QV4::ExecutionEngine *v4 = v4engine(); |
2011 | |
2012 | // NB: always use scriptContext() by default as this method ignores whether |
2013 | // there's already a stack frame (except when dealing with closures). the |
2014 | // method is called from C++ (through QQmlEngine::executeRuntimeFunction()) |
2015 | // and thus the caller must ensure correct setup |
2016 | QV4::Scope scope(v4); |
2017 | QV4::ExecutionContext *ctx = v4->scriptContext(); |
2018 | QV4::Scoped<QV4::ExecutionContext> callContext(scope, |
2019 | QV4::QmlContext::create(parent: ctx, context: ddata->outerContext, scopeObject: thisObject)); |
2020 | |
2021 | if (auto nested = function->nestedFunction()) { |
2022 | // if a nested function is already known, call the closure directly |
2023 | function = nested; |
2024 | } else if (function->isClosureWrapper()) { |
2025 | // if there is a nested function, but we don't know it, we need to call |
2026 | // an outer function first and then the inner function. we fetch the |
2027 | // return value of a function call (that is a closure) by calling a |
2028 | // different version of ExecutionEngine::callInContext() that returns a |
2029 | // QV4::ReturnedValue with no arguments since they are not needed by the |
2030 | // outer function anyhow |
2031 | QV4::ScopedFunctionObject result(scope, |
2032 | v4->callInContext(function, self: thisObject, ctxt: callContext, argc: 0, argv: nullptr)); |
2033 | Q_ASSERT(result->function()); |
2034 | Q_ASSERT(result->function()->compilationUnit == function->compilationUnit); |
2035 | |
2036 | // overwrite the function and its context |
2037 | function = result->function(); |
2038 | callContext = QV4::Scoped<QV4::ExecutionContext>(scope, result->scope()); |
2039 | } |
2040 | |
2041 | v4->callInContext(function, self: thisObject, ctxt: callContext, argc, args, types); |
2042 | } |
2043 | |
2044 | QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url) |
2045 | { |
2046 | auto unit = typeLoader.getType(unNormalizedUrl: url)->compilationUnit(); |
2047 | if (!unit) |
2048 | return nullptr; |
2049 | if (!unit->engine) |
2050 | unit->linkToEngine(engine: v4engine()); |
2051 | return unit; |
2052 | } |
2053 | |
2054 | QQmlRefPointer<QQmlContextData> |
2055 | QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, |
2056 | const QQmlRefPointer<QQmlContextData> &parentContext, |
2057 | int subComponentIndex, bool isComponentRoot) |
2058 | { |
2059 | Q_ASSERT(unit); |
2060 | |
2061 | QQmlRefPointer<QQmlContextData> context; |
2062 | context = QQmlContextData::createRefCounted(parent: parentContext); |
2063 | context->setInternal(true); |
2064 | context->setImports(unit->typeNameCache); |
2065 | context->initFromTypeCompilationUnit(unit, subComponentIndex); |
2066 | |
2067 | if (isComponentRoot && unit->dependentScripts.size()) { |
2068 | QV4::ExecutionEngine *v4 = v4engine(); |
2069 | Q_ASSERT(v4); |
2070 | QV4::Scope scope(v4); |
2071 | |
2072 | QV4::ScopedObject scripts(scope, v4->newArrayObject(count: unit->dependentScripts.size())); |
2073 | context->setImportedScripts(QV4::PersistentValue(v4, scripts.asReturnedValue())); |
2074 | QV4::ScopedValue v(scope); |
2075 | for (int i = 0; i < unit->dependentScripts.size(); ++i) { |
2076 | QQmlRefPointer<QQmlScriptData> s = unit->dependentScripts.at(i); |
2077 | scripts->put(idx: i, v: (v = s->scriptValueForContext(parentCtxt: context))); |
2078 | } |
2079 | } |
2080 | |
2081 | return context; |
2082 | } |
2083 | |
2084 | #if defined(Q_OS_WIN) |
2085 | // Normalize a file name using Shell API. As opposed to converting it |
2086 | // to a short 8.3 name and back, this also works for drives where 8.3 notation |
2087 | // is disabled (see 8dot3name options of fsutil.exe). |
2088 | static inline QString shellNormalizeFileName(const QString &name) |
2089 | { |
2090 | const QString nativeSeparatorName(QDir::toNativeSeparators(name)); |
2091 | const LPCTSTR nameC = reinterpret_cast<LPCTSTR>(nativeSeparatorName.utf16()); |
2092 | // The correct declaration of the SHGetPathFromIDList symbol is |
2093 | // being used in mingw-w64 as of r6215, which is a v3 snapshot. |
2094 | #if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3) |
2095 | ITEMIDLIST *file; |
2096 | if (FAILED(SHParseDisplayName(nameC, NULL, reinterpret_cast<LPITEMIDLIST>(&file), 0, NULL))) |
2097 | return name; |
2098 | #else |
2099 | PIDLIST_ABSOLUTE file; |
2100 | if (FAILED(SHParseDisplayName(nameC, NULL, &file, 0, NULL))) |
2101 | return name; |
2102 | #endif |
2103 | TCHAR buffer[MAX_PATH]; |
2104 | bool gotPath = SHGetPathFromIDList(file, buffer); |
2105 | ILFree(file); |
2106 | |
2107 | if (!gotPath) |
2108 | return name; |
2109 | |
2110 | QString canonicalName = QString::fromWCharArray(buffer); |
2111 | // Upper case drive letter |
2112 | if (canonicalName.size() > 2 && canonicalName.at(1) == QLatin1Char(':')) |
2113 | canonicalName[0] = canonicalName.at(0).toUpper(); |
2114 | return QDir::cleanPath(canonicalName); |
2115 | } |
2116 | #endif // Q_OS_WIN |
2117 | |
2118 | bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) |
2119 | { |
2120 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) |
2121 | QFileInfo info(fileName); |
2122 | const QString absolute = info.absoluteFilePath(); |
2123 | |
2124 | #if defined(Q_OS_DARWIN) |
2125 | const QString canonical = info.canonicalFilePath(); |
2126 | #elif defined(Q_OS_WIN) |
2127 | // No difference if the path is qrc based |
2128 | if (absolute[0] == QLatin1Char(':')) |
2129 | return true; |
2130 | const QString canonical = shellNormalizeFileName(absolute); |
2131 | #endif |
2132 | |
2133 | const int absoluteLength = absolute.length(); |
2134 | const int canonicalLength = canonical.length(); |
2135 | |
2136 | int length = qMin(absoluteLength, canonicalLength); |
2137 | if (lengthIn >= 0) { |
2138 | length = qMin(lengthIn, length); |
2139 | } else { |
2140 | // No length given: Limit to file name. Do not trigger |
2141 | // on drive letters or folder names. |
2142 | int lastSlash = absolute.lastIndexOf(QLatin1Char('/')); |
2143 | if (lastSlash < 0) |
2144 | lastSlash = absolute.lastIndexOf(QLatin1Char('\\')); |
2145 | if (lastSlash >= 0) { |
2146 | const int fileNameLength = absoluteLength - 1 - lastSlash; |
2147 | length = qMin(length, fileNameLength); |
2148 | } |
2149 | } |
2150 | |
2151 | for (int ii = 0; ii < length; ++ii) { |
2152 | const QChar &a = absolute.at(absoluteLength - 1 - ii); |
2153 | const QChar &c = canonical.at(canonicalLength - 1 - ii); |
2154 | |
2155 | if (a.toLower() != c.toLower()) |
2156 | return true; |
2157 | if (a != c) |
2158 | return false; |
2159 | } |
2160 | #else |
2161 | Q_UNUSED(lengthIn); |
2162 | Q_UNUSED(fileName); |
2163 | #endif |
2164 | return true; |
2165 | } |
2166 | |
2167 | /*! |
2168 | \fn QQmlEngine *qmlEngine(const QObject *object) |
2169 | \relates QQmlEngine |
2170 | |
2171 | Returns the QQmlEngine associated with \a object, if any. This is equivalent to |
2172 | QQmlEngine::contextForObject(object)->engine(), but more efficient. |
2173 | |
2174 | \note Add \c{#include <QtQml>} to use this function. |
2175 | |
2176 | \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlContext() |
2177 | */ |
2178 | |
2179 | /*! |
2180 | \fn QQmlContext *qmlContext(const QObject *object) |
2181 | \relates QQmlEngine |
2182 | |
2183 | Returns the QQmlContext associated with \a object, if any. This is equivalent to |
2184 | QQmlEngine::contextForObject(object). |
2185 | |
2186 | \note Add \c{#include <QtQml>} to use this function. |
2187 | |
2188 | \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine() |
2189 | */ |
2190 | |
2191 | void hasJsOwnershipIndicator(QQmlGuardImpl *) {}; |
2192 | |
2193 | LoadHelper::LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri) |
2194 | : QQmlTypeLoader::Blob({}, QQmlDataBlob::QmlFile, loader) |
2195 | , m_uri(uri.toString()) |
2196 | |
2197 | { |
2198 | auto import = std::make_shared<PendingImport>(); |
2199 | import->uri = m_uri; |
2200 | QList<QQmlError> errorList; |
2201 | if (!Blob::addImport(import, errors: &errorList)) { |
2202 | qCDebug(lcQmlImport) << "LoadHelper: Errors loading " << m_uri << errorList; |
2203 | m_uri.clear(); // reset m_uri to remember the failure |
2204 | } |
2205 | } |
2206 | |
2207 | LoadHelper::ResolveTypeResult LoadHelper::resolveType(QAnyStringView typeName) |
2208 | { |
2209 | QQmlType type; |
2210 | if (!couldFindModule()) |
2211 | return {.status: ResolveTypeResult::NoSuchModule, .type: type}; |
2212 | QQmlTypeModule *module = QQmlMetaType::typeModule(uri: m_uri, version: QTypeRevision{}); |
2213 | if (module) { |
2214 | type = module->type(name: typeName.toString(), version: {}); |
2215 | if (type.isValid()) |
2216 | return {.status: ResolveTypeResult::ModuleFound, .type: type}; |
2217 | } |
2218 | // The module exists (see check above), but there is no QQmlTypeModule |
2219 | // ==> pure QML module, attempt resolveType |
2220 | QTypeRevision versionReturn; |
2221 | QList<QQmlError> errors; |
2222 | QQmlImportNamespace *ns_return = nullptr; |
2223 | m_importCache->resolveType(type: typeName.toString(), type_return: &type, version_return: &versionReturn, |
2224 | ns_return: &ns_return, |
2225 | errors: &errors); |
2226 | return {.status: ResolveTypeResult::ModuleFound, .type: type}; |
2227 | } |
2228 | |
2229 | bool LoadHelper::couldFindModule() const |
2230 | { |
2231 | if (m_uri.isEmpty()) |
2232 | return false; |
2233 | for (const auto &import: std::as_const(t: m_unresolvedImports)) |
2234 | if (import->priority == 0) // compare QQmlTypeData::allDependenciesDone |
2235 | return false; |
2236 | return true; |
2237 | } |
2238 | |
2239 | QT_END_NAMESPACE |
2240 | |
2241 | #include "moc_qqmlengine_p.cpp" |
2242 | |
2243 | #include "moc_qqmlengine.cpp" |
2244 | |
2245 | |