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
54
55using namespace Qt::StringLiterals;
56
58
59/*!
60 \qmltype QtObject
61 \nativetype QObject
62 \inqmlmodule QtQml
63 \ingroup qml-utility-elements
64 \brief A basic QML type.
65
66 The QtObject type is a non-visual element which contains only the
67 objectName property.
68
69 It can be useful to create a QtObject if you need an extremely
70 lightweight type to enclose a set of custom properties:
71
72 \snippet qml/qtobject.qml 0
73
74 It can also be useful for C++ integration, as it is just a plain
75 QObject. See the QObject documentation for further details.
76*/
77/*!
78 \qmlproperty string QtObject::objectName
79 This property holds the QObject::objectName for this specific object instance.
80
81 This allows a C++ application to locate an item within a QML component
82 using the QObject::findChild() method. For example, the following C++
83 application locates the child \l Rectangle item and dynamically changes its
84 \c color value:
85
86 \qml
87 // MyRect.qml
88
89 import QtQuick 2.0
90
91 Item {
92 width: 200; height: 200
93
94 Rectangle {
95 anchors.fill: parent
96 color: "red"
97 objectName: "myRect"
98 }
99 }
100 \endqml
101
102 \code
103 // main.cpp
104
105 QQuickView view;
106 view.setSource(QUrl::fromLocalFile("MyRect.qml"));
107 view.show();
108
109 QQuickItem *item = view.rootObject()->findChild<QQuickItem*>("myRect");
110 if (item)
111 item->setProperty("color", QColor(Qt::yellow));
112 \endcode
113*/
114
115Q_CONSTINIT std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
116bool QQmlEnginePrivate::s_designerMode = false;
117
118bool QQmlEnginePrivate::designerMode()
119{
120 return s_designerMode;
121}
122
123void QQmlEnginePrivate::activateDesignerMode()
124{
125 s_designerMode = true;
126}
127
128
129/*!
130 \class QQmlImageProviderBase
131 \brief The QQmlImageProviderBase class is used to register image providers in the QML engine.
132 \inmodule QtQml
133
134 Image providers must be registered with the QML engine. The only information the QML
135 engine knows about image providers is the type of image data they provide. To use an
136 image provider to acquire image data, you must cast the QQmlImageProviderBase pointer
137 to a QQuickImageProvider pointer.
138
139 \sa QQuickImageProvider, QQuickTextureFactory
140*/
141
142/*!
143 \enum QQmlImageProviderBase::ImageType
144
145 Defines the type of image supported by this image provider.
146
147 \value Image The Image Provider provides QImage images.
148 The QQuickImageProvider::requestImage() method will be called for all image requests.
149 \value Pixmap The Image Provider provides QPixmap images.
150 The QQuickImageProvider::requestPixmap() method will be called for all image requests.
151 \value Texture The Image Provider provides QSGTextureProvider based images.
152 The QQuickImageProvider::requestTexture() method will be called for all image requests.
153 \value ImageResponse The Image provider provides QQuickTextureFactory based images.
154 Should only be used in QQuickAsyncImageProvider or its subclasses.
155 The QQuickAsyncImageProvider::requestImageResponse() method will be called for all image requests.
156 Since Qt 5.6
157 \omitvalue Invalid
158*/
159
160/*!
161 \enum QQmlImageProviderBase::Flag
162
163 Defines specific requirements or features of this image provider.
164
165 \value ForceAsynchronousImageLoading Ensures that image requests to the provider are
166 run in a separate thread, which allows the provider to spend as much time as needed
167 on producing the image without blocking the main thread.
168*/
169
170/*!
171 \fn QQmlImageProviderBase::imageType() const
172
173 Implement this method to return the image type supported by this image provider.
174*/
175
176/*!
177 \fn QQmlImageProviderBase::flags() const
178
179 Implement this to return the properties of this image provider.
180*/
181
182/*! \internal */
183QQmlImageProviderBase::QQmlImageProviderBase()
184{
185}
186
187/*! \internal */
188QQmlImageProviderBase::~QQmlImageProviderBase()
189{
190}
191
192QQmlEnginePrivate::~QQmlEnginePrivate()
193{
194 if (inProgressCreations)
195 qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations);
196
197 if (incubationController) incubationController->d = nullptr;
198 incubationController = nullptr;
199
200#if QT_CONFIG(qml_debug)
201 delete profiler;
202#endif
203}
204
205void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
206{
207 QObjectPrivate *p = QObjectPrivate::get(o);
208 if (QQmlData *d = QQmlData::get(p)) {
209 const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
210 if (d->ownContext) {
211 d->ownContext->deepClearContextObject(o, invalidate, invalidate);
212 d->ownContext.reset();
213 d->context = nullptr;
214 Q_ASSERT(!d->outerContext || d->outerContext->contextObject() != o);
215 } else if (d->outerContext && d->outerContext->contextObject() == o) {
216 d->outerContext->deepClearContextObject(o, invalidate, invalidate);
217 }
218
219 if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
220 // This is somewhat dangerous because another thread might concurrently
221 // try to resolve the dynamic metaobject. In practice this will then
222 // lead to either the code path that still returns the interceptor
223 // metaobject or the code path that returns the string casted one. Both
224 // is fine if you cannot actually touch the object itself. Since the
225 // other thread is obviously not synchronized to this one, it can't.
226 //
227 // In particular we do this when delivering the frameSwapped() signal
228 // in QQuickWindow. The handler for frameSwapped() is written in a way
229 // that is thread safe as long as QQuickWindow's dtor hasn't finished.
230 // QQuickWindow's dtor does synchronize with the render thread, but it
231 // runs _after_ qdeclarativeelement_destructor.
232 static_cast<QQmlInterceptorMetaObject *>(p->metaObject)->invalidate();
233 d->hasVMEMetaObject = d->hasInterceptorMetaObject = false;
234 }
235
236 // Mark this object as in the process of deletion to
237 // prevent it resolving in bindings
238 QQmlData::markAsDeleted(o);
239 }
240}
241
242QQmlData::QQmlData(Ownership ownership)
243 : ownMemory(ownership == OwnsMemory), indestructible(true), explicitIndestructibleSet(false),
244 hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
245 hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false), dummy(0),
246 bindingBitsArraySize(InlineBindingArraySize)
247{
248 memset(bindingBitsValue, 0, sizeof(bindingBitsValue));
249 init();
250}
251
252QQmlData::~QQmlData()
253{
254}
255
256void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o)
257{
258 QQmlData *ddata = static_cast<QQmlData *>(d);
259 ddata->destroyed(o);
260}
261
262
264{
265public:
267
268 int qt_metacall(QMetaObject::Call, int methodIndex, void **a) override {
269 if (!target)
270 return -1;
271
272 QMetaMethod method = target->metaObject()->method(methodIndex);
273 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
274 int signalIndex = QMetaObjectPrivate::signalIndex(method);
275 QQmlData *ddata = QQmlData::get(target, false);
276 QQmlNotifierEndpoint *ep = ddata->notify(signalIndex);
277 if (ep) QQmlNotifier::emitNotify(ep, a);
278
279 delete this;
280
281 return -1;
282 }
283};
284
285void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int index, void **a)
286{
287 QQmlData *ddata = QQmlData::get(object, false);
288 if (!ddata) return; // Probably being deleted
289
290 // In general, QML only supports QObject's that live on the same thread as the QQmlEngine
291 // that they're exposed to. However, to make writing "worker objects" that calculate data
292 // in a separate thread easier, QML allows a QObject that lives in the same thread as the
293 // QQmlEngine to emit signals from a different thread. These signals are then automatically
294 // marshalled back onto the QObject's thread and handled by QML from there. This is tested
295 // by the qqmlecmascript::threadSignal() autotest.
296
297 // Relaxed semantics here. If we're on a different thread we might schedule a useless event,
298 // but that should be rare.
299 if (!ddata->notifyList.loadRelaxed())
300 return;
301
302 auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
303 if (QThread::currentThreadId() != objectThreadData->threadId.loadRelaxed()) {
304 if (!objectThreadData->thread.loadAcquire())
305 return;
306
307 QMetaMethod m = QMetaObjectPrivate::signal(object->metaObject(), index);
308 QList<QByteArray> parameterTypes = m.parameterTypes();
309
310 auto ev = std::make_unique<QMetaCallEvent>(m.methodIndex(), 0, nullptr,
311 object, index,
312 parameterTypes.size() + 1);
313
314 void **args = ev->args();
315 QMetaType *types = ev->types();
316
317 for (int ii = 0; ii < parameterTypes.size(); ++ii) {
318 const QByteArray &typeName = parameterTypes.at(ii);
319 if (typeName.endsWith('*'))
320 types[ii + 1] = QMetaType(QMetaType::VoidStar);
321 else
322 types[ii + 1] = QMetaType::fromName(typeName);
323
324 if (!types[ii + 1].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 args[ii + 1] = types[ii + 1].create(a[ii + 1]);
332 }
333
334 QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject;
335 mpo->target = object;
336 mpo->moveToThread(objectThreadData->thread.loadAcquire());
337 QCoreApplication::postEvent(mpo, ev.release());
338
339 } else {
340 QQmlNotifierEndpoint *ep = ddata->notify(index);
341 if (ep) QQmlNotifier::emitNotify(ep, a);
342 }
343}
344
345int QQmlData::receivers(QAbstractDeclarativeData *d, const QObject *, int index)
346{
347 QQmlData *ddata = static_cast<QQmlData *>(d);
348 return ddata->endpointCount(index);
349}
350
351bool QQmlData::isSignalConnected(QAbstractDeclarativeData *d, const QObject *, int index)
352{
353 QQmlData *ddata = static_cast<QQmlData *>(d);
354 return ddata->signalHasEndpoint(index);
355}
356
357int QQmlData::endpointCount(int index)
358{
359 int count = 0;
360 QQmlNotifierEndpoint *ep = notify(index);
361 if (!ep)
362 return count;
363 ++count;
364 while (ep->next) {
365 ++count;
366 ep = ep->next;
367 }
368 return count;
369}
370
371void QQmlData::markAsDeleted(QObject *o)
372{
373 QVarLengthArray<QObject *> workStack;
374 workStack.push_back(o);
375 while (!workStack.isEmpty()) {
376 auto currentObject = workStack.last();
377 workStack.pop_back();
378 QQmlData::setQueuedForDeletion(currentObject);
379 auto currentObjectPriv = QObjectPrivate::get(currentObject);
380 for (QObject *child: std::as_const(currentObjectPriv->children))
381 workStack.push_back(child);
382 }
383}
384
385void QQmlData::setQueuedForDeletion(QObject *object)
386{
387 if (object) {
388 if (QQmlData *ddata = QQmlData::get(object)) {
389 if (ddata->ownContext) {
390 Q_ASSERT(ddata->ownContext.data() == ddata->context);
391 ddata->ownContext->deepClearContextObject(object);
392 ddata->ownContext.reset();
393 ddata->context = nullptr;
394 }
395 ddata->isQueuedForDeletion = true;
396
397 // Disconnect the notifiers now - during object destruction this would be too late,
398 // since the disconnect call wouldn't be able to call disconnectNotify(), as it isn't
399 // possible to get the metaobject anymore.
400 // Also, there is no point in evaluating bindings in order to set properties on
401 // half-deleted objects.
402 ddata->disconnectNotifiers(DeleteNotifyList::No);
403 }
404 }
405}
406
407void QQmlData::flushPendingBinding(int coreIndex)
408{
409 clearPendingBindingBit(coreIndex);
410
411 // Find the binding
412 QQmlAbstractBinding *b = bindings;
413 while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
414 b->targetPropertyIndex().hasValueTypeIndex()))
415 b = b->nextBinding();
416
417 if (b && b->targetPropertyIndex().coreIndex() == coreIndex &&
418 !b->targetPropertyIndex().hasValueTypeIndex())
419 b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
420 QQmlPropertyData::DontRemoveBinding);
421}
422
423QQmlData::DeferredData::DeferredData() = default;
424QQmlData::DeferredData::~DeferredData() = default;
425
426template<>
427int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
428{
429 QQmlPrivate::RegisterType type = {
430 QQmlPrivate::RegisterType::CurrentVersion,
431 QMetaType(),
432 QMetaType(),
433 0, nullptr, nullptr,
434 QString(),
435 nullptr,
436 uri,
437 QTypeRevision::fromVersion(versionMajor, versionMinor),
438 qmlName,
439 nullptr,
440 nullptr,
441 nullptr,
442 -1,
443 -1,
444 -1,
445 nullptr,
446 nullptr,
447 nullptr,
448 QTypeRevision::zero(),
449 -1,
450 QQmlPrivate::ValueTypeCreationMethod::None,
451 };
452
453 return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
454}
455
456bool QQmlEnginePrivate::baseModulesUninitialized = true;
457void QQmlEnginePrivate::init()
458{
459 Q_Q(QQmlEngine);
460
461 if (baseModulesUninitialized) {
462 // Register builtins
463 qml_register_types_QML();
464
465 // No need to specifically register those.
466 static_assert(std::is_same_v<QStringList, QList<QString>>);
467 static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
468
469 qRegisterMetaType<QQmlScriptString>();
470 qRegisterMetaType<QQmlComponent::Status>();
471 qRegisterMetaType<QList<QObject*> >();
472 qRegisterMetaType<QQmlBinding*>();
473
474 // Protect the module: We don't want any URL interceptor to mess with the builtins.
475 qmlProtectModule("QML", 1);
476
477 QQmlData::init();
478 baseModulesUninitialized = false;
479 }
480
481 q->handle()->setQmlEngine(q);
482
483 rootContext = new QQmlContext(q,true);
484}
485
486/*!
487 \class QQmlEngine
488 \since 5.0
489 \inmodule QtQml
490 \brief The QQmlEngine class provides an environment for instantiating QML components.
491
492 A QQmlEngine is used to manage \l{QQmlComponent}{components} and objects created from
493 them and execute their bindings and functions. QQmlEngine also inherits from
494 \l{QJSEngine} which allows seamless integration between your QML components and
495 JavaScript code.
496
497 Each QML component is instantiated in a QQmlContext. In QML, contexts are arranged
498 hierarchically and this hierarchy is managed by the QQmlEngine. By default,
499 components are instantiated in the \l {QQmlEngine::rootContext()}{root context}.
500
501 \sa QQmlComponent, QQmlContext, {QML Global Object}, QQmlApplicationEngine
502*/
503
504/*!
505 Create a new QQmlEngine with the given \a parent.
506*/
507QQmlEngine::QQmlEngine(QObject *parent)
508: QJSEngine(*new QQmlEnginePrivate(this), parent)
509{
510 Q_D(QQmlEngine);
511 d->init();
512 QJSEnginePrivate::addToDebugServer(this);
513}
514
515/*!
516* \internal
517*/
518QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent)
519: QJSEngine(dd, parent)
520{
521 Q_D(QQmlEngine);
522 d->init();
523}
524
525/*!
526 Destroys the QQmlEngine.
527
528 Any QQmlContext's created on this engine will be
529 invalidated, but not destroyed (unless they are parented to the
530 QQmlEngine object).
531
532 See ~QJSEngine() for details on cleaning up the JS engine.
533*/
534QQmlEngine::~QQmlEngine()
535{
536 Q_D(QQmlEngine);
537 handle()->inShutdown = true;
538 QJSEnginePrivate::removeFromDebugServer(this);
539
540 // Emit onDestruction signals for the root context before
541 // we destroy the contexts, engine, Singleton Types etc. that
542 // may be required to handle the destruction signal.
543 QQmlContextPrivate::get(rootContext())->emitDestruction();
544
545 // clean up all singleton type instances which we own.
546 // we do this here and not in the private dtor since otherwise a crash can
547 // occur (if we are the QObject parent of the QObject singleton instance)
548 // XXX TODO: performance -- store list of singleton types separately?
549 d->singletonInstances.clear();
550
551 delete d->rootContext;
552 d->rootContext = nullptr;
553
554 d->typeLoader.invalidate();
555
556 // QQmlGadgetPtrWrapper can have QQmlData with various references.
557 qDeleteAll(d->cachedValueTypeInstances);
558 d->cachedValueTypeInstances.clear();
559}
560
561/*! \fn void QQmlEngine::quit()
562 This signal is emitted when the QML loaded by the engine would like to quit.
563
564 \sa exit()
565 */
566
567/*! \fn void QQmlEngine::exit(int retCode)
568 This signal is emitted when the QML loaded by the engine would like to exit
569 from the event loop with the specified return code \a retCode.
570
571 \since 5.8
572 \sa quit()
573 */
574
575
576/*! \fn void QQmlEngine::warnings(const QList<QQmlError> &warnings)
577 This signal is emitted when \a warnings messages are generated by QML.
578 */
579
580/*!
581 Clears the engine's internal component cache.
582
583 This function causes the property metadata of most components previously
584 loaded by the engine to be destroyed. It does so by dropping unreferenced
585 components from the engine's component cache. It does not drop components that
586 are still referenced since that would almost certainly lead to crashes further
587 down the line.
588
589 If no components are referenced, this function returns the engine to a state
590 where it does not contain any loaded component data. This may be useful in
591 order to reload a smaller subset of the previous component set, or to load a
592 new version of a previously loaded component.
593
594 Once the component cache has been cleared, components must be loaded before
595 any new objects can be created.
596
597 \note Any existing objects created from QML components retain their types,
598 even if you clear the component cache. This includes singleton objects. If you
599 create more objects from the same QML code after clearing the cache, the new
600 objects will be of different types than the old ones. Assigning such a new
601 object to a property of its declared type belonging to an object created
602 before clearing the cache won't work.
603
604 As a general rule of thumb, make sure that no objects created from QML
605 components are alive when you clear the component cache.
606
607 \sa trimComponentCache(), clearSingletons()
608 */
609void QQmlEngine::clearComponentCache()
610{
611 Q_D(QQmlEngine);
612
613 // Contexts can hold on to CUs but live on the JS heap.
614 // Use a non-incremental GC run to get rid of those.
615 QV4::MemoryManager *mm = handle()->memoryManager;
616 auto oldLimit = mm->gcStateMachine->timeLimit;
617 mm->setGCTimeLimit(-1);
618 mm->runGC();
619 mm->gcStateMachine->timeLimit = std::move(oldLimit);
620
621 handle()->trimCompilationUnits();
622 d->typeLoader.clearCache();
623 QQmlMetaType::freeUnusedTypesAndCaches();
624}
625
626/*!
627 Trims the engine's internal component cache.
628
629 This function causes the property metadata of any loaded components which are
630 not currently in use to be destroyed.
631
632 A component is considered to be in use if there are any extant instances of
633 the component itself, any instances of other components that use the component,
634 or any objects instantiated by any of those components.
635
636 \sa clearComponentCache()
637 */
638void QQmlEngine::trimComponentCache()
639{
640 Q_D(QQmlEngine);
641 handle()->trimCompilationUnits();
642 d->typeLoader.trimCache();
643}
644
645/*!
646 Clears all singletons the engine owns.
647
648 This function drops all singleton instances, deleting any QObjects owned by
649 the engine among them. This is useful to make sure that no QML-created objects
650 are left before calling clearComponentCache().
651
652 QML properties holding QObject-based singleton instances become null if the
653 engine owns the singleton or retain their value if the engine doesn't own it.
654 The singletons are not automatically re-created by accessing existing
655 QML-created objects. Only when new components are instantiated, the singletons
656 are re-created.
657
658 \sa clearComponentCache()
659 */
660void QQmlEngine::clearSingletons()
661{
662 Q_D(QQmlEngine);
663 d->singletonInstances.clear();
664}
665
666/*!
667 Returns the engine's root context.
668
669 The root context is automatically created by the QQmlEngine.
670 Data that should be available to all QML component instances
671 instantiated by the engine should be put in the root context.
672
673 Additional data that should only be available to a subset of
674 component instances should be added to sub-contexts parented to the
675 root context.
676*/
677QQmlContext *QQmlEngine::rootContext() const
678{
679 Q_D(const QQmlEngine);
680 return d->rootContext;
681}
682
683#if QT_DEPRECATED_SINCE(6, 0)
684/*!
685 \internal
686 \deprecated
687 This API is private for 5.1
688
689 Returns the last QQmlAbstractUrlInterceptor. It must not be modified outside
690 the GUI thread.
691*/
692QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const
693{
694 Q_D(const QQmlEngine);
695 return d->typeLoader.urlInterceptors().last();
696}
697#endif
698
699/*!
700 Adds a \a urlInterceptor to be used when resolving URLs in QML.
701 This also applies to URLs used for loading script files and QML types.
702 The URL interceptors should not be modifed while the engine is loading files,
703 or URL selection may be inconsistent. Multiple URL interceptors, when given,
704 will be called in the order they were added for each URL.
705
706 QQmlEngine does not take ownership of the interceptor and won't delete it.
707*/
708void QQmlEngine::addUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
709{
710 Q_D(QQmlEngine);
711 d->typeLoader.addUrlInterceptor(urlInterceptor);
712}
713
714/*!
715 Remove a \a urlInterceptor that was previously added using
716 \l addUrlInterceptor. The URL interceptors should not be modifed while the
717 engine is loading files, or URL selection may be inconsistent.
718
719 This does not delete the interceptor, but merely removes it from the engine.
720 You can re-use it on the same or a different engine afterwards.
721*/
722void QQmlEngine::removeUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
723{
724 Q_D(QQmlEngine);
725 d->typeLoader.removeUrlInterceptor(urlInterceptor);
726}
727
728/*!
729 Run the current URL interceptors on the given \a url of the given \a type and
730 return the result.
731 */
732QUrl QQmlEngine::interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataType type) const
733{
734 Q_D(const QQmlEngine);
735 return d->typeLoader.interceptUrl(url, type);
736}
737
738/*!
739 Returns the list of currently active URL interceptors.
740 */
741QList<QQmlAbstractUrlInterceptor *> QQmlEngine::urlInterceptors() const
742{
743 Q_D(const QQmlEngine);
744 return d->typeLoader.urlInterceptors();
745}
746
747QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const
748{
749 const QString providerIdLower = providerId.toLower();
750 QMutexLocker locker(&imageProviderMutex);
751 return imageProviders.value(providerIdLower);
752}
753
754#if QT_CONFIG(qml_network)
755/*!
756 Sets the \a factory to use for creating QNetworkAccessManager(s).
757
758 QNetworkAccessManager is used for all network access by QML. By
759 implementing a factory it is possible to create custom
760 QNetworkAccessManager with specialized caching, proxy and cookie
761 support.
762
763 The factory must be set before executing the engine.
764
765 \note QQmlEngine does not take ownership of the factory.
766*/
767void QQmlEngine::setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *factory)
768{
769 Q_D(QQmlEngine);
770 d->typeLoader.setNetworkAccessManagerFactory(factory);
771}
772
773class QQmlEnginePublicAPIToken {};
774
775/*!
776 Returns the current QQmlNetworkAccessManagerFactory.
777
778 \sa setNetworkAccessManagerFactory()
779*/
780QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const
781{
782 Q_D(const QQmlEngine);
783 return d->typeLoader.networkAccessManagerFactory().get(QQmlEnginePublicAPIToken());
784}
785
786QNetworkAccessManager *QQmlEnginePrivate::getNetworkAccessManager()
787{
788 Q_Q(QQmlEngine);
789 if (!networkAccessManager)
790 networkAccessManager = typeLoader.createNetworkAccessManager(q);
791 return networkAccessManager;
792}
793
794/*!
795 Returns a common QNetworkAccessManager which can be used by any QML
796 type instantiated by this engine.
797
798 If a QQmlNetworkAccessManagerFactory has been set and a
799 QNetworkAccessManager has not yet been created, the
800 QQmlNetworkAccessManagerFactory will be used to create the
801 QNetworkAccessManager; otherwise the returned QNetworkAccessManager
802 will have no proxy or cache set.
803
804 \sa setNetworkAccessManagerFactory()
805*/
806QNetworkAccessManager *QQmlEngine::networkAccessManager() const
807{
808 // ### Qt7: This method is clearly not const since it _creates_ the network access manager.
809 Q_D(const QQmlEngine);
810 return const_cast<QQmlEnginePrivate *>(d)->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 &d->typeLoader, 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();
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 Q_D(QQmlEngine);
1610 d->typeLoader.addImportPath(path);
1611}
1612
1613/*!
1614 Returns the list of directories where the engine searches for
1615 installed modules in a URL-based directory structure.
1616
1617 For example, if \c /opt/MyApp/lib/imports is in the path, then QML that
1618 imports \c com.mycompany.Feature will cause the QQmlEngine to look
1619 in \c /opt/MyApp/lib/imports/com/mycompany/Feature/ for the components
1620 provided by that module. A \c qmldir file is required for defining the
1621 type version mapping and possibly QML extensions plugins.
1622
1623 By default, this list contains the paths mentioned in
1624 \l {QML Import Path}.
1625
1626 \sa addImportPath(), setImportPathList()
1627*/
1628QStringList QQmlEngine::importPathList() const
1629{
1630 Q_D(const QQmlEngine);
1631 return d->typeLoader.importPathList();
1632}
1633
1634/*!
1635 Sets \a paths as the list of directories where the engine searches for
1636 installed modules in a URL-based directory structure.
1637
1638 By default, this list contains the paths mentioned in
1639 \l {QML Import Path}.
1640
1641 \warning Calling setImportPathList does not preserve the default
1642 import paths.
1643
1644 \sa importPathList(), addImportPath()
1645 */
1646void QQmlEngine::setImportPathList(const QStringList &paths)
1647{
1648 Q_D(QQmlEngine);
1649 d->typeLoader.setImportPathList(paths);
1650}
1651
1652
1653/*!
1654 Adds \a path as a directory where the engine searches for
1655 native plugins for imported modules (referenced in the \c qmldir file).
1656
1657 By default, the list contains only \c ., i.e. the engine searches
1658 in the directory of the \c qmldir file itself.
1659
1660 The newly added \a path will be first in the pluginPathList().
1661
1662 \sa setPluginPathList()
1663*/
1664void QQmlEngine::addPluginPath(const QString& path)
1665{
1666 Q_D(QQmlEngine);
1667 d->typeLoader.addPluginPath(path);
1668}
1669
1670/*!
1671 Returns the list of directories where the engine searches for
1672 native plugins for imported modules (referenced in the \c qmldir file).
1673
1674 By default, the list contains only \c ., i.e. the engine searches
1675 in the directory of the \c qmldir file itself.
1676
1677 \sa addPluginPath(), setPluginPathList()
1678*/
1679QStringList QQmlEngine::pluginPathList() const
1680{
1681 Q_D(const QQmlEngine);
1682 return d->typeLoader.pluginPathList();
1683}
1684
1685/*!
1686 Sets the list of directories where the engine searches for
1687 native plugins for imported modules (referenced in the \c qmldir file)
1688 to \a paths.
1689
1690 By default, the list contains only \c ., i.e. the engine searches
1691 in the directory of the \c qmldir file itself.
1692
1693 \sa pluginPathList(), addPluginPath()
1694 */
1695void QQmlEngine::setPluginPathList(const QStringList &paths)
1696{
1697 Q_D(QQmlEngine);
1698 d->typeLoader.setPluginPathList(paths);
1699}
1700
1701#if QT_CONFIG(library)
1702#if QT_DEPRECATED_SINCE(6, 4)
1703/*!
1704 \deprecated [6.4] Import the module from QML with an "import" statement instead.
1705
1706 Imports the plugin named \a filePath with the \a uri provided.
1707 Returns true if the plugin was successfully imported; otherwise returns false.
1708
1709 On failure and if non-null, the \a errors list will have any errors which occurred prepended to it.
1710
1711 The plugin has to be a Qt plugin which implements the QQmlEngineExtensionPlugin interface.
1712
1713 \note Directly loading plugins like this can confuse the module import logic. In order to make
1714 the import logic load plugins from a specific place, you can use \l addPluginPath(). Each
1715 plugin should be part of a QML module that you can import using the "import" statement.
1716*/
1717bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
1718{
1719 Q_D(QQmlEngine);
1720 QQmlTypeLoaderQmldirContent qmldir;
1721 QQmlPluginImporter importer(uri, QTypeRevision(), &qmldir, &d->typeLoader, errors);
1722 return importer.importDynamicPlugin(filePath, uri, false).isValid();
1723}
1724#endif
1725#endif
1726
1727/*!
1728 \property QQmlEngine::offlineStoragePath
1729 \brief the directory for storing offline user data
1730
1731 Returns the directory where SQL and other offline
1732 storage is placed.
1733
1734 The SQL databases created with \c openDatabaseSync() are stored here.
1735
1736 The default is QML/OfflineStorage in the platform-standard
1737 user application data directory.
1738
1739 Note that the path may not currently exist on the filesystem, so
1740 callers wanting to \e create new files at this location should create
1741 it first - see QDir::mkpath().
1742
1743 \sa {Qt Quick Local Storage QML Types}
1744*/
1745
1746/*!
1747 \fn void QQmlEngine::offlineStoragePathChanged()
1748 This signal is emitted when \l offlineStoragePath changes.
1749 \since 6.5
1750*/
1751
1752void QQmlEngine::setOfflineStoragePath(const QString& dir)
1753{
1754 Q_D(QQmlEngine);
1755 if (dir == d->offlineStoragePath)
1756 return;
1757 d->offlineStoragePath = dir;
1758 Q_EMIT offlineStoragePathChanged();
1759}
1760
1761QString QQmlEngine::offlineStoragePath() const
1762{
1763 Q_D(const QQmlEngine);
1764
1765 if (d->offlineStoragePath.isEmpty()) {
1766 QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
1767 QQmlEnginePrivate *e = const_cast<QQmlEnginePrivate *>(d);
1768 if (!dataLocation.isEmpty()) {
1769 e->offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator())
1770 + QDir::separator() + QLatin1String("QML")
1771 + QDir::separator() + QLatin1String("OfflineStorage");
1772 Q_EMIT e->q_func()->offlineStoragePathChanged();
1773 }
1774 }
1775
1776 return d->offlineStoragePath;
1777}
1778
1779/*!
1780 Returns the file path where a \l{QtQuick.LocalStorage}{Local Storage}
1781 database with the identifier \a databaseName is (or would be) located.
1782
1783 \sa {openDatabaseSync}{LocalStorage.openDatabaseSync()}
1784 \since 5.9
1785*/
1786QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) const
1787{
1788 Q_D(const QQmlEngine);
1789 QCryptographicHash md5(QCryptographicHash::Md5);
1790 md5.addData(databaseName.toUtf8());
1791 return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex());
1792}
1793
1794QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
1795{
1796 Q_Q(const QQmlEngine);
1797 return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator();
1798}
1799
1800static bool hasRequiredProperties(const QQmlPropertyCache::ConstPtr &propertyCache)
1801{
1802 bool requiredPropertiesFound = false;
1803 // we don't expect to find any, so the loop has no early termination check
1804 if (propertyCache) {
1805 for (int idx = 0, count = propertyCache->propertyCount(); idx < count; ++idx)
1806 requiredPropertiesFound |= propertyCache->property(idx)->isRequired();
1807 }
1808 return requiredPropertiesFound;
1809}
1810
1811template<>
1812QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
1813{
1814 Q_Q(QQmlEngine);
1815
1816 QQmlType::SingletonInstanceInfo::ConstPtr siinfo = type.singletonInstanceInfo();
1817 Q_ASSERT(siinfo != nullptr);
1818
1819 QJSValue value = singletonInstances.value(siinfo);
1820 if (!value.isUndefined())
1821 return value;
1822
1823 if (siinfo->scriptCallback) {
1824 value = siinfo->scriptCallback(q, q);
1825 if (value.isQObject()) {
1826 QObject *o = value.toQObject();
1827 // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
1828 // should behave identically to QML singleton types.
1829 q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
1830 }
1831 singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
1832
1833 } else if (siinfo->qobjectCallback) {
1834 QObject *o = siinfo->qobjectCallback(q, q);
1835 if (!o) {
1836 QQmlError error;
1837 error.setMessageType(QtMsgType::QtCriticalMsg);
1838 error.setDescription(QString::asprintf("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.",
1839 qPrintable(QString::fromUtf8(type.typeName()))));
1840 warning(error);
1841 } else {
1842 type.createProxy(o);
1843
1844 // if this object can use a property cache, create it now
1845 QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(o);
1846 if (Q_UNLIKELY(hasRequiredProperties(propertyCache))) {
1847 // there's no way to set required properties on a singleton
1848 delete o;
1849 o = nullptr;
1850 QQmlError error;
1851 error.setMessageType(QtMsgType::QtCriticalMsg);
1852 error.setDescription(QString::asprintf("Singleton \"%s\" is not available because the type has unset required properties.",
1853 qPrintable(QString::fromUtf8(type.typeName()))));
1854 warning(error);
1855 } else {
1856 // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
1857 // should behave identically to QML singleton types. You can, however, manually
1858 // assign a context; and clearSingletons() retains the contexts, in which case
1859 // we don't want to see warnings about the object already having a context.
1860 QQmlData *data = QQmlData::get(o, true);
1861 if (!data->context) {
1862 auto contextData = QQmlContextData::get(new QQmlContext(q->rootContext(), q));
1863 data->context = contextData.data();
1864 contextData->addOwnedObject(data);
1865 }
1866 }
1867 }
1868
1869 value = q->newQObject(o);
1870 singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
1871 } else if (!siinfo->url.isEmpty()) {
1872 QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
1873 if (component.isError()) {
1874 warning(component.errors());
1875 v4engine()->throwError(QLatin1String("Due to the preceding error(s), Singleton \"%1\" could not be loaded.").arg(QString::fromUtf8(type.typeName())));
1876
1877 return QJSValue(QJSValue::UndefinedValue);
1878 }
1879 QObject *o = component.beginCreate(q->rootContext());
1880 auto *compPriv = QQmlComponentPrivate::get(&component);
1881 if (compPriv->hasUnsetRequiredProperties()) {
1882 /* We would only get the errors from the component after (complete)Create.
1883 We can't call create, as we need to convertAndInsert before completeCreate (otherwise
1884 tst_qqmllanguage::compositeSingletonCircular fails).
1885 On the other hand, we don't want to call cnovertAndInsert if we have an error
1886 So create the unset required component errors manually.
1887 */
1888 delete o;
1889 const auto requiredProperties = compPriv->requiredProperties();
1890 QList<QQmlError> errors (requiredProperties->size());
1891 for (const auto &reqProp: *requiredProperties)
1892 errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(reqProp));
1893 warning(errors);
1894 v4engine()->throwError(QLatin1String("Due to the preceding error(s), Singleton \"%1\" could not be loaded.").arg(QString::fromUtf8(type.typeName())));
1895 return QJSValue(QJSValue::UndefinedValue);
1896 }
1897
1898 value = q->newQObject(o);
1899 singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
1900 component.completeCreate();
1901 }
1902
1903 return value;
1904}
1905
1906bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const
1907{
1908 return typeLoader.isTypeLoaded(url);
1909}
1910
1911bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const
1912{
1913 return typeLoader.isScriptLoaded(url);
1914}
1915
1916void QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex,
1917 QObject *thisObject, int argc, void **args,
1918 QMetaType *types)
1919{
1920 const auto unit = compilationUnitFromUrl(url);
1921 if (!unit)
1922 return;
1923 executeRuntimeFunction(unit, functionIndex, thisObject, argc, args, types);
1924}
1925
1926void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationUnit *unit,
1927 qsizetype functionIndex, QObject *thisObject,
1928 int argc, void **args, QMetaType *types)
1929{
1930 Q_ASSERT(unit);
1931 Q_ASSERT((functionIndex >= 0) && (functionIndex < unit->runtimeFunctions.size()));
1932 Q_ASSERT(thisObject);
1933
1934 QQmlData *ddata = QQmlData::get(thisObject);
1935 Q_ASSERT(ddata && ddata->outerContext);
1936
1937 QV4::Function *function = unit->runtimeFunctions[functionIndex];
1938 Q_ASSERT(function);
1939 Q_ASSERT(function->compiledFunction);
1940
1941 QV4::ExecutionEngine *v4 = v4engine();
1942
1943 // NB: always use scriptContext() by default as this method ignores whether
1944 // there's already a stack frame (except when dealing with closures). the
1945 // method is called from C++ (through QQmlEngine::executeRuntimeFunction())
1946 // and thus the caller must ensure correct setup
1947 QV4::Scope scope(v4);
1948 QV4::ExecutionContext *ctx = v4->scriptContext();
1949 QV4::Scoped<QV4::ExecutionContext> callContext(scope,
1950 QV4::QmlContext::create(ctx, ddata->outerContext, thisObject));
1951
1952 if (auto nested = function->nestedFunction()) {
1953 // if a nested function is already known, call the closure directly
1954 function = nested;
1955 } else if (function->isClosureWrapper()) {
1956 // if there is a nested function, but we don't know it, we need to call
1957 // an outer function first and then the inner function. we fetch the
1958 // return value of a function call (that is a closure) by calling a
1959 // different version of ExecutionEngine::callInContext() that returns a
1960 // QV4::ReturnedValue with no arguments since they are not needed by the
1961 // outer function anyhow
1962 QV4::Scoped<QV4::JavaScriptFunctionObject> result(scope,
1963 v4->callInContext(function, thisObject, callContext, 0, nullptr));
1964 Q_ASSERT(result->function());
1965 Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
1966
1967 // overwrite the function and its context
1968 function = result->function();
1969 callContext = QV4::Scoped<QV4::ExecutionContext>(scope, result->scope());
1970 }
1971
1972 v4->callInContext(function, thisObject, callContext, argc, args, types);
1973}
1974
1975QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url)
1976{
1977 QV4::ExecutionEngine *v4 = v4engine();
1978 if (auto unit = v4->compilationUnitForUrl(url)) {
1979 if (!unit->runtimeStrings)
1980 unit->populate();
1981 return unit.data();
1982 }
1983
1984 auto unit = typeLoader.getType(url)->compilationUnit();
1985 if (!unit)
1986 return nullptr;
1987
1988 auto executable = v4->executableCompilationUnit(std::move(unit));
1989 executable->populate();
1990 return executable.data();
1991}
1992
1993QQmlRefPointer<QQmlContextData>
1994QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
1995 const QQmlRefPointer<QQmlContextData> &parentContext,
1996 int subComponentIndex, bool isComponentRoot)
1997{
1998 Q_ASSERT(unit);
1999
2000 QQmlRefPointer<QQmlContextData> context;
2001 context = QQmlContextData::createRefCounted(parentContext);
2002 context->setInternal(true);
2003 context->setImports(unit->typeNameCache());
2004 context->initFromTypeCompilationUnit(unit, subComponentIndex);
2005
2006 const auto *dependentScripts = unit->dependentScriptsPtr();
2007 const qsizetype dependentScriptsSize = dependentScripts->size();
2008 if (isComponentRoot && dependentScriptsSize) {
2009 QV4::ExecutionEngine *v4 = v4engine();
2010 Q_ASSERT(v4);
2011 QV4::Scope scope(v4);
2012
2013 QV4::ScopedObject scripts(scope, v4->newArrayObject(dependentScriptsSize));
2014 context->setImportedScripts(v4, scripts);
2015 QV4::ScopedValue v(scope);
2016 for (qsizetype i = 0; i < dependentScriptsSize; ++i)
2017 scripts->put(i, (v = dependentScripts->at(i)->scriptValueForContext(context)));
2018 }
2019
2020 return context;
2021}
2022
2023#if defined(Q_OS_WIN)
2024// Normalize a file name using Shell API. As opposed to converting it
2025// to a short 8.3 name and back, this also works for drives where 8.3 notation
2026// is disabled (see 8dot3name options of fsutil.exe).
2027static inline QString shellNormalizeFileName(const QString &name)
2028{
2029 const QString nativeSeparatorName(QDir::toNativeSeparators(name));
2030 const LPCTSTR nameC = reinterpret_cast<LPCTSTR>(nativeSeparatorName.utf16());
2031// The correct declaration of the SHGetPathFromIDList symbol is
2032// being used in mingw-w64 as of r6215, which is a v3 snapshot.
2033#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3)
2034 ITEMIDLIST *file;
2035 if (FAILED(SHParseDisplayName(nameC, NULL, reinterpret_cast<LPITEMIDLIST>(&file), 0, NULL)))
2036 return name;
2037#else
2038 PIDLIST_ABSOLUTE file;
2039 if (FAILED(SHParseDisplayName(nameC, NULL, &file, 0, NULL)))
2040 return name;
2041#endif
2042 TCHAR buffer[MAX_PATH];
2043 bool gotPath = SHGetPathFromIDList(file, buffer);
2044 ILFree(file);
2045
2046 if (!gotPath)
2047 return name;
2048
2049 QString canonicalName = QString::fromWCharArray(buffer);
2050 // Upper case drive letter
2051 if (canonicalName.size() > 2 && canonicalName.at(1) == QLatin1Char(':'))
2052 canonicalName[0] = canonicalName.at(0).toUpper();
2053 return QDir::cleanPath(canonicalName);
2054}
2055#endif // Q_OS_WIN
2056
2057bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
2058{
2059#if defined(Q_OS_DARWIN) || defined(Q_OS_WIN)
2060 QFileInfo info(fileName);
2061 const QString absolute = info.absoluteFilePath();
2062
2063 // No difference if the path is qrc based
2064 if (absolute[0] == QLatin1Char(':'))
2065 return true;
2066
2067#if defined(Q_OS_DARWIN)
2068 const QString canonical = info.canonicalFilePath();
2069 if (const auto suffix = info.suffix();
2070 (suffix == "qml"_L1 || suffix == "dylib"_L1) && info.exists()) {
2071 // APFS and HFS+ are both case preserving, in which case we can trust that the
2072 // canonical file path reported above is correct. But if any of these file systems
2073 // are mounted via SMB or Virtiofs (Virtualization.framework) macOS will report
2074 // that the canonical file name of "Foo" is "Foo", even if the underlying file
2075 // on the host file system has a canonical name of "foo". As we can't trust the
2076 // canonical name in this case, we go though QDirIterator instead, as the directory
2077 // listing for the mounted filesystem _does_ report the correct canonical file name.
2078 if (pathconf(canonical.toUtf8().constData(), _PC_CASE_PRESERVING) != 1) {
2079 qCDebug(lcQmlImport) << "Detected QML file on non-case-preserving file system"
2080 << QStorageInfo(canonical) << "Verifying file case via directory listing";
2081 QDirIterator dirIterator(info.absolutePath(), { info.fileName() },
2082 QDir::Files | QDir::CaseSensitive, QDirIterator::FollowSymlinks);
2083 if (!dirIterator.hasNext())
2084 return false;
2085 }
2086 }
2087#elif defined(Q_OS_WIN)
2088 const QString canonical = shellNormalizeFileName(absolute);
2089#endif
2090
2091 const int absoluteLength = absolute.length();
2092 const int canonicalLength = canonical.length();
2093
2094 int length = qMin(absoluteLength, canonicalLength);
2095 if (lengthIn >= 0) {
2096 length = qMin(lengthIn, length);
2097 } else {
2098 // No length given: Limit to file name. Do not trigger
2099 // on drive letters or folder names.
2100 int lastSlash = absolute.lastIndexOf(QLatin1Char('/'));
2101 if (lastSlash < 0)
2102 lastSlash = absolute.lastIndexOf(QLatin1Char('\\'));
2103 if (lastSlash >= 0) {
2104 const int fileNameLength = absoluteLength - 1 - lastSlash;
2105 length = qMin(length, fileNameLength);
2106 }
2107 }
2108
2109 for (int ii = 0; ii < length; ++ii) {
2110 const QChar &a = absolute.at(absoluteLength - 1 - ii);
2111 const QChar &c = canonical.at(canonicalLength - 1 - ii);
2112
2113 if (a.toLower() != c.toLower())
2114 return true;
2115 if (a != c)
2116 return false;
2117 }
2118#else
2119 Q_UNUSED(lengthIn);
2120 Q_UNUSED(fileName);
2121#endif
2122 return true;
2123}
2124
2125/*!
2126 \fn QQmlEngine *qmlEngine(const QObject *object)
2127 \relates QQmlEngine
2128
2129 Returns the QQmlEngine associated with \a object, if any. This is equivalent to
2130 QQmlEngine::contextForObject(object)->engine(), but more efficient.
2131
2132 \note Add \c{#include <QtQml>} to use this function.
2133
2134 \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlContext()
2135*/
2136
2137/*!
2138 \fn QQmlContext *qmlContext(const QObject *object)
2139 \relates QQmlEngine
2140
2141 Returns the QQmlContext associated with \a object, if any. This is equivalent to
2142 QQmlEngine::contextForObject(object).
2143
2144 \note Add \c{#include <QtQml>} to use this function.
2145
2146 \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine()
2147*/
2148
2149void hasJsOwnershipIndicator(QQmlGuardImpl *) {};
2150
2151LoadHelper::LoadHelper(
2152 QQmlTypeLoader *loader, QAnyStringView uri, QAnyStringView typeName,
2153 QQmlTypeLoader::Mode mode)
2155 , m_uri(uri.toString())
2157 , m_mode(mode)
2158{
2159 m_typeLoader->loadWithStaticData(this, QByteArray(), m_mode);
2160}
2161
2162void LoadHelper::registerCallback(QQmlComponentPrivate *callback)
2163{
2164 m_callback = callback;
2165}
2166
2167void LoadHelper::unregisterCallback(QQmlComponentPrivate *callback)
2168{
2169 if (m_callback) {
2170 Q_ASSERT(callback == m_callback);
2171 m_callback = nullptr;
2172 }
2173}
2174
2175void LoadHelper::done()
2176{
2177 if (!couldFindModule()) {
2178 m_resolveTypeResult = ResolveTypeResult::NoSuchModule;
2179 return;
2180 }
2181
2182 QQmlTypeModule *module = QQmlMetaType::typeModule(m_uri, QTypeRevision{});
2183 if (module) {
2184 m_type = module->type(m_typeName, {});
2185 if (m_type.isValid()) {
2186 m_resolveTypeResult = ResolveTypeResult::ModuleFound;
2187 return;
2188 }
2189 }
2190
2191 // The module exists (see check above), but there is no QQmlTypeModule
2192 // ==> pure QML module, attempt resolveType
2193 QTypeRevision versionReturn;
2194 QList<QQmlError> errors;
2195 QQmlImportNamespace *ns_return = nullptr;
2196 m_importCache->resolveType(
2197 typeLoader(), m_typeName, &m_type, &versionReturn, &ns_return, &errors);
2198 m_resolveTypeResult = ResolveTypeResult::ModuleFound;
2199}
2200
2201void LoadHelper::completed()
2202{
2203 QQmlTypeLoader::Blob::completed();
2204
2205 if (m_callback) {
2206 m_callback->completeLoadFromModule(m_uri, m_typeName);
2207 m_callback = nullptr;
2208 }
2209}
2210
2211void LoadHelper::dataReceived(const SourceCodeData &)
2212{
2213 auto import = std::make_shared<PendingImport>();
2214 import->uri = m_uri;
2215 QList<QQmlError> errorList;
2216 if (!Blob::addImport(import, &errorList)) {
2217 qCDebug(lcQmlImport) << "LoadHelper: Errors loading " << m_uri << errorList;
2218 m_uri.clear(); // reset m_uri to remember the failure
2219 }
2220}
2221
2222bool LoadHelper::couldFindModule() const
2223{
2224 if (m_uri.isEmpty())
2225 return false;
2226 for (const auto &import: std::as_const(m_unresolvedImports))
2227 if (import->priority == 0) // compare QQmlTypeData::allDependenciesDone
2228 return false;
2229 return true;
2230}
2231
2232QT_END_NAMESPACE
2233
2234#include "moc_qqmlengine.cpp"
QHash< QQmlAttachedPropertiesFunc, QObject * > attachedProperties
QPointer< QObject > target
int qt_metacall(QMetaObject::Call, int methodIndex, void **a) override
Combined button and popup list for selecting options.
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)
bool QQml_isFileCaseCorrect(const QString &fileName, int length=-1)
Returns true if the case of fileName is equivalent to the file case of fileName on disk,...
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...