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
qqmlcomponent.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
8
9#include "qqmlengine_p.h"
10#include "qqmlvme_p.h"
11#include "qqml.h"
12#include "qqmlengine.h"
13#include "qqmlincubator.h"
15#include <private/qqmljavascriptexpression_p.h>
16#include <private/qqmlsourcecoordinate_p.h>
17
18#include <private/qv4functionobject_p.h>
19#include <private/qv4script_p.h>
20#include <private/qv4scopedvalue_p.h>
21#include <private/qv4objectiterator_p.h>
22#include <private/qv4qobjectwrapper_p.h>
23#include <private/qv4jscall_p.h>
24
25#include <QDir>
26#include <QStack>
27#include <QStringList>
28#include <QThreadStorage>
29#include <QtCore/qdebug.h>
30#include <QtCore/qloggingcategory.h>
31#include <qqmlinfo.h>
32
33
34using namespace Qt::Literals::StringLiterals;
35
36namespace {
37 Q_CONSTINIT thread_local int creationDepth = 0;
38}
39
40Q_STATIC_LOGGING_CATEGORY(lcQmlComponentGeneral, "qt.qml.qmlcomponent")
41
42QT_BEGIN_NAMESPACE
43
44class QQmlComponentExtension : public QV4::ExecutionEngine::Deletable
45{
46public:
47 QQmlComponentExtension(QV4::ExecutionEngine *v4);
48 virtual ~QQmlComponentExtension();
49
50 QV4::PersistentValue incubationProto;
51};
53
54/*!
55 \class QQmlComponent
56 \since 5.0
57 \inmodule QtQml
58
59 \brief The QQmlComponent class encapsulates a QML component definition.
60
61 Components are reusable, encapsulated QML types with well-defined interfaces.
62
63 A QQmlComponent instance can be created from a QML file.
64 For example, if there is a \c main.qml file like this:
65
66 \qml
67 import QtQuick 2.0
68
69 Item {
70 width: 200
71 height: 200
72 }
73 \endqml
74
75 The following code loads this QML file as a component, creates an instance of
76 this component using create(), and then queries the \l Item's \l {Item::}{width}
77 value:
78
79 \code
80 QQmlEngine *engine = new QQmlEngine;
81 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
82 if (component.isError()) {
83 qWarning() << "Failed to load main.qml:" << component.errors();
84 return 1;
85 }
86
87 QObject *myObject = component.create();
88 if (component.isError()) {
89 qWarning() << "Failed to create instance of main.qml:" << component.errors();
90 return 1;
91 }
92
93 QQuickItem *item = qobject_cast<QQuickItem*>(myObject);
94 int width = item->width(); // width = 200
95 \endcode
96
97 To create instances of a component in code where a QQmlEngine instance is
98 not available, you can use \l qmlContext() or \l qmlEngine(). For example,
99 in the scenario below, child items are being created within a QQuickItem
100 subclass:
101
102 \code
103 void MyCppItem::init()
104 {
105 QQmlEngine *engine = qmlEngine(this);
106 // Or:
107 // QQmlEngine *engine = qmlContext(this)->engine();
108 QQmlComponent component(engine, QUrl::fromLocalFile("MyItem.qml"));
109 QQuickItem *childItem = qobject_cast<QQuickItem*>(component.create());
110 childItem->setParentItem(this);
111 }
112 \endcode
113
114 Note that these functions will return \c null when called inside the
115 constructor of a QObject subclass, as the instance will not yet have
116 a context nor engine.
117
118 \section2 Network Components
119
120 If the URL passed to QQmlComponent is a network resource, or if the QML document references a
121 network resource, the QQmlComponent has to fetch the network data before it is able to create
122 objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading}
123 \l {QQmlComponent::status()}{status}. An application will have to wait until the component
124 is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}.
125
126 The following example shows how to load a QML file from a network resource. After creating
127 the QQmlComponent, it tests whether the component is loading. If it is, it connects to the
128 QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
129 directly. Note that QQmlComponent::isLoading() may be false for a network component if the
130 component has been cached and is ready immediately.
131
132 \code
133 MyApplication::MyApplication()
134 {
135 // ...
136 component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
137 if (component->isLoading()) {
138 QObject::connect(component, &QQmlComponent::statusChanged,
139 this, &MyApplication::continueLoading);
140 } else {
141 continueLoading();
142 }
143 }
144
145 void MyApplication::continueLoading()
146 {
147 if (component->isError()) {
148 qWarning() << component->errors();
149 } else {
150 QObject *myObject = component->create();
151 }
152 }
153 \endcode
154*/
155
156/*!
157 \qmltype Component
158 \nativetype QQmlComponent
159 \ingroup qml-utility-elements
160 \inqmlmodule QtQml
161 \brief Encapsulates a QML component definition.
162
163 Components are reusable, encapsulated QML types with well-defined interfaces.
164
165 Components are often defined by \l {{QML Documents}}{component files} -
166 that is, \c .qml files. The \e Component type essentially allows QML components
167 to be defined inline, within a \l {QML Documents}{QML document}, rather than as a separate QML file.
168 This may be useful for reusing a small component within a QML file, or for defining
169 a component that logically belongs with other QML components within a file.
170
171 For example, here is a component that is used by multiple \l Loader objects.
172 It contains a single item, a \l Rectangle:
173
174 \snippet qml/component.qml 0
175
176 Notice that while a \l Rectangle by itself would be automatically
177 rendered and displayed, this is not the case for the above rectangle
178 because it is defined inside a \c Component. The component encapsulates the
179 QML types within, as if they were defined in a separate QML
180 file, and is not loaded until requested (in this case, by the
181 two \l Loader objects). Because Component is not derived from Item, you cannot
182 anchor anything to it.
183
184 Defining a \c Component is similar to defining a \l {QML Documents}{QML document}.
185 A QML document has a single top-level item that defines the behavior and
186 properties of that component, and cannot define properties or behavior outside
187 of that top-level item. In the same way, a \c Component definition contains a single
188 top level item (which in the above example is a \l Rectangle) and cannot define any
189 data outside of this item, with the exception of an \e id (which in the above example
190 is \e redSquare).
191
192 The \c Component type is commonly used to provide graphical components
193 for views. For example, the ListView::delegate property requires a \c Component
194 to specify how each list item is to be displayed.
195
196 \c Component objects can also be created dynamically using
197 \l{QtQml::Qt::createComponent()}{Qt.createComponent()}.
198
199 \c {Component}s are useful to declare a type where you only need an
200 instance of the type without having to add an entire new file. However,
201 you cannot name this type and consequently can't use it to declare a
202 property or use it in a type annotation. If you need this, prefer using
203 \l{Defining Object Types through QML Documents#Inline Components}{inline components}.
204
205 \section2 Creation Context
206
207 The creation context of a Component corresponds to the context where the Component was declared.
208 This context is used as the parent context (creating a \l{qtqml-documents-scope.html#component-instance-hierarchy}{context hierarchy})
209 when the component is instantiated by an object such as a ListView or a Loader.
210
211 In the following example, \c comp1 is created within the root context of MyItem.qml, and any objects
212 instantiated from this component will have access to the ids and properties within that context,
213 such as \c internalSettings.color. When \c comp1 is used as a ListView delegate in another context
214 (as in main.qml below), it will continue to have access to the properties of its creation context
215 (which would otherwise be private to external users).
216
217 \table
218 \row
219 \li MyItem.qml
220 \li \snippet qml/component/MyItem.qml 0
221 \row
222 \li main.qml
223 \li \snippet qml/component/main.qml 0
224 \endtable
225
226 It is important that the lifetime of the creation context outlive any created objects. See
227 \l{Maintaining Dynamically Created Objects} for more details.
228*/
229
230/*!
231 \qmlattachedsignal Component::completed()
232
233 Emitted after the object has been instantiated. This can be used to
234 execute script code at startup, once the full QML environment has been
235 established.
236
237 The \c onCompleted signal handler can be declared on any object. The order
238 of running the handlers is undefined.
239
240 \qml
241 Rectangle {
242 Component.onCompleted: console.log("Completed Running!")
243 Rectangle {
244 Component.onCompleted: console.log("Nested Completed Running!")
245 }
246 }
247 \endqml
248*/
249
250/*!
251 \qmlattachedsignal Component::destruction()
252
253 Emitted as the object begins destruction. This can be used to undo
254 work done in response to the \l {completed}{completed()} signal, or other
255 imperative code in your application.
256
257 The \c onDestruction signal handler can be declared on any object. The
258 order of running the handlers is undefined.
259
260 \qml
261 Rectangle {
262 Component.onDestruction: console.log("Destruction Beginning!")
263 Rectangle {
264 Component.onDestruction: console.log("Nested Destruction Beginning!")
265 }
266 }
267 \endqml
268
269 \sa {Qt Qml}
270*/
271
272/*!
273 \enum QQmlComponent::Status
274
275 Specifies the loading status of the QQmlComponent.
276
277 \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content.
278 \value Ready This QQmlComponent is ready and create() may be called.
279 \value Loading This QQmlComponent is loading network data.
280 \value Error An error has occurred. Call errors() to retrieve a list of \l {QQmlError}{errors}.
281*/
282
283/*!
284 \enum QQmlComponent::CompilationMode
285
286 Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
287
288 \value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
289 This is not always possible; for example, remote URLs will always load asynchronously.
290 \value Asynchronous Load/compile the component in a background thread.
291*/
292
293void QQmlComponentPrivate::ready(QQmlNotifyingBlob *)
294{
295 Q_Q(QQmlComponent);
296
297 Q_ASSERT(m_typeData);
298
299 fromTypeData(m_typeData);
300 m_typeData.reset();
301 setProgress(1.0);
302 emit q->statusChanged(q->status());
303}
304
305void QQmlComponentPrivate::progress(QQmlNotifyingBlob *, qreal p)
306{
307 setProgress(p);
308}
309
310void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data)
311{
312 m_url = data->finalUrl();
313 if (auto cu = data->compilationUnit())
314 m_compilationUnit = m_engine->handle()->executableCompilationUnit(std::move(cu));
315
316 if (!m_compilationUnit) {
317 Q_ASSERT(data->isError());
318 m_state.errors.clear();
319 m_state.appendErrors(data->errors());
320 }
321}
322
323bool QQmlComponentPrivate::hadTopLevelRequiredProperties() const
324{
325 return m_state.creator()->componentHadTopLevelRequiredProperties();
326}
327
328void QQmlComponentPrivate::clear()
329{
330 if (m_typeData) {
331 m_typeData->unregisterCallback(this);
332 m_typeData.reset();
333 }
334
335 if (m_loadHelper) {
336 m_loadHelper->unregisterCallback(this);
337 m_loadHelper.reset();
338 }
339
340 m_compilationUnit.reset();
341 m_inlineComponentName.reset();
342}
343
344QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context)
345{
346 if (!m_engine) {
347 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
348 qWarning("QQmlComponent: Must provide an engine before calling create");
349 return nullptr;
350 }
351 if (!context)
352 context = m_engine->rootContext();
353 return q->beginCreate(context);
354}
355
357 QV4::Value *object, const QString &propertyName, const QQmlObjectCreator *creator)
358{
359 if (!creator)
360 return;
361
362 QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>();
363 if (!wrapper)
364 return;
365
366 QObject *o = wrapper->object();
367 if (!o)
368 return;
369
370 if (QQmlData *ddata = QQmlData::get(o)) {
371 const QQmlPropertyData *propData = ddata->propertyCache->property(
372 propertyName, o, ddata->outerContext);
373 if (propData && propData->acceptsQBinding())
374 creator->removePendingBinding(o, propData->coreIndex());
375 return;
376 }
377
378 const QMetaObject *meta = o->metaObject();
379 Q_ASSERT(meta);
380 const int index = meta->indexOfProperty(propertyName.toUtf8());
381 if (index != -1 && meta->property(index).isBindable())
382 creator->removePendingBinding(o, index);
383}
384
385bool QQmlComponentPrivate::setInitialProperty(
386 QObject *base, const QString &name, const QVariant &value)
387{
388 bool isComplexProperty = name.contains(u'.');
389 QQmlProperty prop;
390 // we don't allow "fixing" inner required properties - that would seriously hamper local reasoning
391 if (m_state.hasUnsetRequiredProperties() && !isComplexProperty)
392 prop = QQmlComponentPrivate::removePropertyFromRequired(
393 base, name, m_state.requiredProperties(), m_engine);
394 else if (QQmlContext *ctxt = qmlContext(base); ctxt)
395 prop = QQmlProperty(base, name, ctxt);
396 else
397 prop = QQmlProperty(base, name, m_engine);
398
399 if (!prop.isValid()) {
400 // Having extra properties in the object that contains initial properties is not a problem.
401 // In JavaScript you can hardly ensure the _absence_ of properties. There are implicitly
402 // added properties on many objects, properties added via prototype, etc.
403 if (!isComplexProperty)
404 return true;
405
406 // QQmlProperty can't handle accesses on value types
407 const QStringList properties = name.split(u'.');
408 QV4::Scope scope(m_engine->handle());
409 QV4::ScopedObject object(scope, QV4::QObjectWrapper::wrap(scope.engine, base));
410 QV4::ScopedString segment(scope);
411 for (int i = 0; i < properties.size() - 1; ++i) {
412 segment = scope.engine->newString(properties.at(i));
413 object = object->get(segment);
414 if (scope.engine->hasException || object->isNullOrUndefined())
415 break;
416 }
417
418 const QString lastProperty = properties.last();
419 if (!object->isNullOrUndefined()) {
420 segment = scope.engine->newString(lastProperty);
421 QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(value.metaType(), value.constData()));
422 object->put(segment, v);
423 } else {
424 return false;
425 }
426
427 if (scope.engine->hasException) {
428 qmlWarning(base, scope.engine->catchExceptionAsQmlError());
429 scope.engine->hasException = false;
430 return false;
431 }
432
433 removePendingQPropertyBinding(object, lastProperty, m_state.creator());
434 return true;
435 }
436
437 if (QQmlPropertyPrivate::get(prop)->writeValueProperty(value, {})) {
438 if (prop.isBindable()) {
439 if (QQmlObjectCreator *creator = m_state.creator())
440 creator->removePendingBinding(prop.object(), prop.index());
441 }
442 return true;
443 }
444
445 QQmlError error{};
446 error.setUrl(m_url);
447 error.setDescription(QStringLiteral("Could not set initial property %1").arg(name));
448 qmlWarning(base, error);
449 return false;
450}
451
452/*!
453 \internal
454*/
455QQmlComponent::QQmlComponent(QObject *parent)
456 : QObject(*(new QQmlComponentPrivate), parent)
457{
458}
459
460/*!
461 Destruct the QQmlComponent.
462*/
463QQmlComponent::~QQmlComponent()
464{
465 Q_D(QQmlComponent);
466
467 if (d->m_state.isCompletePending()) {
468 qWarning("QQmlComponent: Component destroyed while completion pending");
469
470 if (isError()) {
471 qWarning() << "This may have been caused by one of the following errors:";
472 for (const QQmlComponentPrivate::AnnotatedQmlError &e : std::as_const(d->m_state.errors))
473 qWarning().nospace().noquote() << QLatin1String(" ") << e.error;
474 }
475
476 // we might not have the creator anymore if the engine is gone
477 if (d->m_state.hasCreator())
478 d->completeCreate();
479 }
480
481 if (d->m_typeData) {
482 d->m_typeData->unregisterCallback(d);
483 if (d->m_engine && !d->m_typeData->isCompleteOrError()) {
484 // In this case we have to send it to the type loader thread to be dropped. It will
485 // manipulate its "waiting" lists that other blobs may be using concurrently.
486 QQmlTypeLoader::get(d->m_engine)->drop(QQmlDataBlob::Ptr(d->m_typeData.data()));
487 }
488 d->m_typeData.reset();
489 }
490}
491
492/*!
493 \qmlproperty enumeration Component::status
494
495 This property holds the status of component loading. The status can be one of the
496 following:
497
498 \value Component.Null no data is available for the component
499 \value Component.Ready the component has been loaded, and can be used to create instances.
500 \value Component.Loading the component is currently being loaded
501 \value Component.Error an error occurred while loading the component.
502 Calling \l errorString() will provide a human-readable description of any errors.
503 */
504
505/*!
506 \property QQmlComponent::status
507 The component's current \l{QQmlComponent::Status} {status}.
508 */
509QQmlComponent::Status QQmlComponent::status() const
510{
511 Q_D(const QQmlComponent);
512
513 if (d->m_typeData)
514 return Loading;
515 else if (!d->m_state.errors.isEmpty())
516 return Error;
517 else if (d->m_engine && (d->m_compilationUnit || d->loadedType().isValid()))
518 return Ready;
519 else if (d->m_loadHelper)
520 return Loading;
521 else
522 return Null;
523}
524
525/*!
526 Returns true if status() == QQmlComponent::Null.
527*/
528bool QQmlComponent::isNull() const
529{
530 return status() == Null;
531}
532
533/*!
534 Returns true if status() == QQmlComponent::Ready.
535*/
536bool QQmlComponent::isReady() const
537{
538 return status() == Ready;
539}
540
541/*!
542 Returns true if status() == QQmlComponent::Error.
543*/
544bool QQmlComponent::isError() const
545{
546 return status() == Error;
547}
548
549/*!
550 Returns true if status() == QQmlComponent::Loading.
551*/
552bool QQmlComponent::isLoading() const
553{
554 return status() == Loading;
555}
556
557/*!
558 Returns true if the component was created in a QML files that specifies
559 \c{pragma ComponentBehavior: Bound}, otherwise returns false.
560
561 \since 6.5
562 */
563bool QQmlComponent::isBound() const
564{
565 Q_D(const QQmlComponent);
566 return d->isBound();
567}
568
569/*!
570 \qmlproperty real Component::progress
571 The progress of loading the component, from 0.0 (nothing loaded)
572 to 1.0 (finished).
573*/
574
575/*!
576 \property QQmlComponent::progress
577 The progress of loading the component, from 0.0 (nothing loaded)
578 to 1.0 (finished).
579*/
580qreal QQmlComponent::progress() const
581{
582 Q_D(const QQmlComponent);
583 return d->m_progress;
584}
585
586/*!
587 \fn void QQmlComponent::progressChanged(qreal progress)
588
589 Emitted whenever the component's loading progress changes. \a progress will be the
590 current progress between 0.0 (nothing loaded) and 1.0 (finished).
591*/
592
593/*!
594 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
595
596 Emitted whenever the component's status changes. \a status will be the
597 new status.
598*/
599
600/*!
601 Create a QQmlComponent with no data and give it the specified
602 \a engine and \a parent. Set the data with setData().
603*/
604QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
605 : QObject(*(new QQmlComponentPrivate), parent)
606{
607 Q_D(QQmlComponent);
608 d->m_engine = engine;
609 QObject::connect(engine, &QObject::destroyed, this, [d]() {
610 d->m_state.clear();
611 d->m_engine = nullptr;
612 });
613}
614
615/*!
616 Create a QQmlComponent from the given \a url and give it the
617 specified \a parent and \a engine.
618
619 \include qqmlcomponent.qdocinc url-note
620
621 \sa loadUrl()
622*/
623QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
624 : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent)
625{
626}
627
628/*!
629 Create a QQmlComponent from the given \a url and give it the
630 specified \a parent and \a engine. If \a mode is \l Asynchronous,
631 the component will be loaded and compiled asynchronously.
632
633 \include qqmlcomponent.qdocinc url-note
634
635 \sa loadUrl()
636*/
637QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode,
638 QObject *parent)
639 : QQmlComponent(engine, parent)
640{
641 Q_D(QQmlComponent);
642 d->loadUrl(url, mode);
643}
644
645/*!
646 Create a QQmlComponent from the given \a uri and \a typeName and give it
647 the specified \a parent and \a engine. If possible, the component will
648 be loaded synchronously.
649
650 \sa loadFromModule()
651 \since 6.5
652 \overload
653*/
654QQmlComponent::QQmlComponent(QQmlEngine *engine, QAnyStringView uri, QAnyStringView typeName, QObject *parent)
655 : QQmlComponent(engine, uri, typeName, QQmlComponent::PreferSynchronous, parent)
656{
657
658}
659
660/*!
661 Create a QQmlComponent from the given \a uri and \a typeName and give it
662 the specified \a parent and \a engine. If \a mode is \l Asynchronous,
663 the component will be loaded and compiled asynchronously.
664
665 \sa loadFromModule()
666 \since 6.5
667 \overload
668*/
669QQmlComponent::QQmlComponent(QQmlEngine *engine, QAnyStringView uri, QAnyStringView typeName, CompilationMode mode, QObject *parent)
670 : QQmlComponent(engine, parent)
671{
672 loadFromModule(uri, typeName, mode);
673}
674
675/*!
676 Create a QQmlComponent from the given \a fileName and give it the specified
677 \a parent and \a engine.
678
679 \sa loadUrl()
680*/
681QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
682 QObject *parent)
683 : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent)
684{
685}
686
687/*!
688 Create a QQmlComponent from the given \a fileName and give it the specified
689 \a parent and \a engine. If \a mode is \l Asynchronous,
690 the component will be loaded and compiled asynchronously.
691
692 \sa loadUrl()
693*/
694QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
695 CompilationMode mode, QObject *parent)
696 : QQmlComponent(engine, parent)
697{
698 Q_D(QQmlComponent);
699 if (fileName.startsWith(u':'))
700 d->loadUrl(QUrl(QLatin1String("qrc") + fileName), mode);
701 else if (QDir::isAbsolutePath(fileName))
702 d->loadUrl(QUrl::fromLocalFile(fileName), mode);
703 else
704 d->loadUrl(QUrl(fileName), mode);
705}
706
707/*!
708 \internal
709*/
710QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit,
711 int start, QObject *parent)
712 : QQmlComponent(engine, parent)
713{
714 Q_D(QQmlComponent);
715 d->m_compilationUnit.reset(compilationUnit);
716 d->m_start = start;
717 d->m_url = compilationUnit->finalUrl();
718 d->m_progress = 1.0;
719}
720
721void QQmlComponentPrivate::setData(const QByteArray &data, const QUrl &url, QQmlComponent::CompilationMode compilationMode)
722{
723 if (!m_engine) {
724 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
725 qWarning("QQmlComponent: Must provide an engine before calling setData");
726 return;
727 }
728
729 clear();
730
731 m_url = url;
732
733 // trim existing components with same URL; they would bloat the cache
734 // While the warning tries to discourage it, using an empty URL for the
735 // creation of a one-off component is a somewhat common use-case
736 m_engine->handle()->trimCompilationUnitsForUrl(url);
737
738 QQmlTypeLoader::Mode mode = compilationMode == QQmlComponent::Asynchronous
739 ? QQmlTypeLoader::Asynchronous
740 : QQmlTypeLoader::PreferSynchronous;
741 QQmlRefPointer<QQmlTypeData> typeData = QQmlTypeLoader::get(m_engine)->getType(data, url, mode);
742
743 if (typeData->isCompleteOrError()) {
744 fromTypeData(typeData);
745 setProgress(1.0);
746 } else {
747 m_typeData = typeData;
748 m_typeData->registerCallback(this);
749 setProgress(typeData->progress());
750 }
751}
752
753/*!
754 Sets the QQmlComponent to use the given QML \a data. If \a url
755 is provided, it is used to set the component name and to provide
756 a base path for items resolved by this component. The component will
757 be loaded and compiled synchronously.
758
759 \warning The new component will shadow any existing component of
760 the same URL. You should not pass a URL of an existing component.
761*/
762void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
763{
764 Q_D(QQmlComponent);
765 d->setData(data, url, PreferSynchronous);
766 emit statusChanged(status());
767}
768
769/*!
770 Returns the QQmlContext the component was created in. This is only
771 valid for components created directly from QML.
772*/
773QQmlContext *QQmlComponent::creationContext() const
774{
775 Q_D(const QQmlComponent);
776 if (!d->m_creationContext.isNull())
777 return d->m_creationContext->asQQmlContext();
778
779 return qmlContext(this);
780}
781
782/*!
783 Returns the QQmlEngine of this component.
784
785 \since 5.12
786*/
787QQmlEngine *QQmlComponent::engine() const
788{
789 Q_D(const QQmlComponent);
790 return d->m_engine;
791}
792
793/*!
794 Load the QQmlComponent from the provided \a url.
795
796 \include qqmlcomponent.qdocinc url-note
797*/
798void QQmlComponent::loadUrl(const QUrl &url)
799{
800 Q_D(QQmlComponent);
801 d->loadUrl(url);
802}
803
804/*!
805 Load the QQmlComponent from the provided \a url.
806 If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
807
808 \include qqmlcomponent.qdocinc url-note
809*/
810void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
811{
812 Q_D(QQmlComponent);
813 d->loadUrl(url, mode);
814}
815
816void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
817{
818 Q_Q(QQmlComponent);
819 clear();
820
821 if (newUrl.isRelative()) {
822 // The new URL is a relative URL like QUrl("main.qml").
823 m_url = m_engine->baseUrl().resolved(QUrl(newUrl.toString()));
824 } else if (m_engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(newUrl.toLocalFile())) {
825 // The new URL is a file on disk but it's a relative path; e.g.:
826 // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml")
827 // We need to remove the scheme so that it becomes a relative URL with a relative path:
828 QUrl fixedUrl(newUrl);
829 fixedUrl.setScheme(QString());
830 // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl().
831 // This is a compatibility hack for QTBUG-58837.
832 m_url = m_engine->baseUrl().resolved(fixedUrl);
833 } else {
834 m_url = newUrl;
835 }
836
837 if (m_url.scheme() == "qrc"_L1 && !m_url.path().startsWith("/"_L1)) {
838 qWarning().nospace().noquote()
839 << "QQmlComponent: attempted to load via a relative URL '" << m_url.toString()
840 << "' in resource file system. This is not fully supported and may not work";
841 }
842
843 if (newUrl.isEmpty()) {
844 QQmlError error;
845 error.setDescription(QQmlComponent::tr("Invalid empty URL"));
846 m_state.errors.emplaceBack(error);
847 return;
848 }
849
850 setProgress(0.0);
851
852 QQmlTypeLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
853 ? QQmlTypeLoader::Asynchronous
854 : QQmlTypeLoader::PreferSynchronous;
855 QQmlRefPointer<QQmlTypeData> data = QQmlTypeLoader::get(m_engine)->getType(m_url, loaderMode);
856
857 if (data->isCompleteOrError()) {
858 fromTypeData(data);
859 setProgress(1.0);
860 } else {
861 m_typeData = data;
862 m_typeData->registerCallback(this);
863 setProgress(data->progress());
864 }
865
866 emit q->statusChanged(q->status());
867}
868
869/*!
870 Returns the list of errors that occurred during the last compile or create
871 operation. An empty list is returned if isError() is not set.
872*/
873QList<QQmlError> QQmlComponent::errors() const
874{
875 Q_D(const QQmlComponent);
876 QList<QQmlError> errors;
877 errors.reserve(d->m_state.errors.size());
878 for (const QQmlComponentPrivate::AnnotatedQmlError &annotated : d->m_state.errors)
879 errors.emplaceBack(annotated.error);
880 return errors;
881}
882
883/*!
884 \qmlmethod string Component::errorString()
885
886 Returns a human-readable description of any error.
887
888 The string includes the file, location, and description of each error.
889 If multiple errors are present, they are separated by a newline character.
890
891 If no errors are present, an empty string is returned.
892*/
893
894/*!
895 \internal
896 errorString() is only meant as a way to get the errors from QML side.
897*/
898QString QQmlComponent::errorString() const
899{
900 Q_D(const QQmlComponent);
901 QString ret;
902 if(!isError())
903 return ret;
904 for (const QQmlComponentPrivate::AnnotatedQmlError &annotated : d->m_state.errors) {
905 ret += annotated.error.toString() + QLatin1Char('\n');
906 }
907 return ret;
908}
909
910/*!
911 \qmlproperty url Component::url
912 The component URL. This is the URL that was used to construct the component.
913*/
914
915/*!
916 \property QQmlComponent::url
917 The component URL. This is the URL passed to either the constructor,
918 or the loadUrl(), or setData() methods.
919*/
920QUrl QQmlComponent::url() const
921{
922 Q_D(const QQmlComponent);
923 return d->m_url;
924}
925
926/*!
927 \internal
928*/
929QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
930 : QObject(dd, parent)
931{
932}
933
934/*!
935 Create an object instance from this component, within the specified \a context.
936 Returns \nullptr if creation failed.
937
938 If \a context is \nullptr (the default), it will create the instance in the
939 \l {QQmlEngine::rootContext()}{root context} of the engine.
940
941 The ownership of the returned object instance is transferred to the caller.
942
943 If the object being created from this component is a visual item, it must
944 have a visual parent, which can be set by calling
945 QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick}
946 for more details.
947
948 \sa QQmlEngine::ObjectOwnership
949*/
950QObject *QQmlComponent::create(QQmlContext *context)
951{
952 Q_D(QQmlComponent);
953 return d->createWithProperties(
954 nullptr, QVariantMap {}, context, QQmlComponentPrivate::CreateBehavior::Cpp);
955}
956
957/*!
958 Create an object instance of this component, within the specified \a context,
959 and initialize its top-level properties with \a initialProperties.
960
961 \omit
962 TODO: also mention errorString() when QTBUG-93239 is fixed
963 \endomit
964
965 If any of the \a initialProperties cannot be set, a warning is issued. If
966 there are unset required properties, the object creation fails and returns
967 \c nullptr, in which case \l isError() will return \c true.
968
969 If \a context is \nullptr (the default), it will create the instance in the
970 \l {QQmlEngine::rootContext()}{root context} of the engine.
971
972 The ownership of the returned object instance is transferred to the caller.
973
974 \sa QQmlComponent::create
975 \since 5.14
976*/
977QObject *QQmlComponent::createWithInitialProperties(
978 const QVariantMap& initialProperties, QQmlContext *context)
979{
980 Q_D(QQmlComponent);
981 return d->createWithProperties(
982 nullptr, initialProperties, context, QQmlComponentPrivate::CreateBehavior::Cpp);
983}
984
985static void QQmlComponent_setQmlParent(QObject *me, QObject *parent); // forward declaration
986
987/*! \internal
988 */
989QObject *QQmlComponentPrivate::createWithProperties(
990 QObject *parent, const QVariantMap &properties,
991 QQmlContext *context, CreateBehavior behavior)
992{
993 Q_Q(QQmlComponent);
994
995 QObject *rv = doBeginCreate(q, context);
996 if (!rv) {
997 if (m_state.isCompletePending()) {
998 // overridden completCreate might assume that
999 // the object has actually been created
1000 ++creationDepth;
1001 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine);
1002 complete(ep, &m_state);
1003 --creationDepth;
1004 }
1005 return nullptr;
1006 }
1007
1008 QQmlComponent_setQmlParent(rv, parent); // internally checks if parent is nullptr
1009
1010 if (behavior == CreateBehavior::Qml) {
1011 bool ok = true;
1012 for (auto it = properties.cbegin(), end = properties.cend(); it != end; ++it)
1013 ok = setInitialProperty(rv, it.key(), it.value()) && ok;
1014 q->completeCreate();
1015 if (m_state.hasUnsetRequiredProperties()) {
1016 for (const auto &unsetRequiredProperty : std::as_const(*m_state.requiredProperties())) {
1017 const QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1018 qmlWarning(rv, error);
1019 }
1020 delete std::exchange(rv, nullptr);
1021 } else if (!ok) {
1022 // We've already warned about this before
1023 delete std::exchange(rv, nullptr);
1024 }
1025 } else {
1026 setInitialProperties(rv, properties);
1027 q->completeCreate();
1028 if (m_state.hasUnsetRequiredProperties())
1029 delete std::exchange(rv, nullptr);
1030 }
1031
1032 return rv;
1033}
1034
1035/*!
1036 Create an object instance from this component, within the specified \a context.
1037 Returns \nullptr if creation failed.
1038
1039 \note This method provides advanced control over component instance creation.
1040 In general, programmers should use QQmlComponent::create() to create object
1041 instances.
1042
1043 When QQmlComponent constructs an instance, it occurs in three steps:
1044
1045 \list 1
1046 \li The object hierarchy is created, and constant values are assigned.
1047 \li Property bindings are evaluated for the first time.
1048 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
1049 \endlist
1050
1051 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
1052 only performs step 1. QQmlComponent::completeCreate() must be called to
1053 complete steps 2 and 3.
1054
1055 This breaking point is sometimes useful when using attached properties to
1056 communicate information to an instantiated component, as it allows their
1057 initial values to be configured before property bindings take effect.
1058
1059 The ownership of the returned object instance is transferred to the caller.
1060
1061 \note The categorization of bindings into constant values and actual
1062 bindings is intentionally unspecified and may change between versions of Qt
1063 and depending on whether and how you are using \l{qmlcachegen}. You should
1064 not rely on any particular binding to be evaluated either before or after
1065 beginCreate() returns. For example a constant expression like
1066 \e{MyType.EnumValue} may be recognized as such at compile time or deferred
1067 to be executed as binding. The same holds for constant expressions like
1068 \e{-(5)} or \e{"a" + " constant string"}.
1069
1070 \sa completeCreate(), QQmlEngine::ObjectOwnership
1071*/
1072QObject *QQmlComponent::beginCreate(QQmlContext *context)
1073{
1074 Q_D(QQmlComponent);
1075 Q_ASSERT(context);
1076 return d->beginCreate(QQmlContextData::get(context));
1077}
1078
1079static QQmlParserStatus *parserStatusCast(const QQmlType &type, QObject *rv)
1080{
1081 const int parserStatusCast = type.parserStatusCast();
1082 return parserStatusCast == -1
1083 ? nullptr
1084 : reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(rv) + parserStatusCast);
1085}
1086
1087QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> context)
1088{
1089 Q_Q(QQmlComponent);
1090 auto cleanup = qScopeGuard([this] {
1091 if (!m_state.errors.isEmpty() && lcQmlComponentGeneral().isDebugEnabled()) {
1092 for (const auto &e : std::as_const(m_state.errors)) {
1093 qCDebug(lcQmlComponentGeneral) << "QQmlComponent: " << e.error.toString();
1094 }
1095 }
1096 });
1097 if (!context) {
1098 qWarning("QQmlComponent: Cannot create a component in a null context");
1099 return nullptr;
1100 }
1101
1102 if (!context->isValid()) {
1103 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1104 return nullptr;
1105 }
1106
1107 if (context->engine() != m_engine) {
1108 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1109 return nullptr;
1110 }
1111
1112 if (m_state.isCompletePending()) {
1113 qWarning("QQmlComponent: Cannot create new component instance before completing the previous");
1114 return nullptr;
1115 }
1116
1117 // filter out temporary errors as they do not really affect component's
1118 // state (they are not part of the document compilation)
1119 m_state.errors.removeIf([](const auto &e) { return e.isTransient; });
1120 m_state.clearRequiredProperties();
1121
1122 if (!q->isReady()) {
1123 qWarning("QQmlComponent: Component is not ready");
1124 return nullptr;
1125 }
1126
1127 // Do not create infinite recursion in object creation
1128 static const int maxCreationDepth = 10;
1129 if (creationDepth >= maxCreationDepth) {
1130 qWarning("QQmlComponent: Component creation is recursing - aborting");
1131 return nullptr;
1132 }
1133
1134 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(m_engine);
1135
1136 enginePriv->inProgressCreations++;
1137 m_state.errors.clear();
1138 m_state.setCompletePending(true);
1139
1140 QObject *rv = nullptr;
1141 auto setupDData = [&]() {
1142 QQmlData *ddata = QQmlData::get(rv);
1143 Q_ASSERT(ddata);
1144 // top-level objects should never get JS ownership.
1145 // if JS ownership is needed this needs to be explicitly undone (like in createObject())
1146 ddata->indestructible = true;
1147 ddata->explicitIndestructibleSet = true;
1148 ddata->rootObjectInCreation = false;
1149
1150 // Assign parent context to the object if we haven't created one.
1151 if (!ddata->outerContext)
1152 ddata->outerContext = context.data();
1153 if (!ddata->context)
1154 ddata->context = context.data();
1155 };
1156
1157 const QQmlType type = loadedType();
1158 if (!type.isValid()) {
1159 enginePriv->referenceScarceResources();
1160 const QString *icName = m_inlineComponentName.get();
1161 m_state.initCreator(
1162 context, m_compilationUnit, m_creationContext, icName ? *icName : QString());
1163
1164 QQmlObjectCreator::CreationFlags flags;
1165 if (icName) {
1166 flags = QQmlObjectCreator::InlineComponent;
1167 if (m_start == -1)
1168 m_start = m_compilationUnit->inlineComponentId(*icName);
1169 Q_ASSERT(m_start > 0);
1170 } else {
1171 flags = QQmlObjectCreator::NormalObject;
1172 }
1173
1174 rv = m_state.creator()->create(m_start, nullptr, nullptr, flags);
1175 if (!rv)
1176 m_state.appendCreatorErrors();
1177 else
1178 setupDData();
1179 enginePriv->dereferenceScarceResources();
1180 } else {
1181 // TODO: extract into function
1182 rv = type.createWithQQmlData();
1183 QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(rv);
1184 setupDData();
1185 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv)) {
1186 parserStatus->classBegin();
1187 m_state.ensureRequiredPropertyStorage(rv);
1188 } else if (type.finalizerCast() != -1) {
1189 m_state.ensureRequiredPropertyStorage(rv);
1190 }
1191
1192 if (propertyCache) {
1193 for (int i = 0, propertyCount = propertyCache->propertyCount(); i < propertyCount; ++i) {
1194 if (const QQmlPropertyData *propertyData = propertyCache->property(i); propertyData->isRequired()) {
1195 m_state.ensureRequiredPropertyStorage(rv);
1196 RequiredPropertyInfo info;
1197 info.propertyName = propertyData->name(rv);
1198 m_state.addPendingRequiredProperty(rv, propertyData, info);
1199 }
1200 }
1201 } else {
1202 // we couldn't get a propertyCache from ensurePropertyCache
1203 // it is unclear what we can do in that case
1204 // ### TOOD: QTBUG-136560
1205 }
1206 }
1207
1208 return rv;
1209}
1210
1211void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
1212 QObject *object, DeferredState *deferredState)
1213{
1214 QQmlData *ddata = QQmlData::get(object);
1215 Q_ASSERT(!ddata->deferredData.isEmpty());
1216
1217 deferredState->reserve(ddata->deferredData.size());
1218
1219 for (QQmlData::DeferredData *deferredData : std::as_const(ddata->deferredData)) {
1220 enginePriv->inProgressCreations++;
1221
1222 ConstructionState state;
1223 state.setCompletePending(true);
1224
1225 auto creator = state.initCreator(
1226 deferredData->context->parent(),
1227 deferredData->compilationUnit,
1228 QQmlRefPointer<QQmlContextData>(),
1229 deferredData->inlineComponentName
1230 );
1231
1232 if (!creator->populateDeferredProperties(object, deferredData))
1233 state.appendCreatorErrors();
1234 deferredData->bindings.clear();
1235
1236 deferredState->push_back(std::move(state));
1237 }
1238}
1239
1240void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState)
1241{
1242 for (ConstructionState &state : *deferredState)
1243 complete(enginePriv, &state);
1244}
1245
1246void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
1247{
1248 if (state->isCompletePending()) {
1249 QQmlInstantiationInterrupt interrupt;
1250 state->creator()->finalize(interrupt);
1251
1252 state->setCompletePending(false);
1253
1254 enginePriv->inProgressCreations--;
1255
1256 if (0 == enginePriv->inProgressCreations) {
1257 while (enginePriv->erroredBindings) {
1258 enginePriv->warning(enginePriv->erroredBindings->removeError());
1259 }
1260 }
1261 }
1262}
1263
1264/*!
1265 \internal
1266 Finds the matching top-level property with name \a name of the component \a createdComponent.
1267 If it was a required property or an alias to a required property contained in \a
1268 requiredProperties, it is removed from it.
1269 \a requiredProperties must be non-null.
1270
1271 If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
1272 was found in requiredProperties.
1273
1274 Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
1275 for further processing (for instance, actually setting the property value).
1276
1277 Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
1278 classes which create components should not need it and should only need to call
1279 setInitialProperties.
1280 */
1281QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
1282 QObject *target, const QString &name, RequiredProperties *requiredProperties,
1283 QQmlEngine *engine, bool *wasInRequiredProperties)
1284{
1285 Q_ASSERT(requiredProperties);
1286
1287 const QQmlProperty prop(target, name, engine);
1288 if (!prop.isValid()) {
1289 if (wasInRequiredProperties)
1290 *wasInRequiredProperties = false;
1291 return prop;
1292 }
1293
1294 const QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop);
1295 bool found = false;
1296
1297 // resolve outstanding required properties
1298 const QQmlPropertyData *targetProp = &privProp->core;
1299 QQmlData *data = QQmlData::get(target);
1300 Q_ASSERT(data && data->propertyCache);
1301
1302 if (targetProp->isAlias()) {
1303 if (requiredProperties->remove(
1304 { target, data->propertyCache->property(targetProp->coreIndex()) })) {
1305 found = true;
1306 }
1307
1308 QQmlPropertyIndex originalIndex(targetProp->coreIndex());
1309 QQmlPropertyIndex propIndex;
1310 QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
1311 data = QQmlData::get(target);
1312 Q_ASSERT(data && data->propertyCache);
1313 targetProp = data->propertyCache->property(propIndex.coreIndex());
1314 } else {
1315 // we need to get the pointer from the property cache instead of directly using
1316 // targetProp, or else the lookup will fail
1317 targetProp = data->propertyCache->property(targetProp->coreIndex());
1318 }
1319
1320 // Check if the resolved target property itself is required.
1321 if (requiredProperties->remove({target, targetProp}))
1322 found = true;
1323
1324 if (wasInRequiredProperties)
1325 *wasInRequiredProperties = found;
1326
1327 return prop;
1328}
1329
1330/*!
1331 This method provides advanced control over component instance creation.
1332 In general, programmers should use QQmlComponent::create() to create a
1333 component.
1334
1335 This function completes the component creation begun with QQmlComponent::beginCreate()
1336 and must be called afterwards.
1337
1338 \sa beginCreate()
1339*/
1340void QQmlComponent::completeCreate()
1341{
1342 Q_D(QQmlComponent);
1343
1344 d->completeCreate();
1345}
1346
1347void QQmlComponentPrivate::completeCreate()
1348{
1349 if (m_state.hasUnsetRequiredProperties()) {
1350 for (const auto& unsetRequiredProperty: std::as_const(*m_state.requiredProperties())) {
1351 QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1352 m_state.errors.push_back(QQmlComponentPrivate::AnnotatedQmlError { error, true });
1353 }
1354 }
1355
1356 const QQmlType type = loadedType();
1357 if (type.isValid()) {
1358 QObject *rv = m_state.target();
1359 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv))
1360 parserStatus->componentComplete();
1361
1362 if (const int finalizerCast = type.finalizerCast(); finalizerCast != -1) {
1363 auto *hook = reinterpret_cast<QQmlFinalizerHook *>(
1364 reinterpret_cast<char *>(rv) + finalizerCast);
1365 hook->componentFinalized();
1366 }
1367
1368 /*
1369 We can directly set completePending to false, as finalize is only concerned
1370 with setting up pending bindings, but that cannot happen here, as we're
1371 dealing with a pure C++ type, which cannot have pending bindings
1372 */
1373 m_state.setCompletePending(false);
1374 QQmlEnginePrivate::get(m_engine)->inProgressCreations--;
1375 } else if (m_state.isCompletePending()) {
1376 ++creationDepth;
1377 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine);
1378 complete(ep, &m_state);
1379 --creationDepth;
1380 }
1381}
1382
1383QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
1384: QObject(parent), m_prev(nullptr), m_next(nullptr)
1385{
1386}
1387
1388QQmlComponentAttached::~QQmlComponentAttached()
1389{
1390 if (m_prev) *m_prev = m_next;
1391 if (m_next) m_next->m_prev = m_prev;
1392 m_prev = nullptr;
1393 m_next = nullptr;
1394}
1395
1396/*!
1397 \internal
1398*/
1399QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
1400{
1401 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
1402
1403 QQmlEngine *engine = qmlEngine(obj);
1404 if (!engine)
1405 return a;
1406
1407 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
1408 if (p->activeObjectCreator) { // XXX should only be allowed during begin
1409 a->insertIntoList(p->activeObjectCreator->componentAttachment());
1410 } else {
1411 QQmlData *d = QQmlData::get(obj);
1412 Q_ASSERT(d);
1413 Q_ASSERT(d->context);
1414 d->context->addComponentAttached(a);
1415 }
1416
1417 return a;
1418}
1419
1420/*!
1421 Load the QQmlComponent for \a typeName in the module \a uri.
1422 If the type is implemented via a QML file, \a mode is used to
1423 load it. Types backed by C++ are always loaded synchronously.
1424
1425 \code
1426 QQmlEngine engine;
1427 QQmlComponent component(&engine);
1428 component.loadFromModule("QtQuick", "Item");
1429 // once the component is ready
1430 std::unique_ptr<QObject> item(component.create());
1431 Q_ASSERT(item->metaObject() == &QQuickItem::staticMetaObject);
1432 \endcode
1433
1434 \since 6.5
1435 \sa loadUrl()
1436 */
1437void QQmlComponent::loadFromModule(QAnyStringView uri, QAnyStringView typeName,
1438 QQmlComponent::CompilationMode mode)
1439{
1440 Q_D(QQmlComponent);
1441
1442 QQmlTypeLoader::Mode typeLoaderMode = QQmlTypeLoader::Synchronous;
1443 switch (mode) {
1444 case QQmlComponent::PreferSynchronous:
1445 typeLoaderMode = QQmlTypeLoader::PreferSynchronous;
1446 break;
1447 case QQmlComponent::Asynchronous:
1448 typeLoaderMode = QQmlTypeLoader::Asynchronous;
1449 break;
1450 }
1451
1452 d->prepareLoadFromModule(uri, typeName, typeLoaderMode);
1453 if (d->m_loadHelper->isCompleteOrError())
1454 d->completeLoadFromModule(uri, typeName);
1455 else
1456 d->m_loadHelper->registerCallback(d);
1457}
1458
1459void QQmlComponentPrivate::prepareLoadFromModule(
1460 QAnyStringView uri, QAnyStringView typeName, QQmlTypeLoader::Mode mode)
1461{
1462 // Don't let any old loadHelper call us back anymore.
1463 if (m_loadHelper)
1464 m_loadHelper->unregisterCallback(this);
1465
1466 // LoadHelper must be on the Heap as it derives from QQmlRefCount
1467 m_loadHelper = QQml::makeRefPointer<LoadHelper>(QQmlTypeLoader::get(m_engine), uri, typeName, mode);
1468}
1469
1470void QQmlComponentPrivate::completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName)
1471{
1472 Q_Q(QQmlComponent);
1473
1474 // we always mimic the progressChanged behavior from loadUrl
1475 auto reportError = [&](QString msg) {
1476 QQmlError error;
1477 error.setDescription(msg);
1478 m_state.errors.push_back(std::move(error));
1479 setProgress(1);
1480 emit q->statusChanged(q->Error);
1481 };
1482 auto emitComplete = [&]() {
1483 setProgress(1);
1484 emit q->statusChanged(q->status());
1485 };
1486
1487 setProgress(0);
1488
1489 const QQmlType type = m_loadHelper->type();
1490
1491 if (m_loadHelper->resolveTypeResult() == LoadHelper::ResolveTypeResult::NoSuchModule) {
1492 reportError(QLatin1String(R"(No module named "%1" found)").arg(uri.toString()));
1493 } else if (!type.isValid()) {
1494 reportError(QLatin1String(R"(Module "%1" contains no type named "%2")")
1495 .arg(uri.toString(), typeName.toString()));
1496 } else if (type.isCreatable()) {
1497 emitComplete();
1498 } else if (type.isInlineComponent()) {
1499 auto baseUrl = type.sourceUrl();
1500 baseUrl.setFragment(QString());
1501
1502 Q_ASSERT(m_progress == 0.0);
1503 {
1504 // we don't want to emit status changes from the "helper" loadUrl below
1505 // because it would signal success to early
1506 QSignalBlocker blockSignals(q);
1507 // we really need to continue in a synchronous way, otherwise we can't check the CU
1508 loadUrl(baseUrl, QQmlComponent::PreferSynchronous);
1509 }
1510 // We do want to emit any progress change that happened in the "helper" loadUrl.
1511 if (m_progress != 0.0)
1512 emit q->progressChanged(m_progress);
1513
1514 if (q->isError()) {
1515 emitComplete();
1516 return;
1517 }
1518 QString elementName = type.elementName();
1519 if (m_compilationUnit->inlineComponentId(elementName) == -1) {
1520 QString realTypeName = typeName.toString();
1521 realTypeName.truncate(realTypeName.indexOf(u'.'));
1522 QString errorMessage = R"(Type "%1" from module "%2" contains no inline component named "%3".)"_L1.arg(
1523 realTypeName, uri.toString(), elementName);
1524 if (elementName == u"qml")
1525 errorMessage += " To load the type \"%1\", drop the \".qml\" extension."_L1.arg(realTypeName);
1526 reportError(std::move(errorMessage));
1527 } else {
1528 m_inlineComponentName = std::make_unique<QString>(std::move(elementName));
1529 emitComplete();
1530 }
1531 } else if (type.isComposite()) {
1532 QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous;
1533 switch (m_loadHelper->mode()) {
1534 case QQmlTypeLoader::Asynchronous:
1535 mode = QQmlComponent::Asynchronous;
1536 break;
1537 case QQmlTypeLoader::PreferSynchronous:
1538 case QQmlTypeLoader::Synchronous:
1539 mode = QQmlComponent::PreferSynchronous;
1540 break;
1541 }
1542
1543 // loadUrl takes care of signal emission
1544 loadUrl(type.sourceUrl(), mode);
1545 } else if (type.isSingleton()) {
1546 // TODO: This is meant to cover composite singletons but doesn't.
1547 reportError(QLatin1String(R"(%1 is a singleton, and cannot be loaded)").arg(typeName.toString()));
1548 } else {
1549 reportError(QLatin1String("Could not load %1, as the type is uncreatable").arg(typeName.toString()));
1550 }
1551}
1552
1553/*!
1554 Create an object instance from this component using the provided
1555 \a incubator. \a context specifies the context within which to create the object
1556 instance.
1557
1558 If \a context is \nullptr (the default), it will create the instance in the
1559 \l {QQmlEngine::rootContext()}{root context} of the engine.
1560
1561 \a forContext specifies a context that this object creation depends upon.
1562 If the \a forContext is being created asynchronously, and the
1563 \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
1564 this object will also be created asynchronously.
1565 If \a forContext is \nullptr (by default), the \a context will be used for this decision.
1566
1567 The created object and its creation status are available via the
1568 \a incubator.
1569
1570 \sa QQmlIncubator
1571*/
1572
1573void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlContext *forContext)
1574{
1575 Q_D(QQmlComponent);
1576
1577 if (!context)
1578 context = d->m_engine->rootContext();
1579
1580 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
1581 QQmlRefPointer<QQmlContextData> forContextData =
1582 forContext ? QQmlContextData::get(forContext) : contextData;
1583
1584 if (!contextData->isValid()) {
1585 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1586 return;
1587 }
1588
1589 if (contextData->engine() != d->m_engine) {
1590 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1591 return;
1592 }
1593
1594 if (!isReady()) {
1595 qWarning("QQmlComponent: Component is not ready");
1596 return;
1597 }
1598
1599 incubator.clear();
1600 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d);
1601
1602 if (d->loadedType().isValid()) {
1603 // there isn't really an incubation process for C++ backed types
1604 // so just create the object and signal that we are ready
1605
1606 p->incubateCppBasedComponent(this, context);
1607 return;
1608 }
1609
1610 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->m_engine);
1611
1612 p->compilationUnit = d->m_compilationUnit;
1613 p->enginePriv = enginePriv;
1614 p->creator.reset(new QQmlObjectCreator(
1615 contextData, d->m_compilationUnit, d->m_creationContext,
1616 d->m_inlineComponentName ? *d->m_inlineComponentName : QString(), p.data()));
1617 p->subComponentToCreate = d->m_start;
1618
1619 enginePriv->incubate(incubator, forContextData);
1620}
1621
1622/*!
1623 Set top-level \a properties of the \a object that was created from a
1624 QQmlComponent.
1625
1626 This method provides advanced control over component instance creation.
1627 In general, programmers should use
1628 \l QQmlComponent::createWithInitialProperties to create an object instance
1629 from a component.
1630
1631 Use this method after beginCreate and before completeCreate has been called.
1632 If a provided property does not exist, a warning is issued.
1633
1634 This method does not allow setting initial nested properties directly.
1635 Instead, setting an initial value for value type properties with nested
1636 properties can be achieved by creating that value type, assigning its nested
1637 property and then passing the value type as an initial property of the
1638 object to be constructed.
1639
1640 For example, in order to set fond.bold, you can create a QFont, set its
1641 weight to bold and then pass the font as an initial property.
1642
1643 \since 5.14
1644*/
1645void QQmlComponent::setInitialProperties(QObject *object, const QVariantMap &properties)
1646{
1647 Q_D(QQmlComponent);
1648 d->setInitialProperties(object, properties);
1649}
1650
1651bool QQmlComponentPrivate::setInitialProperties(QObject *object, const QVariantMap &properties)
1652{
1653 bool result = true;
1654 for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) {
1655 if (it.key().contains(u'.')) {
1656 auto segments = it.key().split(u'.');
1657 QString description = u"Setting initial properties failed: Cannot initialize nested "_s
1658 u"property."_s;
1659 if (segments.size() >= 2) {
1660 QString s = u" To set %1.%2 as an initial property, create %1, set its "_s
1661 u"property %2, and pass %1 as an initial property."_s;
1662 description += s.arg(segments[0], segments[1]);
1663 }
1664 QQmlError error{};
1665 error.setUrl(m_url);
1666 error.setDescription(description);
1667 qmlWarning(object, error);
1668 return false;
1669 }
1670
1671 // Still try to set them, even if a previous one has failed.
1672 result = setInitialProperty(object, it.key(), it.value()) && result;
1673 }
1674 return result;
1675}
1676
1677/*
1678 This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
1679 arguments instead of QQmlContext which means we don't have to construct the rather weighty
1680 wrapper class for every delegate item.
1681
1682 This is used by QQmlDelegateModel.
1683*/
1684void QQmlComponentPrivate::incubateObject(
1685 QQmlIncubator *incubationTask,
1686 QQmlComponent *component,
1687 QQmlEngine *engine,
1688 const QQmlRefPointer<QQmlContextData> &context,
1689 const QQmlRefPointer<QQmlContextData> &forContext)
1690{
1691 QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask);
1692 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
1693 QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component);
1694
1695 incubatorPriv->compilationUnit = componentPriv->m_compilationUnit;
1696 incubatorPriv->enginePriv = enginePriv;
1697 incubatorPriv->creator.reset(new QQmlObjectCreator(
1698 context, componentPriv->m_compilationUnit, componentPriv->m_creationContext,
1699 m_inlineComponentName ? *m_inlineComponentName : QString()));
1700
1701 if (m_start == -1) {
1702 if (const QString *icName = componentPriv->m_inlineComponentName.get()) {
1703 m_start = m_compilationUnit->inlineComponentId(*icName);
1704 Q_ASSERT(m_start > 0);
1705 }
1706 }
1707 incubatorPriv->subComponentToCreate = componentPriv->m_start;
1708
1709 enginePriv->incubate(*incubationTask, forContext);
1710}
1711
1712
1713
1715
1716namespace QV4 {
1717
1718namespace Heap {
1719
1720#define QmlIncubatorObjectMembers(class, Member)
1721 Member(class, HeapValue, HeapValue, valuemapOrObject)
1722 Member(class, HeapValue, HeapValue, statusChanged)
1723 Member(class, Pointer, QmlContext *, qmlContext)
1724 Member(class, NoMark, QQmlComponentIncubator *, incubator)
1725 Member(class, NoMark, QV4QPointer<QObject>, parent)
1726
1728 DECLARE_MARKOBJECTS(QmlIncubatorObject)
1729
1730 void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
1731 inline void destroy();
1732};
1733
1734}
1735
1737{
1738 V4_OBJECT2(QmlIncubatorObject, Object)
1740
1742 static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1743 static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1744 static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1745 static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1746
1748 void setInitialState(QObject *, RequiredProperties *requiredProperties);
1749};
1750
1751}
1752
1754
1756{
1757public:
1758 QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode)
1760 {
1761 incubatorObject.set(inc->internalClass->engine, inc);
1762 }
1763
1765 QV4::Scope scope(incubatorObject.engine());
1766 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1767 i->statusChanged(s);
1768 }
1769
1770 void setInitialState(QObject *o) override {
1771 QV4::Scope scope(incubatorObject.engine());
1772 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1773 auto d = QQmlIncubatorPrivate::get(this);
1774 i->setInitialState(o, d->requiredProperties());
1775 }
1776
1777 QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating
1778};
1779
1780
1781static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
1782{
1783 if (parent) {
1784 me->setParent(parent);
1785 typedef QQmlPrivate::AutoParentFunction APF;
1786 QList<APF> functions = QQmlMetaType::parentFunctions();
1787
1788 bool needParent = false;
1789 for (int ii = 0; ii < functions.size(); ++ii) {
1790 QQmlPrivate::AutoParentResult res = functions.at(ii)(me, parent);
1791 if (res == QQmlPrivate::Parented) {
1792 needParent = false;
1793 break;
1794 } else if (res == QQmlPrivate::IncompatibleParent) {
1795 needParent = true;
1796 }
1797 }
1798 if (needParent)
1799 qmlWarning(me) << "Created graphical object was not placed in the graphics scene.";
1800 }
1801}
1802
1803/*!
1804 \qmlmethod QtObject Component::createObject(QtObject parent, var properties)
1805
1806 Creates and returns an object instance of this component that will have
1807 the given \a parent and \a properties. The \a properties argument is optional.
1808 Returns null if object creation fails.
1809
1810 The object will be created in the same context as the one in which the component
1811 was created. This function will always return null when called on components
1812 which were not created in QML.
1813
1814 If you wish to create an object without setting a parent, specify \c null for
1815 the \a parent value. Note that if the returned object is to be displayed, you
1816 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
1817 property, otherwise the object will not be visible.
1818
1819 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
1820 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
1821 because setting the Item parent does not change object ownership. Only the graphical parent is changed.
1822
1823 This method accepts an optional \a properties argument that specifies a
1824 map of initial property values for the created object. These values are applied before the object
1825 creation is finalized. This is more efficient than setting property values after object creation,
1826 particularly where large sets of property values are defined, and also allows property bindings
1827 to be set up (using \l{Qt::binding}{Qt.binding}) before the object is created.
1828
1829 The \a properties argument is specified as a map of property-value items. For example, the code
1830 below creates an object with initial \c x and \c y values of 100 and 100, respectively:
1831
1832 \qml
1833 const component = Qt.createComponent("Button.qml");
1834 if (component.status === Component.Ready) {
1835 component.createObject(parent, { x: 100, y: 100 });
1836 }
1837 \endqml
1838
1839 Dynamically created instances can be deleted with the \c destroy() method.
1840 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1841
1842 \sa incubateObject()
1843*/
1844
1845
1846void QQmlComponentPrivate::setInitialProperties(
1847 QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
1848 const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
1849 const QQmlObjectCreator *creator)
1850{
1851 QV4::Scope scope(engine);
1852 QV4::ScopedObject object(scope);
1853 QV4::ScopedObject valueMap(scope, v);
1854 QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly);
1855 QV4::ScopedString name(scope);
1856 QV4::ScopedValue val(scope);
1857 if (engine->hasException)
1858 return;
1859
1860 // js modules (mjs) have no qmlContext
1861 QV4::ScopedStackFrame frame(scope, qmlContext ? qmlContext : engine->scriptContext());
1862
1863 while (1) {
1864 name = it.nextPropertyNameAsString(val);
1865 if (!name)
1866 break;
1867 object = o;
1868 const QStringList properties = name->toQString().split(QLatin1Char('.'));
1869 bool isTopLevelProperty = properties.size() == 1;
1870 for (int i = 0; i < properties.size() - 1; ++i) {
1871 name = engine->newString(properties.at(i));
1872 object = object->get(name);
1873 if (engine->hasException || !object) {
1874 break;
1875 }
1876 }
1877 if (engine->hasException) {
1878 qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
1879 continue;
1880 }
1881 if (!object) {
1882 QQmlError error;
1883 error.setUrl(qmlContext ? qmlContext->qmlContext()->url() : QUrl());
1884 error.setDescription(QLatin1String("Cannot resolve property \"%1\".")
1885 .arg(properties.join(u'.')));
1886 qmlWarning(createdComponent, error);
1887 continue;
1888 }
1889 const QString lastProperty = properties.last();
1890 name = engine->newString(lastProperty);
1891 object->put(name, val);
1892 if (engine->hasException) {
1893 qmlWarning(createdComponent, engine->catchExceptionAsQmlError());
1894 continue;
1895 } else if (isTopLevelProperty && requiredProperties) {
1896 auto prop = removePropertyFromRequired(createdComponent, name->toQString(),
1897 requiredProperties, engine->qmlEngine());
1898 }
1899
1900 removePendingQPropertyBinding(object, lastProperty, creator);
1901 }
1902
1903 engine->hasException = false;
1904}
1905
1906QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty)
1907{
1908 QQmlError error;
1909 QString description = QLatin1String("Required property %1 was not initialized").arg(unsetRequiredProperty.propertyName);
1910 switch (unsetRequiredProperty.aliasesToRequired.size()) {
1911 case 0:
1912 break;
1913 case 1: {
1914 const auto info = unsetRequiredProperty.aliasesToRequired.first();
1915 description += QLatin1String("\nIt can be set via the alias property %1 from %2\n").arg(info.propertyName, info.fileUrl.toString());
1916 break;
1917 }
1918 default:
1919 description += QLatin1String("\nIt can be set via one of the following alias properties:");
1920 for (const auto &aliasInfo: unsetRequiredProperty.aliasesToRequired) {
1921 description += QLatin1String("\n- %1 (%2)").arg(aliasInfo.propertyName, aliasInfo.fileUrl.toString());
1922 }
1923 description += QLatin1Char('\n');
1924 }
1925 error.setDescription(description);
1926 error.setUrl(unsetRequiredProperty.fileUrl);
1927 error.setLine(qmlConvertSourceCoordinate<quint32, int>(
1928 unsetRequiredProperty.location.line()));
1929 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
1930 unsetRequiredProperty.location.column()));
1931 return error;
1932}
1933
1934#if QT_DEPRECATED_SINCE(6, 3)
1935/*!
1936 \internal
1937*/
1938void QQmlComponent::createObject(QQmlV4FunctionPtr args)
1939{
1940 Q_D(QQmlComponent);
1941 Q_ASSERT(d->m_engine);
1942 Q_ASSERT(args);
1943
1944 qmlWarning(this) << "Unsuitable arguments passed to createObject(). The first argument should "
1945 "be a QObject* or null, and the second argument should be a JavaScript "
1946 "object or a QVariantMap";
1947
1948 QObject *parent = nullptr;
1949 QV4::ExecutionEngine *v4 = args->v4engine();
1950 QV4::Scope scope(v4);
1951 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
1952
1953 if (args->length() >= 1) {
1954 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
1955 if (qobjectWrapper)
1956 parent = qobjectWrapper->object();
1957 }
1958
1959 if (args->length() >= 2) {
1960 QV4::ScopedValue v(scope, (*args)[1]);
1961 if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
1962 qmlWarning(this) << tr("createObject: value is not an object");
1963 args->setReturnValue(QV4::Encode::null());
1964 return;
1965 }
1966 valuemap = v;
1967 }
1968
1969 QQmlContext *ctxt = creationContext();
1970 if (!ctxt) ctxt = d->m_engine->rootContext();
1971
1972 QObject *rv = beginCreate(ctxt);
1973
1974 if (!rv) {
1975 args->setReturnValue(QV4::Encode::null());
1976 return;
1977 }
1978
1979 QQmlComponent_setQmlParent(rv, parent);
1980
1981 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4, rv));
1982 Q_ASSERT(object->isObject());
1983
1984 if (!valuemap->isUndefined()) {
1985 QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
1986 QQmlComponentPrivate::setInitialProperties(
1987 v4, qmlContext, object, valuemap, d->m_state.requiredProperties(), rv,
1988 d->m_state.creator());
1989 }
1990 if (d->m_state.hasUnsetRequiredProperties()) {
1991 QList<QQmlError> errors;
1992 for (const auto &requiredProperty: std::as_const(*d->m_state.requiredProperties())) {
1993 errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(requiredProperty));
1994 }
1995 qmlWarning(rv, errors);
1996 args->setReturnValue(QV4::Encode::null());
1997 delete rv;
1998 return;
1999 }
2000
2001 d->completeCreate();
2002
2003 Q_ASSERT(QQmlData::get(rv));
2004 QQmlData::get(rv)->explicitIndestructibleSet = false;
2005 QQmlData::get(rv)->indestructible = false;
2006
2007 args->setReturnValue(object->asReturnedValue());
2008}
2009#endif
2010
2011/*!
2012 \internal
2013 */
2014QObject *QQmlComponent::createObject(QObject *parent, const QVariantMap &properties)
2015{
2016 Q_D(QQmlComponent);
2017 Q_ASSERT(d->m_engine);
2018 QObject *rv = d->createWithProperties(
2019 parent, properties, creationContext(), QQmlComponentPrivate::CreateBehavior::Qml);
2020 if (rv) {
2021 QQmlData *qmlData = QQmlData::get(rv);
2022 Q_ASSERT(qmlData);
2023 qmlData->explicitIndestructibleSet = false;
2024 qmlData->indestructible = false;
2025 }
2026 return rv;
2027}
2028
2029/*!
2030 \qmlmethod var Component::incubateObject(QtObject parent, var properties, enumeration mode)
2031
2032 Creates an incubator for an instance of this component. Incubators allow new component
2033 instances to be instantiated asynchronously and do not cause freezes in the UI.
2034
2035 The \a parent argument specifies the parent the created instance will have. Omitting the
2036 parameter or passing null will create an object with no parent. In this case, a reference
2037 to the created object must be held so that it is not destroyed by the garbage collector.
2038
2039 The \a properties argument is specified as a map of property-value items which will be
2040 set on the created object during its construction. \a mode may be Qt.Synchronous or
2041 Qt.Asynchronous, and controls whether the instance is created synchronously or asynchronously.
2042 The default is asynchronous. In some circumstances, even if Qt.Synchronous is specified,
2043 the incubator may create the object asynchronously. This happens if the component calling
2044 incubateObject() is itself being created asynchronously.
2045
2046 All three arguments are optional.
2047
2048 If successful, the method returns an incubator, otherwise null. The incubator has the following
2049 properties:
2050
2051 \list
2052 \li \c status - The status of the incubator. Valid values are Component.Ready, Component.Loading and
2053 Component.Error.
2054 \li \c object - The created object instance. Will only be available once the incubator is in the
2055 Ready status.
2056 \li \c onStatusChanged - Specifies a callback function to be invoked when the status changes. The
2057 status is passed as a parameter to the callback.
2058 \li \c{forceCompletion()} - Call to complete incubation synchronously.
2059 \endlist
2060
2061 The following example demonstrates how to use an incubator:
2062
2063 \qml
2064 const component = Qt.createComponent("Button.qml");
2065
2066 const incubator = component.incubateObject(parent, { x: 10, y: 10 });
2067 if (incubator.status !== Component.Ready) {
2068 incubator.onStatusChanged = function(status) {
2069 if (status === Component.Ready) {
2070 print("Object", incubator.object, "is now ready!");
2071 }
2072 };
2073 } else {
2074 print("Object", incubator.object, "is ready immediately!");
2075 }
2076 \endqml
2077
2078 Dynamically created instances can be deleted with the \c destroy() method.
2079 See \l {Dynamic QML Object Creation from JavaScript} for more information.
2080
2081 \sa createObject()
2082*/
2083
2084/*!
2085 \internal
2086*/
2087void QQmlComponent::incubateObject(QQmlV4FunctionPtr args)
2088{
2089 Q_D(QQmlComponent);
2090 Q_ASSERT(d->m_engine);
2091 Q_UNUSED(d);
2092 Q_ASSERT(args);
2093 QV4::ExecutionEngine *v4 = args->v4engine();
2094 QV4::Scope scope(v4);
2095
2096 QObject *parent = nullptr;
2097 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
2098 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
2099
2100 if (args->length() >= 1) {
2101 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
2102 if (qobjectWrapper)
2103 parent = qobjectWrapper->object();
2104 }
2105
2106 if (args->length() >= 2) {
2107 QV4::ScopedValue v(scope, (*args)[1]);
2108 if (v->isNull()) {
2109 } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
2110 qmlWarning(this) << tr("createObject: value is not an object");
2111 args->setReturnValue(QV4::Encode::null());
2112 return;
2113 } else {
2114 valuemap = v;
2115 }
2116 }
2117
2118 if (args->length() >= 3) {
2119 QV4::ScopedValue val(scope, (*args)[2]);
2120 quint32 v = val->toUInt32();
2121 if (v == 0)
2122 mode = QQmlIncubator::Asynchronous;
2123 else if (v == 1)
2124 mode = QQmlIncubator::AsynchronousIfNested;
2125 }
2126
2127 QQmlComponentExtension *e = componentExtension(args->v4engine());
2128
2129 QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode));
2130 QV4::ScopedObject p(scope, e->incubationProto.value());
2131 r->setPrototypeOf(p);
2132
2133 if (!valuemap->isUndefined())
2134 r->d()->valuemapOrObject.set(scope.engine, valuemap);
2135 r->d()->qmlContext.set(scope.engine, v4->qmlContext());
2136 r->d()->parent = parent;
2137
2138 QQmlIncubator *incubator = r->d()->incubator;
2139 create(*incubator, creationContext());
2140
2141 if (incubator->status() == QQmlIncubator::Null) {
2142 args->setReturnValue(QV4::Encode::null());
2143 } else {
2144 args->setReturnValue(r.asReturnedValue());
2145 }
2146}
2147
2148// XXX used by QSGLoader
2149void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties *requiredProperties)
2150{
2151 QV4::ExecutionEngine *v4engine = m_engine->handle();
2152 QV4::Scope scope(v4engine);
2153
2154 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate));
2155 Q_ASSERT(object->as<QV4::Object>());
2156
2157 if (!valuemap.isUndefined()) {
2158 setInitialProperties(
2159 v4engine, qmlContext, object, valuemap, requiredProperties, toCreate, m_state.creator());
2160 }
2161}
2162
2163QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
2164{
2165 QV4::Scope scope(v4);
2166 QV4::ScopedObject proto(scope, v4->newObject());
2167 proto->defineAccessorProperty(QStringLiteral("onStatusChanged"),
2168 QV4::QmlIncubatorObject::method_get_statusChanged, QV4::QmlIncubatorObject::method_set_statusChanged);
2169 proto->defineAccessorProperty(QStringLiteral("status"), QV4::QmlIncubatorObject::method_get_status, nullptr);
2170 proto->defineAccessorProperty(QStringLiteral("object"), QV4::QmlIncubatorObject::method_get_object, nullptr);
2171 proto->defineDefaultProperty(QStringLiteral("forceCompletion"), QV4::QmlIncubatorObject::method_forceCompletion);
2172
2173 incubationProto.set(v4, proto);
2174}
2175
2176QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const FunctionObject *b, const Value *thisObject, const Value *, int)
2177{
2178 QV4::Scope scope(b);
2179 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2180 if (!o)
2181 THROW_TYPE_ERROR();
2182
2183 return QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object());
2184}
2185
2186QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const FunctionObject *b, const Value *thisObject, const Value *, int)
2187{
2188 QV4::Scope scope(b);
2189 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2190 if (!o)
2191 THROW_TYPE_ERROR();
2192
2193 o->d()->incubator->forceCompletion();
2194
2195 RETURN_UNDEFINED();
2196}
2197
2198QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int)
2199{
2200 QV4::Scope scope(b);
2201 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2202 if (!o)
2203 THROW_TYPE_ERROR();
2204
2205 return QV4::Encode(o->d()->incubator->status());
2206}
2207
2208QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *, int)
2209{
2210 QV4::Scope scope(b);
2211 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2212 if (!o)
2213 THROW_TYPE_ERROR();
2214
2215 return QV4::Encode(o->d()->statusChanged);
2216}
2217
2218QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
2219{
2220 QV4::Scope scope(b);
2221 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
2222 if (!o || argc < 1)
2223 THROW_TYPE_ERROR();
2224
2225 o->d()->statusChanged.set(scope.engine, argv[0]);
2226
2227 RETURN_UNDEFINED();
2228}
2229
2230QQmlComponentExtension::~QQmlComponentExtension()
2231{
2232}
2233
2234void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
2235{
2236 Object::init();
2237 valuemapOrObject.set(internalClass->engine, QV4::Value::undefinedValue());
2238 statusChanged.set(internalClass->engine, QV4::Value::undefinedValue());
2239 parent.init();
2240 qmlContext.set(internalClass->engine, nullptr);
2241 incubator = new QQmlComponentIncubator(this, m);
2242}
2243
2244void QV4::Heap::QmlIncubatorObject::destroy() {
2245 delete incubator;
2246 parent.destroy();
2247 Object::destroy();
2248}
2249
2250void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties *requiredProperties)
2251{
2252 QQmlComponent_setQmlParent(o, d()->parent);
2253
2254 if (!d()->valuemapOrObject.isUndefined()) {
2255 QV4::ExecutionEngine *v4 = engine();
2256 QV4::Scope scope(v4);
2257 QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
2258 QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
2259 QQmlComponentPrivate::setInitialProperties(
2260 v4, qmlCtxt, obj, d()->valuemapOrObject, requiredProperties, o,
2261 QQmlIncubatorPrivate::get(d()->incubator)->creator.data());
2262 }
2263}
2264
2265void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
2266{
2267 QV4::Scope scope(engine());
2268
2269 QObject *object = d()->incubator->object();
2270
2271 if (s == QQmlIncubator::Ready) {
2272 // We don't need the arguments anymore, but we still want to hold on to the object so
2273 // that it doesn't get gc'd
2274 d()->valuemapOrObject.set(scope.engine, QV4::QObjectWrapper::wrap(scope.engine, object));
2275
2276 QQmlData *ddata = QQmlData::get(object);
2277 Q_ASSERT(ddata);
2278 ddata->explicitIndestructibleSet = false;
2279 ddata->indestructible = false;
2280 }
2281
2282 QV4::ScopedFunctionObject f(scope, d()->statusChanged);
2283 if (f) {
2284 QV4::JSCallArguments jsCallData(scope, 1);
2285 *jsCallData.thisObject = this;
2286 jsCallData.args[0] = QV4::Value::fromUInt32(s);
2287 f->call(jsCallData);
2288 if (scope.hasException()) {
2289 QQmlError error = scope.engine->catchExceptionAsQmlError();
2290 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
2291 }
2292 }
2293
2294 if (s != QQmlIncubator::Loading)
2295 d()->incubator->incubatorObject.clear();
2296}
2297
2298#undef INITIALPROPERTIES_SOURCE
2299
2300QT_END_NAMESPACE
2301
2302#include "moc_qqmlcomponent.cpp"
2303#include "moc_qqmlcomponentattached_p.cpp"
\inmodule QtCore
Definition qobject.h:106
void setInitialState(QObject *o) override
Called after the object is first created, but before complex property bindings are evaluated and,...
void statusChanged(Status s) override
Called when the status of the incubator changes.
QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode)
QV4::PersistentValue incubatorObject
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:19
Status
Specifies the status of the QQmlIncubator.
DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext)
Definition qjsvalue.h:24
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static void removePendingQPropertyBinding(QV4::Value *object, const QString &propertyName, const QQmlObjectCreator *creator)
static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
DEFINE_OBJECT_VTABLE(QV4::QmlIncubatorObject)
V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension)
static QQmlParserStatus * parserStatusCast(const QQmlType &type, QObject *rv)
static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
void statusChanged(QQmlIncubator::Status)
void setInitialState(QObject *, RequiredProperties *requiredProperties)
static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)