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// Qt-Security score:significant
4
5#include "qqmlengine_p.h"
6#include "qqmlengine.h"
7
8#include <private/qqmlcontext_p.h>
9#include <private/qqmlpluginimporter_p.h>
10#include <private/qqmlprofiler_p.h>
11#include <private/qqmlscriptdata_p.h>
12#include <private/qqmlsourcecoordinate_p.h>
13#include <private/qqmltype_p.h>
14#include <private/qqmltypedata_p.h>
15#include <private/qqmlvmemetaobject_p.h>
16#include <private/qqmlcomponent_p.h>
17
18#include <QtQml/qqml.h>
19#include <QtQml/qqmlcomponent.h>
20#include <QtQml/qqmlcontext.h>
21#include <QtQml/qqmlincubator.h>
22#include <QtQml/qqmlscriptstring.h>
23
24#include <QtCore/qcoreapplication.h>
25#include <QtCore/qcryptographichash.h>
26#include <QtCore/qdir.h>
27#include <QtCore/qdiriterator.h>
28#include <QtCore/qmetaobject.h>
29#include <QtCore/qmutex.h>
30#include <QtCore/qstandardpaths.h>
31#include <QtCore/qstorageinfo.h>
32#include <QtCore/qthread.h>
33
34#if QT_CONFIG(qml_network)
35#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
36#include <QtNetwork/qnetworkaccessmanager.h>
37#endif
38
39#ifdef Q_OS_WIN // for %APPDATA%
40# include <qt_windows.h>
41# include <shlobj.h>
42# include <QtCore/qlibrary.h>
43# ifndef CSIDL_APPDATA
44# define CSIDL_APPDATA 0x001a // <username>\Application Data
45# endif
46#endif // Q_OS_WIN
47
48#ifdef Q_OS_DARWIN
49# include <unistd.h>
50#endif
51
53
54using namespace Qt::StringLiterals;
55
57
58/*!
59 \qmltype QtObject
60 \nativetype QObject
61 \inqmlmodule QtQml
62 \ingroup qml-utility-elements
63 \brief A basic QML type.
64
65 The QtObject type is a non-visual element which contains only the
66 objectName property.
67
68 It can be useful to create a QtObject if you need an extremely
69 lightweight type to enclose a set of custom properties:
70
71 \snippet qml/qtobject.qml 0
72
73 It can also be useful for C++ integration, as it is just a plain
74 QObject. See the QObject documentation for further details.
75*/
76/*!
77 \qmlproperty string QtObject::objectName
78 This property holds the QObject::objectName for this specific object instance.
79
80 This allows a C++ application to locate an item within a QML component
81 using the QObject::findChild() method. For example, the following C++
82 application locates the child \l Rectangle item and dynamically changes its
83 \c color value:
84
85 \qml
86 // MyRect.qml
87
88 import QtQuick 2.0
89
90 Item {
91 width: 200; height: 200
92
93 Rectangle {
94 anchors.fill: parent
95 color: "red"
96 objectName: "myRect"
97 }
98 }
99 \endqml
100
101 \code
102 // main.cpp
103
104 QQuickView view;
105 view.setSource(QUrl::fromLocalFile("MyRect.qml"));
106 view.show();
107
108 QQuickItem *item = view.rootObject()->findChild<QQuickItem*>("myRect");
109 if (item)
110 item->setProperty("color", QColor(Qt::yellow));
111 \endcode
112*/
113
114Q_CONSTINIT std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
115bool QQmlEnginePrivate::s_designerMode = false;
116
117bool QQmlEnginePrivate::designerMode()
118{
119 return s_designerMode;
120}
121
122void QQmlEnginePrivate::activateDesignerMode()
123{
124 s_designerMode = true;
125}
126
127
128/*!
129 \class QQmlImageProviderBase
130 \brief The QQmlImageProviderBase class is used to register image providers in the QML engine.
131 \inmodule QtQml
132
133 Image providers must be registered with the QML engine. The only information the QML
134 engine knows about image providers is the type of image data they provide. To use an
135 image provider to acquire image data, you must cast the QQmlImageProviderBase pointer
136 to a QQuickImageProvider pointer.
137
138 \sa QQuickImageProvider, QQuickTextureFactory
139*/
140
141/*!
142 \enum QQmlImageProviderBase::ImageType
143
144 Defines the type of image supported by this image provider.
145
146 \value Image The Image Provider provides QImage images.
147 The QQuickImageProvider::requestImage() method will be called for all image requests.
148 \value Pixmap The Image Provider provides QPixmap images.
149 The QQuickImageProvider::requestPixmap() method will be called for all image requests.
150 \value Texture The Image Provider provides QSGTextureProvider based images.
151 The QQuickImageProvider::requestTexture() method will be called for all image requests.
152 \value ImageResponse The Image provider provides QQuickTextureFactory based images.
153 Should only be used in QQuickAsyncImageProvider or its subclasses.
154 The QQuickAsyncImageProvider::requestImageResponse() method will be called for all image requests.
155 Since Qt 5.6
156 \omitvalue Invalid
157*/
158
159/*!
160 \enum QQmlImageProviderBase::Flag
161
162 Defines specific requirements or features of this image provider.
163
164 \value ForceAsynchronousImageLoading Ensures that image requests to the provider are
165 run in a separate thread, which allows the provider to spend as much time as needed
166 on producing the image without blocking the main thread.
167*/
168
169/*!
170 \fn QQmlImageProviderBase::imageType() const
171
172 Implement this method to return the image type supported by this image provider.
173*/
174
175/*!
176 \fn QQmlImageProviderBase::flags() const
177
178 Implement this to return the properties of this image provider.
179*/
180
181/*! \internal */
182QQmlImageProviderBase::QQmlImageProviderBase()
183{
184}
185
186/*! \internal */
187QQmlImageProviderBase::~QQmlImageProviderBase()
188{
189}
190
191QQmlEnginePrivate::~QQmlEnginePrivate()
192{
193 if (inProgressCreations)
194 qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations);
195
196 if (incubationController) incubationController->d = nullptr;
197 incubationController = nullptr;
198
199#if QT_CONFIG(qml_debug)
200 delete profiler;
201#endif
202}
203
204void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
205{
206 QObjectPrivate *p = QObjectPrivate::get(o);
207 if (QQmlData *d = QQmlData::get(p)) {
208 const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
209 if (d->ownContext) {
210 d->ownContext->deepClearContextObject(o, invalidate, invalidate);
211 d->ownContext.reset();
212 d->context = nullptr;
213 Q_ASSERT(!d->outerContext || d->outerContext->contextObject() != o);
214 } else if (d->outerContext && d->outerContext->contextObject() == o) {
215 d->outerContext->deepClearContextObject(o, invalidate, invalidate);
216 }
217
218 if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
219 // This is somewhat dangerous because another thread might concurrently
220 // try to resolve the dynamic metaobject. In practice this will then
221 // lead to either the code path that still returns the interceptor
222 // metaobject or the code path that returns the string casted one. Both
223 // is fine if you cannot actually touch the object itself. Since the
224 // other thread is obviously not synchronized to this one, it can't.
225 //
226 // In particular we do this when delivering the frameSwapped() signal
227 // in QQuickWindow. The handler for frameSwapped() is written in a way
228 // that is thread safe as long as QQuickWindow's dtor hasn't finished.
229 // QQuickWindow's dtor does synchronize with the render thread, but it
230 // runs _after_ qdeclarativeelement_destructor.
231 static_cast<QQmlInterceptorMetaObject *>(p->metaObject)->invalidate();
232 d->hasVMEMetaObject = d->hasInterceptorMetaObject = false;
233 }
234
235 // Mark this object as in the process of deletion to
236 // prevent it resolving in bindings
237 QQmlData::markAsDeleted(o);
238 }
239}
240
241template<>
242int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
243{
244 QQmlPrivate::RegisterType type = {
245 QQmlPrivate::RegisterType::CurrentVersion,
246 QMetaType(),
247 QMetaType(),
248 0, nullptr, nullptr,
249 QString(),
250 nullptr,
251 uri,
252 QTypeRevision::fromVersion(versionMajor, versionMinor),
253 qmlName,
254 nullptr,
255 nullptr,
256 nullptr,
257 -1,
258 -1,
259 -1,
260 nullptr,
261 nullptr,
262 nullptr,
263 QTypeRevision::zero(),
264 -1,
265 QQmlPrivate::ValueTypeCreationMethod::None,
266 };
267
268 return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
269}
270
271bool QQmlEnginePrivate::baseModulesUninitialized = true;
272void QQmlEnginePrivate::init()
273{
274 Q_Q(QQmlEngine);
275
276 if (baseModulesUninitialized) {
277 // Register builtins
278 qml_register_types_QML();
279
280 // No need to specifically register those.
281 static_assert(std::is_same_v<QStringList, QList<QString>>);
282 static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
283
284 qRegisterMetaType<QQmlScriptString>();
285 qRegisterMetaType<QQmlComponent::Status>();
286 qRegisterMetaType<QList<QObject*> >();
287 qRegisterMetaType<QQmlBinding*>();
288
289 // Protect the module: We don't want any URL interceptor to mess with the builtins.
290 qmlProtectModule("QML", 1);
291
292 QQmlData::init();
293 baseModulesUninitialized = false;
294 }
295
296 q->handle()->setQmlEngine(q);
297
298 rootContext = new QQmlContext(q,true);
299}
300
301/*!
302 \class QQmlEngine
303 \since 5.0
304 \inmodule QtQml
305 \brief The QQmlEngine class provides an environment for instantiating QML components.
306
307 A QQmlEngine is used to manage \l{QQmlComponent}{components} and objects created from
308 them and execute their bindings and functions. QQmlEngine also inherits from
309 \l{QJSEngine} which allows seamless integration between your QML components and
310 JavaScript code.
311
312 Each QML component is instantiated in a QQmlContext. In QML, contexts are arranged
313 hierarchically and this hierarchy is managed by the QQmlEngine. By default,
314 components are instantiated in the \l {QQmlEngine::rootContext()}{root context}.
315
316 \sa QQmlComponent, QQmlContext, {QML Global Object}, QQmlApplicationEngine
317*/
318
319/*!
320 Create a new QQmlEngine with the given \a parent.
321*/
322QQmlEngine::QQmlEngine(QObject *parent)
323: QJSEngine(*new QQmlEnginePrivate, parent)
324{
325 Q_D(QQmlEngine);
326 d->init();
327 QJSEnginePrivate::addToDebugServer(this);
328}
329
330/*!
331* \internal
332*/
333QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent)
334: QJSEngine(dd, parent)
335{
336 Q_D(QQmlEngine);
337 d->init();
338}
339
340/*!
341 Destroys the QQmlEngine.
342
343 Any QQmlContext's created on this engine will be
344 invalidated, but not destroyed (unless they are parented to the
345 QQmlEngine object).
346
347 See ~QJSEngine() for details on cleaning up the JS engine.
348*/
349QQmlEngine::~QQmlEngine()
350{
351 Q_D(QQmlEngine);
352
353#if QT_CONFIG(qml_worker_script)
354 // Delete the workerscript engine early
355 // so that it won't be able to use the type loader anymore.
356 delete std::exchange(d->workerScriptEngine, nullptr);
357#endif
358
359 QV4::ExecutionEngine *v4 = handle();
360 v4->inShutdown = true;
361 QJSEnginePrivate::removeFromDebugServer(this);
362
363 // Emit onDestruction signals for the root context before
364 // we destroy the contexts, engine, Singleton Types etc. that
365 // may be required to handle the destruction signal.
366 QQmlContextPrivate::get(rootContext())->emitDestruction();
367
368 // clean up all singleton type instances which we own.
369 // we do this here and not in the private dtor since otherwise a crash can
370 // occur (if we are the QObject parent of the QObject singleton instance)
371 // XXX TODO: performance -- store list of singleton types separately?
372 d->singletonInstances.clear();
373
374 delete d->rootContext;
375 d->rootContext = nullptr;
376
377 v4->typeLoader()->invalidate();
378
379 // QQmlGadgetPtrWrapper can have QQmlData with various references.
380 qDeleteAll(d->cachedValueTypeInstances);
381 d->cachedValueTypeInstances.clear();
382
383 v4->resetQmlEngine();
384}
385
386/*! \fn void QQmlEngine::quit()
387 This signal is emitted when the QML loaded by the engine would like to quit.
388
389 \sa exit()
390 */
391
392/*! \fn void QQmlEngine::exit(int retCode)
393 This signal is emitted when the QML loaded by the engine would like to exit
394 from the event loop with the specified return code \a retCode.
395
396 \since 5.8
397 \sa quit()
398 */
399
400
401/*! \fn void QQmlEngine::warnings(const QList<QQmlError> &warnings)
402 This signal is emitted when \a warnings messages are generated by QML.
403 */
404
405/*!
406 Clears the engine's internal component cache.
407
408 This function causes the property metadata of most components previously
409 loaded by the engine to be destroyed. It does so by dropping unreferenced
410 components from the engine's component cache. It does not drop components that
411 are still referenced since that would almost certainly lead to crashes further
412 down the line.
413
414 If no components are referenced, this function returns the engine to a state
415 where it does not contain any loaded component data. This may be useful in
416 order to reload a smaller subset of the previous component set, or to load a
417 new version of a previously loaded component.
418
419 Once the component cache has been cleared, components must be loaded before
420 any new objects can be created.
421
422 \note Any existing objects created from QML components retain their types,
423 even if you clear the component cache. This includes singleton objects. If you
424 create more objects from the same QML code after clearing the cache, the new
425 objects will be of different types than the old ones. Assigning such a new
426 object to a property of its declared type belonging to an object created
427 before clearing the cache won't work.
428
429 As a general rule of thumb, make sure that no objects created from QML
430 components are alive when you clear the component cache.
431
432 \sa trimComponentCache(), clearSingletons()
433 */
434void QQmlEngine::clearComponentCache()
435{
436 Q_D(QQmlEngine);
437
438 // QQmlGadgetPtrWrapper can have QQmlData with various references.
439 qDeleteAll(std::exchange(d->cachedValueTypeInstances, {}));
440
441 QV4::ExecutionEngine *v4 = handle();
442
443 // Reset the values of JavaScript libraries and ECMAScript modules
444 // So that they get re-evaluated on next usage.
445 {
446 const auto cus = v4->compilationUnits();
447 for (const auto &cu : cus) {
448 cu->setValue(QV4::Value::emptyValue());
449 delete[] std::exchange(cu->imports, nullptr);
450 }
451 }
452
453 // Contexts can hold on to CUs but live on the JS heap.
454 // Use a non-incremental GC run to get rid of those.
455 QV4::MemoryManager *mm = v4->memoryManager;
456 auto oldLimit = mm->gcStateMachine->timeLimit;
457 mm->setGCTimeLimit(-1);
458 mm->runGC();
459 mm->gcStateMachine->timeLimit = std::move(oldLimit);
460
461 v4->trimCompilationUnits();
462 v4->typeLoader()->clearCache();
463 QQmlMetaType::freeUnusedTypesAndCaches();
464}
465
466/*!
467 Trims the engine's internal component cache.
468
469 This function causes the property metadata of any loaded components which are
470 not currently in use to be destroyed.
471
472 A component is considered to be in use if there are any extant instances of
473 the component itself, any instances of other components that use the component,
474 or any objects instantiated by any of those components.
475
476 \sa clearComponentCache()
477 */
478void QQmlEngine::trimComponentCache()
479{
480 QV4::ExecutionEngine *v4 = handle();
481 v4->trimCompilationUnits();
482 v4->typeLoader()->trimCache();
483}
484
485/*!
486 Clears all singletons the engine owns.
487
488 This function drops all singleton instances, deleting any QObjects owned by
489 the engine among them. This is useful to make sure that no QML-created objects
490 are left before calling clearComponentCache().
491
492 QML properties holding QObject-based singleton instances become null if the
493 engine owns the singleton or retain their value if the engine doesn't own it.
494 The singletons are not automatically re-created by accessing existing
495 QML-created objects. Only when new components are instantiated, the singletons
496 are re-created.
497
498 \sa clearComponentCache()
499 */
500void QQmlEngine::clearSingletons()
501{
502 Q_D(QQmlEngine);
503 d->singletonInstances.clear();
504}
505
506/*!
507 Returns the engine's root context.
508
509 The root context is automatically created by the QQmlEngine.
510 Data that should be available to all QML component instances
511 instantiated by the engine should be put in the root context.
512
513 Additional data that should only be available to a subset of
514 component instances should be added to sub-contexts parented to the
515 root context.
516*/
517QQmlContext *QQmlEngine::rootContext() const
518{
519 Q_D(const QQmlEngine);
520 return d->rootContext;
521}
522
523#if QT_DEPRECATED_SINCE(6, 0)
524/*!
525 \internal
526 \deprecated
527 This API is private for 5.1
528
529 Returns the last QQmlAbstractUrlInterceptor. It must not be modified outside
530 the GUI thread.
531*/
532QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const
533{
534 return QQmlTypeLoader::get(this)->urlInterceptors().last();
535}
536#endif
537
538/*!
539 Adds a \a urlInterceptor to be used when resolving URLs in QML.
540 This also applies to URLs used for loading script files and QML types.
541 The URL interceptors should not be modifed while the engine is loading files,
542 or URL selection may be inconsistent. Multiple URL interceptors, when given,
543 will be called in the order they were added for each URL.
544
545 QQmlEngine does not take ownership of the interceptor and won't delete it.
546*/
547void QQmlEngine::addUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
548{
549 QQmlTypeLoader::get(this)->addUrlInterceptor(urlInterceptor);
550}
551
552/*!
553 Remove a \a urlInterceptor that was previously added using
554 \l addUrlInterceptor. The URL interceptors should not be modifed while the
555 engine is loading files, or URL selection may be inconsistent.
556
557 This does not delete the interceptor, but merely removes it from the engine.
558 You can re-use it on the same or a different engine afterwards.
559*/
560void QQmlEngine::removeUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
561{
562 QQmlTypeLoader::get(this)->removeUrlInterceptor(urlInterceptor);
563}
564
565/*!
566 Run the current URL interceptors on the given \a url of the given \a type and
567 return the result.
568 */
569QUrl QQmlEngine::interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataType type) const
570{
571 return QQmlTypeLoader::get(this)->interceptUrl(url, type);
572}
573
574/*!
575 Returns the list of currently active URL interceptors.
576 */
577QList<QQmlAbstractUrlInterceptor *> QQmlEngine::urlInterceptors() const
578{
579 return QQmlTypeLoader::get(this)->urlInterceptors();
580}
581
582QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const
583{
584 const QString providerIdLower = providerId.toLower();
585 QMutexLocker locker(&imageProviderMutex);
586 return imageProviders.value(providerIdLower);
587}
588
589#if QT_CONFIG(qml_network)
590/*!
591 Sets the \a factory to use for creating QNetworkAccessManager(s).
592
593 QNetworkAccessManager is used for all network access by QML. By
594 implementing a factory it is possible to create custom
595 QNetworkAccessManager with specialized caching, proxy and cookie
596 support.
597
598 The factory must be set before executing the engine.
599
600 \note QQmlEngine does not take ownership of the factory.
601*/
602void QQmlEngine::setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *factory)
603{
604 QQmlTypeLoader::get(this)->setNetworkAccessManagerFactory(factory);
605}
606
607class QQmlEnginePublicAPIToken {};
608
609/*!
610 Returns the current QQmlNetworkAccessManagerFactory.
611
612 \sa setNetworkAccessManagerFactory()
613*/
614QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const
615{
616 return QQmlTypeLoader::get(this)->networkAccessManagerFactory().get(QQmlEnginePublicAPIToken());
617}
618
619/*!
620 Returns a common QNetworkAccessManager which can be used by any QML
621 type instantiated by this engine.
622
623 If a QQmlNetworkAccessManagerFactory has been set and a
624 QNetworkAccessManager has not yet been created, the
625 QQmlNetworkAccessManagerFactory will be used to create the
626 QNetworkAccessManager; otherwise the returned QNetworkAccessManager
627 will have no proxy or cache set.
628
629 \sa setNetworkAccessManagerFactory()
630*/
631QNetworkAccessManager *QQmlEngine::networkAccessManager() const
632{
633 return handle()->getNetworkAccessManager();
634}
635#endif // qml_network
636
637/*!
638
639 Sets the \a provider to use for images requested via the \e
640 image: url scheme, with host \a providerId. The QQmlEngine
641 takes ownership of \a provider.
642
643 Image providers enable support for pixmap and threaded image
644 requests. See the QQuickImageProvider documentation for details on
645 implementing and using image providers.
646
647 All required image providers should be added to the engine before any
648 QML sources files are loaded.
649
650 \sa removeImageProvider(), QQuickImageProvider, QQmlImageProviderBase
651*/
652void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBase *provider)
653{
654 Q_D(QQmlEngine);
655 QString providerIdLower = providerId.toLower();
656 QSharedPointer<QQmlImageProviderBase> sp(provider);
657 QMutexLocker locker(&d->imageProviderMutex);
658 d->imageProviders.insert(std::move(providerIdLower), std::move(sp));
659}
660
661/*!
662 Returns the image provider set for \a providerId if found; otherwise returns \nullptr.
663
664 \sa QQuickImageProvider
665*/
666QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) const
667{
668 Q_D(const QQmlEngine);
669 const QString providerIdLower = providerId.toLower();
670 QMutexLocker locker(&d->imageProviderMutex);
671 return d->imageProviders.value(providerIdLower).data();
672}
673
674/*!
675 Removes the image provider for \a providerId.
676
677 \sa addImageProvider(), QQuickImageProvider
678*/
679void QQmlEngine::removeImageProvider(const QString &providerId)
680{
681 Q_D(QQmlEngine);
682 const QString providerIdLower = providerId.toLower();
683 QMutexLocker locker(&d->imageProviderMutex);
684 d->imageProviders.take(providerIdLower);
685}
686
687/*!
688 Return the base URL for this engine. The base URL is only used to
689 resolve components when a relative URL is passed to the
690 QQmlComponent constructor.
691
692 If a base URL has not been explicitly set, this method returns the
693 application's current working directory.
694
695 \sa setBaseUrl()
696*/
697QUrl QQmlEngine::baseUrl() const
698{
699 Q_D(const QQmlEngine);
700 if (d->baseUrl.isEmpty()) {
701 const QString currentPath = QDir::currentPath();
702 const QString rootPath = QDir::rootPath();
703 return QUrl::fromLocalFile((currentPath == rootPath) ? rootPath : (currentPath + QDir::separator()));
704 } else {
705 return d->baseUrl;
706 }
707}
708
709/*!
710 Set the base URL for this engine to \a url.
711
712 \sa baseUrl()
713*/
714void QQmlEngine::setBaseUrl(const QUrl &url)
715{
716 Q_D(QQmlEngine);
717 d->baseUrl = url;
718}
719
720/*!
721 Returns true if warning messages will be output to stderr in addition
722 to being emitted by the warnings() signal, otherwise false.
723
724 The default value is true.
725*/
726bool QQmlEngine::outputWarningsToStandardError() const
727{
728 Q_D(const QQmlEngine);
729 return d->outputWarningsToMsgLog;
730}
731
732/*!
733 Set whether warning messages will be output to stderr to \a enabled.
734
735 If \a enabled is true, any warning messages generated by QML will be
736 output to stderr and emitted by the warnings() signal. If \a enabled
737 is false, only the warnings() signal will be emitted. This allows
738 applications to handle warning output themselves.
739
740 The default value is true.
741*/
742void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
743{
744 Q_D(QQmlEngine);
745 d->outputWarningsToMsgLog = enabled;
746}
747
748
749/*!
750 \since 6.6
751 If this method is called inside of a function that is part of
752 a binding in QML, the binding will be treated as a translation binding.
753
754 \code
755 class I18nAwareClass : public QObject {
756
757 //...
758
759 QString text() const
760 {
761 if (auto engine = qmlEngine(this))
762 engine->markCurrentFunctionAsTranslationBinding();
763 return tr("Hello, world!");
764 }
765 };
766 \endcode
767
768 \note This function is mostly useful if you wish to provide your
769 own alternative to the qsTr function. To ensure that properties
770 exposed from C++ classes are updated on language changes, it is
771 instead recommended to react to \c LanguageChange events. That
772 is a more general mechanism which also works when the class is
773 used in a non-QML context, and has slightly less overhead. However,
774 using \c markCurrentFunctionAsTranslationBinding can be acceptable
775 when the class is already closely tied to the QML engine.
776 For more details, see \l {Prepare for Dynamic Language Changes}
777
778 \sa QQmlEngine::retranslate
779*/
780void QQmlEngine::markCurrentFunctionAsTranslationBinding()
781{
782 Q_D(QQmlEngine);
783 if (auto propertyCapture = d->propertyCapture)
784 propertyCapture->captureTranslation();
785}
786
787/*!
788 \internal
789
790 Capture the given property as part of a binding.
791 */
792void QQmlEngine::captureProperty(QObject *object, const QMetaProperty &property) const
793{
794 Q_D(const QQmlEngine);
795 if (d->propertyCapture && !property.isConstant()) {
796 d->propertyCapture->captureProperty(
797 object, property.propertyIndex(),
798 QMetaObjectPrivate::signalIndex(property.notifySignal()));
799 }
800}
801
802/*!
803 \qmlproperty string Qt::uiLanguage
804 \since 5.15
805
806 The uiLanguage holds the name of the language to be used for user interface
807 string translations. It is exposed in C++ as \l QJSEngine::uiLanguage property.
808
809 You can set the value freely and use it in bindings. It is recommended to set it
810 after installing translators in your application. By convention, an empty string
811 means no translation from the language used in the source code is intended to occur.
812
813 If you're using QQmlApplicationEngine and the value changes, QQmlEngine::retranslate()
814 will be called.
815*/
816
817/*!
818 \fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId)
819
820 Returns the instance of a singleton type that was registered under \a qmlTypeId.
821
822 The template argument \e T may be either QJSValue or a pointer to a QObject-derived
823 type and depends on how the singleton was registered. If no instance of \e T has been
824 created yet, it is created now. If \a qmlTypeId does not represent a valid singleton
825 type, either a default constructed QJSValue or a \c nullptr is returned.
826
827 QObject* example:
828
829 \snippet code/src_qml_qqmlengine.cpp 0
830 \codeline
831 \snippet code/src_qml_qqmlengine.cpp 1
832 \codeline
833 \snippet code/src_qml_qqmlengine.cpp 2
834
835 QJSValue example:
836
837 \snippet code/src_qml_qqmlengine.cpp 3
838 \codeline
839 \snippet code/src_qml_qqmlengine.cpp 4
840
841 It is recommended to store the QML type id, e.g. as a static member in the
842 singleton class. The lookup via qmlTypeId() is costly.
843
844 \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId()
845 \since 5.12
846*/
847template<>
848QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
849{
850 Q_D(QQmlEngine);
851 QQmlType type = QQmlMetaType::qmlTypeById(qmlTypeId);
852
853 if (!type.isValid() || !type.isSingleton())
854 return QJSValue();
855
856 return d->singletonInstance<QJSValue>(type);
857}
858
859
860/*!
861 \fn template<typename T> T QQmlEngine::singletonInstance(QAnyStringView uri, QAnyStringView typeName)
862
863 \overload
864 Returns the instance of a singleton type named \a typeName from the module specified by \a uri.
865
866 This method can be used as an alternative to calling qmlTypeId followed by the id based overload of
867 singletonInstance. This is convenient when one only needs to do a one time setup of a
868 singleton; if repeated access to the singleton is required, caching its typeId will allow
869 faster subsequent access via the
870 \l {QQmlEngine::singletonInstance(int qmlTypeId)}{type-id based overload}.
871
872 The template argument \e T may be either QJSValue or a pointer to a QObject-derived
873 type and depends on how the singleton was registered. If no instance of \e T has been
874 created yet, it is created now. If \a typeName does not represent a valid singleton
875 type, either a default constructed QJSValue or a \c nullptr is returned.
876
877 \snippet code/src_qml_qqmlengine.cpp 5
878
879 \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId()
880 \since 6.5
881*/
882template<>
883QJSValue QQmlEngine::singletonInstance<QJSValue>(QAnyStringView uri, QAnyStringView typeName)
884{
885 Q_D(QQmlEngine);
886
887 auto loadHelper = QQml::makeRefPointer<LoadHelper>(
888 QQmlTypeLoader::get(this), uri, typeName, QQmlTypeLoader::Synchronous);
889 const QQmlType type = loadHelper->type();
890
891 if (!type.isSingleton())
892 return {};
893
894 return d->singletonInstance<QJSValue>(type);
895}
896
897/*!
898 Refreshes all binding expressions that use strings marked for translation.
899
900 Call this function after you have installed a new translator with
901 QCoreApplication::installTranslator, to ensure that your user-interface
902 shows up-to-date translations.
903
904 \since 5.10
905*/
906void QQmlEngine::retranslate()
907{
908 Q_D(QQmlEngine);
909 d->translationLanguage.notify();
910}
911
912/*!
913 Returns the QQmlContext for the \a object, or nullptr if no
914 context has been set.
915
916 When the QQmlEngine instantiates a QObject, an internal context is assigned
917 to it automatically. Such internal contexts are read-only. You cannot set
918 context properties on them.
919
920 \sa qmlContext(), qmlEngine(), QQmlContext::setContextProperty()
921 */
922QQmlContext *QQmlEngine::contextForObject(const QObject *object)
923{
924 if(!object)
925 return nullptr;
926
927 QQmlData *data = QQmlData::get(object);
928 if (data && data->outerContext)
929 return data->outerContext->asQQmlContext();
930
931 return nullptr;
932}
933
934/*!
935 Sets the QQmlContext for the \a object to \a context.
936 If the \a object already has a context, a warning is
937 output, but the context is not changed.
938
939 When the QQmlEngine instantiates a QObject, the context is
940 set automatically.
941 */
942void QQmlEngine::setContextForObject(QObject *object, QQmlContext *context)
943{
944 if (!object || !context)
945 return;
946
947 QQmlData *data = QQmlData::get(object, true);
948 if (data->context) {
949 qWarning("QQmlEngine::setContextForObject(): Object already has a QQmlContext");
950 return;
951 }
952
953 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
954 Q_ASSERT(data->context == nullptr);
955 data->context = contextData.data();
956 contextData->addOwnedObject(data);
957}
958
959/*!
960 \reimp
961*/
962bool QQmlEngine::event(QEvent *e)
963{
964 if (e->type() == QEvent::LanguageChange) {
965 retranslate();
966 }
967
968 return QJSEngine::event(e);
969}
970
971void QQmlEnginePrivate::sendQuit()
972{
973 Q_Q(QQmlEngine);
974 emit q->quit();
975 if (q->receivers(SIGNAL(quit())) == 0) {
976 qWarning("Signal QQmlEngine::quit() emitted, but no receivers connected to handle it.");
977 }
978}
979
980void QQmlEnginePrivate::sendExit(int retCode)
981{
982 Q_Q(QQmlEngine);
983 if (q->receivers(SIGNAL(exit(int))) == 0)
984 qWarning("Signal QQmlEngine::exit() emitted, but no receivers connected to handle it.");
985 emit q->exit(retCode);
986}
987
988static void dumpwarning(const QQmlError &error)
989{
990 switch (error.messageType()) {
991 case QtDebugMsg:
992 QMessageLogger(error.url().toString().toLatin1().constData(),
993 error.line(), nullptr).debug().noquote().nospace()
994 << error.toString();
995 break;
996 case QtInfoMsg:
997 QMessageLogger(error.url().toString().toLatin1().constData(),
998 error.line(), nullptr).info().noquote().nospace()
999 << error.toString();
1000 break;
1001 case QtWarningMsg:
1002 case QtFatalMsg: // fatal does not support streaming, and furthermore, is actually fatal. Probably not desirable for QML.
1003 QMessageLogger(error.url().toString().toLatin1().constData(),
1004 error.line(), nullptr).warning().noquote().nospace()
1005 << error.toString();
1006 break;
1007 case QtCriticalMsg:
1008 QMessageLogger(error.url().toString().toLatin1().constData(),
1009 error.line(), nullptr).critical().noquote().nospace()
1010 << error.toString();
1011 break;
1012 }
1013}
1014
1015static void dumpwarning(const QList<QQmlError> &errors)
1016{
1017 for (int ii = 0; ii < errors.size(); ++ii)
1018 dumpwarning(errors.at(ii));
1019}
1020
1021void QQmlEnginePrivate::warning(const QQmlError &error)
1022{
1023 Q_Q(QQmlEngine);
1024 emit q->warnings(QList<QQmlError>({error}));
1025 if (outputWarningsToMsgLog)
1026 dumpwarning(error);
1027}
1028
1029void QQmlEnginePrivate::warning(const QList<QQmlError> &errors)
1030{
1031 Q_Q(QQmlEngine);
1032 emit q->warnings(errors);
1033 if (outputWarningsToMsgLog)
1034 dumpwarning(errors);
1035}
1036
1037void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error)
1038{
1039 if (engine)
1040 QQmlEnginePrivate::get(engine)->warning(error);
1041 else
1042 dumpwarning(error);
1043}
1044
1045void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList<QQmlError> &error)
1046{
1047 if (engine)
1048 QQmlEnginePrivate::get(engine)->warning(error);
1049 else
1050 dumpwarning(error);
1051}
1052
1053void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error)
1054{
1055 if (engine)
1056 engine->warning(error);
1057 else
1058 dumpwarning(error);
1059}
1060
1061void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError> &error)
1062{
1063 if (engine)
1064 engine->warning(error);
1065 else
1066 dumpwarning(error);
1067}
1068
1069QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(
1070 const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages)
1071{
1072 QList<QQmlError> errors;
1073 for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
1074 if (m.isWarning()) {
1075 qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message));
1076 continue;
1077 }
1078
1079 QQmlError error;
1080 error.setUrl(QUrl(fileName));
1081 error.setDescription(m.message);
1082 error.setLine(qmlConvertSourceCoordinate<quint32, int>(m.loc.startLine));
1083 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(m.loc.startColumn));
1084 errors << error;
1085 }
1086 return errors;
1087}
1088
1089void QQmlEnginePrivate::cleanupScarceResources()
1090{
1091 // iterate through the list and release them all.
1092 // note that the actual SRD is owned by the JS engine,
1093 // so we cannot delete the SRD; but we can free the
1094 // memory used by the variant in the SRD.
1095 QV4::ExecutionEngine *engine = v4Engine.get();
1096 while (QV4::ExecutionEngine::ScarceResourceData *sr = engine->scarceResources.first()) {
1097 sr->data = QVariant();
1098 engine->scarceResources.remove(sr);
1099 }
1100}
1101
1102/*!
1103 Adds \a path as a directory where the engine searches for
1104 installed modules in a URL-based directory structure.
1105
1106 The \a path may be a local filesystem directory, a
1107 \l {The Qt Resource System}{Qt Resource} path (\c {:/imports}), a
1108 \l {The Qt Resource System}{Qt Resource} url (\c {qrc:/imports}) or a URL.
1109
1110 The \a path will be converted into canonical form before it
1111 is added to the import path list.
1112
1113 The newly added \a path will be first in the importPathList().
1114
1115 \b {See also} \l setImportPathList(), \l {QML Modules},
1116 and \l [QtQml] {QML Import Path}
1117*/
1118void QQmlEngine::addImportPath(const QString& path)
1119{
1120 QQmlTypeLoader::get(this)->addImportPath(path);
1121}
1122
1123/*!
1124 Returns the list of directories where the engine searches for
1125 installed modules in a URL-based directory structure.
1126
1127 For example, if \c /opt/MyApp/lib/imports is in the path, then QML that
1128 imports \c com.mycompany.Feature will cause the QQmlEngine to look
1129 in \c /opt/MyApp/lib/imports/com/mycompany/Feature/ for the components
1130 provided by that module. A \c qmldir file is required for defining the
1131 type version mapping and possibly QML extensions plugins.
1132
1133 By default, this list contains the paths mentioned in
1134 \l {QML Import Path}.
1135
1136 \sa addImportPath(), setImportPathList()
1137*/
1138QStringList QQmlEngine::importPathList() const
1139{
1140 return QQmlTypeLoader::get(this)->importPathList();
1141}
1142
1143/*!
1144 Sets \a paths as the list of directories where the engine searches for
1145 installed modules in a URL-based directory structure.
1146
1147 By default, this list contains the paths mentioned in
1148 \l {QML Import Path}.
1149
1150 \warning Calling setImportPathList does not preserve the default
1151 import paths.
1152
1153 \sa importPathList(), addImportPath()
1154 */
1155void QQmlEngine::setImportPathList(const QStringList &paths)
1156{
1157 QQmlTypeLoader::get(this)->setImportPathList(paths);
1158}
1159
1160
1161/*!
1162 Adds \a path as a directory where the engine searches for
1163 native plugins for imported modules (referenced in the \c qmldir file).
1164
1165 By default, the list contains only \c ., i.e. the engine searches
1166 in the directory of the \c qmldir file itself.
1167
1168 The newly added \a path will be first in the pluginPathList().
1169
1170 \sa setPluginPathList()
1171*/
1172void QQmlEngine::addPluginPath(const QString& path)
1173{
1174 QQmlTypeLoader::get(this)->addPluginPath(path);
1175}
1176
1177/*!
1178 Returns the list of directories where the engine searches for
1179 native plugins for imported modules (referenced in the \c qmldir file).
1180
1181 By default, the list contains only \c ., i.e. the engine searches
1182 in the directory of the \c qmldir file itself.
1183
1184 \sa addPluginPath(), setPluginPathList()
1185*/
1186QStringList QQmlEngine::pluginPathList() const
1187{
1188 return QQmlTypeLoader::get(this)->pluginPathList();
1189}
1190
1191/*!
1192 Sets the list of directories where the engine searches for
1193 native plugins for imported modules (referenced in the \c qmldir file)
1194 to \a paths.
1195
1196 By default, the list contains only \c ., i.e. the engine searches
1197 in the directory of the \c qmldir file itself.
1198
1199 \sa pluginPathList(), addPluginPath()
1200 */
1201void QQmlEngine::setPluginPathList(const QStringList &paths)
1202{
1203 QQmlTypeLoader::get(this)->setPluginPathList(paths);
1204}
1205
1206#if QT_CONFIG(library)
1207#if QT_DEPRECATED_SINCE(6, 4)
1208/*!
1209 \deprecated [6.4] Import the module from QML with an "import" statement instead.
1210
1211 Imports the plugin named \a filePath with the \a uri provided.
1212 Returns true if the plugin was successfully imported; otherwise returns false.
1213
1214 On failure and if non-null, the \a errors list will have any errors which occurred prepended to it.
1215
1216 The plugin has to be a Qt plugin which implements the QQmlEngineExtensionPlugin interface.
1217
1218 \note Directly loading plugins like this can confuse the module import logic. In order to make
1219 the import logic load plugins from a specific place, you can use \l addPluginPath(). Each
1220 plugin should be part of a QML module that you can import using the "import" statement.
1221*/
1222bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
1223{
1224 QQmlTypeLoaderQmldirContent qmldir;
1225 QQmlPluginImporter importer(uri, QTypeRevision(), &qmldir, QQmlTypeLoader::get(this), errors);
1226 return importer.importDynamicPlugin(filePath, uri, false).isValid();
1227}
1228#endif
1229#endif
1230
1231/*!
1232 \property QQmlEngine::offlineStoragePath
1233 \brief the directory for storing offline user data
1234
1235 Returns the directory where SQL and other offline
1236 storage is placed.
1237
1238 The SQL databases created with \c openDatabaseSync() are stored here.
1239
1240 The default is QML/OfflineStorage in the platform-standard
1241 user application data directory.
1242
1243 Note that the path may not currently exist on the filesystem, so
1244 callers wanting to \e create new files at this location should create
1245 it first - see QDir::mkpath().
1246
1247 \sa {Qt Quick Local Storage QML Types}
1248*/
1249
1250/*!
1251 \fn void QQmlEngine::offlineStoragePathChanged()
1252 This signal is emitted when \l offlineStoragePath changes.
1253 \since 6.5
1254*/
1255
1256void QQmlEngine::setOfflineStoragePath(const QString& dir)
1257{
1258 Q_D(QQmlEngine);
1259 if (dir == d->offlineStoragePath)
1260 return;
1261 d->offlineStoragePath = dir;
1262 Q_EMIT offlineStoragePathChanged();
1263}
1264
1265QString QQmlEngine::offlineStoragePath() const
1266{
1267 Q_D(const QQmlEngine);
1268
1269 if (d->offlineStoragePath.isEmpty()) {
1270 QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
1271 QQmlEnginePrivate *e = const_cast<QQmlEnginePrivate *>(d);
1272 if (!dataLocation.isEmpty()) {
1273 e->offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator())
1274 + QDir::separator() + QLatin1String("QML")
1275 + QDir::separator() + QLatin1String("OfflineStorage");
1276 Q_EMIT e->q_func()->offlineStoragePathChanged();
1277 }
1278 }
1279
1280 return d->offlineStoragePath;
1281}
1282
1283/*!
1284 Returns the file path where a \l{QtQuick.LocalStorage}{Local Storage}
1285 database with the identifier \a databaseName is (or would be) located.
1286
1287 \sa {openDatabaseSync}{LocalStorage.openDatabaseSync()}
1288 \since 5.9
1289*/
1290QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) const
1291{
1292 Q_D(const QQmlEngine);
1293 QCryptographicHash md5(QCryptographicHash::Md5);
1294 md5.addData(databaseName.toUtf8());
1295 return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex());
1296}
1297
1298QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
1299{
1300 Q_Q(const QQmlEngine);
1301 return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator();
1302}
1303
1304static bool hasRequiredProperties(const QQmlPropertyCache::ConstPtr &propertyCache)
1305{
1306 bool requiredPropertiesFound = false;
1307 // we don't expect to find any, so the loop has no early termination check
1308 if (propertyCache) {
1309 for (int idx = 0, count = propertyCache->propertyCount(); idx < count; ++idx)
1310 requiredPropertiesFound |= propertyCache->property(idx)->isRequired();
1311 }
1312 return requiredPropertiesFound;
1313}
1314
1315template<>
1316QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
1317{
1318 Q_Q(QQmlEngine);
1319
1320 QQmlType::SingletonInstanceInfo::ConstPtr siinfo = type.singletonInstanceInfo();
1321 Q_ASSERT(siinfo != nullptr);
1322
1323 QJSValue value = singletonInstances.value(siinfo);
1324 if (!value.isUndefined())
1325 return value;
1326
1327 if (siinfo->scriptCallback) {
1328 value = siinfo->scriptCallback(q, q);
1329 if (value.isQObject()) {
1330 QObject *o = value.toQObject();
1331 // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
1332 // should behave identically to QML singleton types.
1333 q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
1334 }
1335 singletonInstances.convertAndInsert(v4Engine.get(), siinfo, &value);
1336
1337 } else if (siinfo->qobjectCallback) {
1338 QObject *o = siinfo->qobjectCallback(q, q);
1339 if (!o) {
1340 QQmlError error;
1341 error.setMessageType(QtMsgType::QtCriticalMsg);
1342 error.setDescription(QString::asprintf("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.",
1343 qPrintable(QString::fromUtf8(type.typeName()))));
1344 warning(error);
1345 } else {
1346 type.createProxy(o);
1347
1348 // if this object can use a property cache, create it now
1349 QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(o);
1350 if (Q_UNLIKELY(hasRequiredProperties(propertyCache))) {
1351 // there's no way to set required properties on a singleton
1352 delete o;
1353 o = nullptr;
1354 QQmlError error;
1355 error.setMessageType(QtMsgType::QtCriticalMsg);
1356 error.setDescription(QString::asprintf("Singleton \"%s\" is not available because the type has unset required properties.",
1357 qPrintable(QString::fromUtf8(type.typeName()))));
1358 warning(error);
1359 } else {
1360 // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
1361 // should behave identically to QML singleton types. You can, however, manually
1362 // assign a context; and clearSingletons() retains the contexts, in which case
1363 // we don't want to see warnings about the object already having a context.
1364 QQmlData *data = QQmlData::get(o, true);
1365 if (!data->context) {
1366 auto contextData = QQmlContextData::get(new QQmlContext(q->rootContext(), q));
1367 data->context = contextData.data();
1368 contextData->addOwnedObject(data);
1369 }
1370 }
1371 }
1372
1373 value = q->newQObject(o);
1374 singletonInstances.convertAndInsert(v4Engine.get(), siinfo, &value);
1375 } else if (!siinfo->url.isEmpty()) {
1376 QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
1377 if (component.isError()) {
1378 warning(component.errors());
1379 v4Engine->throwError(
1380 QLatin1String("Due to the preceding error(s), "
1381 "Singleton \"%1\" could not be loaded.")
1382 .arg(QString::fromUtf8(type.typeName())));
1383
1384 return QJSValue(QJSValue::UndefinedValue);
1385 }
1386 QObject *o = component.beginCreate(q->rootContext());
1387 auto *compPriv = QQmlComponentPrivate::get(&component);
1388 if (compPriv->hasUnsetRequiredProperties()) {
1389 /* We would only get the errors from the component after (complete)Create.
1390 We can't call create, as we need to convertAndInsert before completeCreate (otherwise
1391 tst_qqmllanguage::compositeSingletonCircular fails).
1392 On the other hand, we don't want to call cnovertAndInsert if we have an error
1393 So create the unset required component errors manually.
1394 */
1395 delete o;
1396 const auto requiredProperties = compPriv->requiredProperties();
1397 QList<QQmlError> errors (requiredProperties->size());
1398 for (const auto &reqProp: *requiredProperties)
1399 errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(reqProp));
1400 warning(errors);
1401 v4Engine->throwError(
1402 QLatin1String("Due to the preceding error(s), "
1403 "Singleton \"%1\" could not be loaded.")
1404 .arg(QString::fromUtf8(type.typeName())));
1405 return QJSValue(QJSValue::UndefinedValue);
1406 }
1407
1408 value = q->newQObject(o);
1409 singletonInstances.convertAndInsert(v4Engine.get(), siinfo, &value);
1410 component.completeCreate();
1411 }
1412
1413 return value;
1414}
1415
1416void QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex,
1417 QObject *thisObject, int argc, void **args,
1418 QMetaType *types)
1419{
1420 const auto unit = compilationUnitFromUrl(url);
1421 if (!unit)
1422 return;
1423 executeRuntimeFunction(unit, functionIndex, thisObject, argc, args, types);
1424}
1425
1426void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationUnit *unit,
1427 qsizetype functionIndex, QObject *thisObject,
1428 int argc, void **args, QMetaType *types)
1429{
1430 Q_ASSERT(unit);
1431 Q_ASSERT((functionIndex >= 0) && (functionIndex < unit->runtimeFunctions.size()));
1432 Q_ASSERT(thisObject);
1433
1434 QQmlData *ddata = QQmlData::get(thisObject);
1435 Q_ASSERT(ddata && ddata->context);
1436
1437 QV4::Function *function = unit->runtimeFunctions[functionIndex];
1438 Q_ASSERT(function);
1439 Q_ASSERT(function->compiledFunction);
1440
1441 QV4::ExecutionEngine *v4 = v4Engine.get();
1442
1443 // NB: always use scriptContext() by default as this method ignores whether
1444 // there's already a stack frame (except when dealing with closures). the
1445 // method is called from C++ (through QQmlEngine::executeRuntimeFunction())
1446 // and thus the caller must ensure correct setup
1447 QV4::Scope scope(v4);
1448 QV4::ExecutionContext *ctx = v4->scriptContext();
1449 QV4::Scoped<QV4::ExecutionContext> callContext(scope,
1450 QV4::QmlContext::create(ctx, ddata->context, thisObject));
1451
1452 if (auto nested = function->nestedFunction()) {
1453 // if a nested function is already known, call the closure directly
1454 function = nested;
1455 } else if (function->isClosureWrapper()) {
1456 // if there is a nested function, but we don't know it, we need to call
1457 // an outer function first and then the inner function. we fetch the
1458 // return value of a function call (that is a closure) by calling a
1459 // different version of ExecutionEngine::callInContext() that returns a
1460 // QV4::ReturnedValue with no arguments since they are not needed by the
1461 // outer function anyhow
1462 QV4::Scoped<QV4::JavaScriptFunctionObject> result(scope,
1463 v4->callInContext(function, thisObject, callContext, 0, nullptr));
1464 Q_ASSERT(result->function());
1465 Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
1466
1467 // overwrite the function and its context
1468 function = result->function();
1469 callContext = QV4::Scoped<QV4::ExecutionContext>(scope, result->scope());
1470 }
1471
1472 v4->callInContext(function, thisObject, callContext, argc, args, types);
1473}
1474
1475QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url)
1476{
1477 QV4::ExecutionEngine *v4 = v4Engine.get();
1478 if (auto unit = v4->compilationUnitForUrl(url)) {
1479 if (!unit->runtimeStrings)
1480 unit->populate();
1481 return unit.data();
1482 }
1483
1484 auto unit = v4->typeLoader()->getType(url)->compilationUnit();
1485 if (!unit)
1486 return nullptr;
1487
1488 auto executable = v4->executableCompilationUnit(std::move(unit));
1489 executable->populate();
1490 return executable.data();
1491}
1492
1493QQmlRefPointer<QQmlContextData>
1494QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
1495 const QQmlRefPointer<QQmlContextData> &parentContext,
1496 int subComponentIndex, bool isComponentRoot)
1497{
1498 Q_ASSERT(unit);
1499
1500 QQmlRefPointer<QQmlContextData> context;
1501 context = QQmlContextData::createRefCounted(parentContext);
1502 context->setInternal(true);
1503 context->setImports(unit->typeNameCache());
1504 context->initFromTypeCompilationUnit(unit, subComponentIndex);
1505
1506 const auto *dependentScripts = unit->dependentScriptsPtr();
1507 const qsizetype dependentScriptsSize = dependentScripts->size();
1508 if (isComponentRoot && dependentScriptsSize) {
1509 QV4::ExecutionEngine *v4 = v4Engine.get();
1510 Q_ASSERT(v4);
1511 QV4::Scope scope(v4);
1512
1513 QV4::ScopedObject scripts(scope, v4->newArrayObject(dependentScriptsSize));
1514 context->setImportedScripts(v4, scripts);
1515 QV4::ScopedValue v(scope);
1516 for (qsizetype i = 0; i < dependentScriptsSize; ++i)
1517 scripts->put(i, (v = dependentScripts->at(i)->scriptValueForContext(context)));
1518 }
1519
1520 return context;
1521}
1522
1523/*!
1524 \fn QQmlEngine *qmlEngine(const QObject *object)
1525 \relates QQmlEngine
1526
1527 Returns the QQmlEngine associated with \a object, if any. This is equivalent to
1528 QQmlEngine::contextForObject(object)->engine(), but more efficient.
1529
1530 \note Add \c{#include <QtQml>} to use this function.
1531
1532 \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlContext()
1533*/
1534
1535/*!
1536 \fn QQmlContext *qmlContext(const QObject *object)
1537 \relates QQmlEngine
1538
1539 Returns the QQmlContext associated with \a object, if any. This is equivalent to
1540 QQmlEngine::contextForObject(object).
1541
1542 \note Add \c{#include <QtQml>} to use this function.
1543
1544 \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine()
1545*/
1546
1547void hasJsOwnershipIndicator(QQmlGuardImpl *) {};
1548
1549LoadHelper::LoadHelper(
1550 QQmlTypeLoader *loader, QAnyStringView uri, QAnyStringView typeName,
1551 QQmlTypeLoader::Mode mode)
1553 , m_uri(uri.toString())
1555 , m_mode(mode)
1556{
1557 m_typeLoader->loadWithStaticData(this, QByteArray(), m_mode);
1558}
1559
1560void LoadHelper::registerCallback(QQmlComponentPrivate *callback)
1561{
1562 m_callback = callback;
1563}
1564
1565void LoadHelper::unregisterCallback(QQmlComponentPrivate *callback)
1566{
1567 if (m_callback) {
1568 Q_ASSERT(callback == m_callback);
1569 m_callback = nullptr;
1570 }
1571}
1572
1573void LoadHelper::done()
1574{
1575 if (!couldFindModule()) {
1576 m_resolveTypeResult = ResolveTypeResult::NoSuchModule;
1577 return;
1578 }
1579
1580 QQmlTypeModule *module = QQmlMetaType::typeModule(m_uri, QTypeRevision{});
1581 if (module) {
1582 m_type = module->type(m_typeName, {});
1583 if (m_type.isValid()) {
1584 m_resolveTypeResult = ResolveTypeResult::ModuleFound;
1585 return;
1586 }
1587 }
1588
1589 // The module exists (see check above), but there is no QQmlTypeModule
1590 // ==> pure QML module, attempt resolveType
1591 QTypeRevision versionReturn;
1592 QList<QQmlError> errors;
1593 QQmlImportNamespace *ns_return = nullptr;
1594 m_importCache->resolveType(
1595 typeLoader(), m_typeName, &m_type, &versionReturn, &ns_return, &errors);
1596 m_resolveTypeResult = ResolveTypeResult::ModuleFound;
1597}
1598
1599void LoadHelper::completed()
1600{
1601 QQmlTypeLoader::Blob::completed();
1602
1603 if (m_callback) {
1604 m_callback->completeLoadFromModule(m_uri, m_typeName);
1605 m_callback = nullptr;
1606 }
1607}
1608
1609void LoadHelper::dataReceived(const SourceCodeData &)
1610{
1611 auto import = std::make_shared<PendingImport>();
1612 import->uri = m_uri;
1613 QList<QQmlError> errorList;
1614 if (!Blob::addImport(import, &errorList)) {
1615 qCDebug(lcQmlImport) << "LoadHelper: Errors loading " << m_uri << errorList;
1616 m_uri.clear(); // reset m_uri to remember the failure
1617 }
1618}
1619
1620bool LoadHelper::couldFindModule() const
1621{
1622 if (m_uri.isEmpty())
1623 return false;
1624 for (const auto &import: std::as_const(m_unresolvedImports))
1625 if (import->priority == 0) // compare QQmlTypeData::allDependenciesDone
1626 return false;
1627 return true;
1628}
1629
1630QT_END_NAMESPACE
1631
1632#include "moc_qqmlengine.cpp"
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)
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...