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