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
63QT_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
121Q_CONSTINIT std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
122bool QQmlEnginePrivate::s_designerMode = false;
123
124bool QQmlEnginePrivate::designerMode()
125{
126 return s_designerMode;
127}
128
129void 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 */
189QQmlImageProviderBase::QQmlImageProviderBase()
190{
191}
192
193/*! \internal */
194QQmlImageProviderBase::~QQmlImageProviderBase()
195{
196}
197
198QQmlEnginePrivate::~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
214void 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
258QQmlData::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
271QQmlData::~QQmlData()
272{
273}
274
275void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o)
276{
277 QQmlData *ddata = static_cast<QQmlData *>(d);
278 ddata->destroyed(o);
279}
280
281
282class QQmlThreadNotifierProxyObject : public QObject
283{
284public:
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
304void 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
364int QQmlData::receivers(QAbstractDeclarativeData *d, const QObject *, int index)
365{
366 QQmlData *ddata = static_cast<QQmlData *>(d);
367 return ddata->endpointCount(index);
368}
369
370bool QQmlData::isSignalConnected(QAbstractDeclarativeData *d, const QObject *, int index)
371{
372 QQmlData *ddata = static_cast<QQmlData *>(d);
373 return ddata->signalHasEndpoint(index);
374}
375
376int 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
390void 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
404void 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
428void 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
444QQmlData::DeferredData::DeferredData() = default;
445QQmlData::DeferredData::~DeferredData() = default;
446
447template<>
448int 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
477bool QQmlEnginePrivate::baseModulesUninitialized = true;
478void 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*/
614QQmlEngine::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*/
625QQmlEngine::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*/
641QQmlEngine::~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 */
709void 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 */
729void 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 */
750void 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*/
767QQmlContext *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*/
782QQmlAbstractUrlInterceptor *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*/
798void 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*/
812void 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 */
822QUrl 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 */
834QList<QQmlAbstractUrlInterceptor *> QQmlEngine::urlInterceptors() const
835{
836 Q_D(const QQmlEngine);
837 return d->urlInterceptors;
838}
839
840QSharedPointer<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*/
860void 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*/
872QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const
873{
874 Q_D(const QQmlEngine);
875 return d->networkAccessManagerFactory;
876}
877
878QNetworkAccessManager *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
891QNetworkAccessManager *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*/
911QNetworkAccessManager *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*/
933void 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*/
947QQmlImageProviderBase *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*/
960void 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*/
978QUrl 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*/
995void 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*/
1007bool 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*/
1023void 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*/
1061void 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 */
1073void 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*/
1128template<>
1129QJSValue 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*/
1163template<>
1164QJSValue 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*/
1191void 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 */
1207QQmlContext *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 */
1227void 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*/
1247bool QQmlEngine::event(QEvent *e)
1248{
1249 if (e->type() == QEvent::LanguageChange) {
1250 retranslate();
1251 }
1252
1253 return QJSEngine::event(event: e);
1254}
1255
1256class QQmlDataExtended {
1257public:
1258 QQmlDataExtended();
1259 ~QQmlDataExtended();
1260
1261 QHash<QQmlAttachedPropertiesFunc, QObject *> attachedProperties;
1262};
1263
1264QQmlDataExtended::QQmlDataExtended()
1265{
1266}
1267
1268QQmlDataExtended::~QQmlDataExtended()
1269{
1270}
1271
1272void 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 = &notifies[index];
1292 notifies[index] = endpoint;
1293
1294 endpoint = ep;
1295 }
1296}
1297
1298void 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 = &notifies[ii];
1314 }
1315
1316 notifiesSize = maximumTodoIndex + 1;
1317
1318 layout(endpoint: todo);
1319 }
1320
1321 maximumTodoIndex = 0;
1322 todo = nullptr;
1323}
1324
1325void 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
1347void 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
1361void 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
1402void 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
1433QHash<QQmlAttachedPropertiesFunc, QObject *> *QQmlData::attachedProperties() const
1434{
1435 if (!extendedData) extendedData = new QQmlDataExtended;
1436 return &extendedData->attachedProperties;
1437}
1438
1439void 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
1528QQmlData::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
1551QQmlData *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
1559QQmlPropertyCache::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
1566void 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
1575void 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
1583static 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
1610static 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
1616void 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
1624void 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
1632void 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
1640void 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
1648void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error)
1649{
1650 if (engine)
1651 engine->warning(error);
1652 else
1653 dumpwarning(error);
1654}
1655
1656void 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
1664QList<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
1684void 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*/
1713void 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*/
1734QStringList 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 */
1752void 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*/
1770void 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*/
1785QStringList 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 */
1801void 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*/
1823bool 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
1859void 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
1868QString 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*/
1893QString 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
1901QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
1902{
1903 Q_Q(const QQmlEngine);
1904 return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator();
1905}
1906
1907template<>
1908QJSValue 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
1975bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const
1976{
1977 return typeLoader.isTypeLoaded(url);
1978}
1979
1980bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const
1981{
1982 return typeLoader.isScriptLoaded(url);
1983}
1984
1985void 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
1995void 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
2044QV4::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
2054QQmlRefPointer<QQmlContextData>
2055QQmlEnginePrivate::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).
2088static 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
2118bool 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
2191void hasJsOwnershipIndicator(QQmlGuardImpl *) {};
2192
2193LoadHelper::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
2207LoadHelper::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
2229bool 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
2239QT_END_NAMESPACE
2240
2241#include "moc_qqmlengine_p.cpp"
2242
2243#include "moc_qqmlengine.cpp"
2244
2245

source code of qtdeclarative/src/qml/qml/qqmlengine.cpp