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.qdoc 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.qdoc 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
721/*!
722 \since 6.12
723
724 Sets the QQmlComponent to use the given QML \a data. If \a url
725 is provided, it is used to set the component name and to provide
726 a base path for items resolved by this component.
727
728 If \a compilationMode is \l Asynchronous, the component will be loaded and compiled
729 asynchronously.
730
731 \warning The new component will shadow any existing component of
732 the same URL. You should not pass a URL of an existing component.
733*/
734void QQmlComponent::setData(const QByteArray &data, const QUrl &url, CompilationMode compilationMode)
735{
736 Q_D(QQmlComponent);
737
738 if (!d->m_engine) {
739 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
740 qWarning("QQmlComponent: Must provide an engine before calling setData");
741 return;
742 }
743
744 d->clear();
745
746 d->m_url = url;
747
748 QQmlTypeLoader::Mode mode = compilationMode == Asynchronous
749 ? QQmlTypeLoader::Asynchronous
750 : QQmlTypeLoader::PreferSynchronous;
751 QQmlRefPointer<QQmlTypeData> typeData = QQmlTypeLoader::get(d->m_engine)->getType(data, url, mode);
752
753 if (typeData->isCompleteOrError()) {
754 d->fromTypeData(typeData);
755 d->setProgress(1.0);
756 } else {
757 d->m_typeData = typeData;
758 d->m_typeData->registerCallback(d);
759 d->setProgress(typeData->progress());
760 }
761
762 emit statusChanged(status());
763}
764
765/*!
766 Sets the QQmlComponent to use the given QML \a data. If \a url
767 is provided, it is used to set the component name and to provide
768 a base path for items resolved by this component.
769
770 This is equivalent to calling \c{setData(data, url, QQmlComponent::PreferSynchronous)}.
771
772 \warning The new component will shadow any existing component of
773 the same URL. You should not pass a URL of an existing component.
774
775 \overload
776*/
777void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
778{
779 setData(data, url, PreferSynchronous);
780}
781
782/*!
783 Returns the QQmlContext the component was created in. This is only
784 valid for components created directly from QML.
785*/
786QQmlContext *QQmlComponent::creationContext() const
787{
788 Q_D(const QQmlComponent);
789 if (!d->m_creationContext.isNull())
790 return d->m_creationContext->asQQmlContext();
791
792 return qmlContext(this);
793}
794
795/*!
796 Returns the QQmlEngine of this component.
797
798 \since 5.12
799*/
800QQmlEngine *QQmlComponent::engine() const
801{
802 Q_D(const QQmlComponent);
803 return d->m_engine;
804}
805
806/*!
807 Load the QQmlComponent from the provided \a url.
808
809 \include qqmlcomponent.qdoc url-note
810*/
811void QQmlComponent::loadUrl(const QUrl &url)
812{
813 Q_D(QQmlComponent);
814 d->loadUrl(url);
815}
816
817/*!
818 Load the QQmlComponent from the provided \a url.
819 If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
820
821 \include qqmlcomponent.qdoc url-note
822*/
823void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
824{
825 Q_D(QQmlComponent);
826 d->loadUrl(url, mode);
827}
828
829void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
830{
831 Q_Q(QQmlComponent);
832 clear();
833
834 if (newUrl.isRelative()) {
835 // The new URL is a relative URL like QUrl("main.qml").
836 m_url = m_engine->baseUrl().resolved(QUrl(newUrl.toString()));
837 } else if (m_engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(newUrl.toLocalFile())) {
838 // The new URL is a file on disk but it's a relative path; e.g.:
839 // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml")
840 // We need to remove the scheme so that it becomes a relative URL with a relative path:
841 QUrl fixedUrl(newUrl);
842 fixedUrl.setScheme(QString());
843 // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl().
844 // This is a compatibility hack for QTBUG-58837.
845 m_url = m_engine->baseUrl().resolved(fixedUrl);
846 } else {
847 m_url = newUrl;
848 }
849
850 if (m_url.scheme() == "qrc"_L1 && !m_url.path().startsWith("/"_L1)) {
851 qWarning().nospace().noquote()
852 << "QQmlComponent: attempted to load via a relative URL '" << m_url.toString()
853 << "' in resource file system. This is not fully supported and may not work";
854 }
855
856 if (newUrl.isEmpty()) {
857 QQmlError error;
858 error.setDescription(QQmlComponent::tr("Invalid empty URL"));
859 m_state.errors.emplaceBack(error);
860 return;
861 }
862
863 setProgress(0.0);
864
865 QQmlTypeLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
866 ? QQmlTypeLoader::Asynchronous
867 : QQmlTypeLoader::PreferSynchronous;
868 QQmlRefPointer<QQmlTypeData> data = QQmlTypeLoader::get(m_engine)->getType(m_url, loaderMode);
869
870 if (data->isCompleteOrError()) {
871 fromTypeData(data);
872 setProgress(1.0);
873 } else {
874 m_typeData = data;
875 m_typeData->registerCallback(this);
876 setProgress(data->progress());
877 }
878
879 emit q->statusChanged(q->status());
880}
881
882/*!
883 Returns the list of errors that occurred during the last compile or create
884 operation. An empty list is returned if isError() is not set.
885*/
886QList<QQmlError> QQmlComponent::errors() const
887{
888 Q_D(const QQmlComponent);
889 QList<QQmlError> errors;
890 errors.reserve(d->m_state.errors.size());
891 for (const QQmlComponentPrivate::AnnotatedQmlError &annotated : d->m_state.errors)
892 errors.emplaceBack(annotated.error);
893 return errors;
894}
895
896/*!
897 \qmlmethod string Component::errorString()
898
899 Returns a human-readable description of any error.
900
901 The string includes the file, location, and description of each error.
902 If multiple errors are present, they are separated by a newline character.
903
904 If no errors are present, an empty string is returned.
905*/
906
907/*!
908 \internal
909 errorString() is only meant as a way to get the errors from QML side.
910*/
911QString QQmlComponent::errorString() const
912{
913 Q_D(const QQmlComponent);
914 QString ret;
915 if(!isError())
916 return ret;
917 for (const QQmlComponentPrivate::AnnotatedQmlError &annotated : d->m_state.errors) {
918 ret += annotated.error.toString() + QLatin1Char('\n');
919 }
920 return ret;
921}
922
923/*!
924 \qmlproperty url Component::url
925 The component URL. This is the URL that was used to construct the component.
926*/
927
928/*!
929 \property QQmlComponent::url
930 The component URL. This is the URL passed to either the constructor,
931 or the loadUrl(), or setData() methods.
932*/
933QUrl QQmlComponent::url() const
934{
935 Q_D(const QQmlComponent);
936 return d->m_url;
937}
938
939/*!
940 \internal
941*/
942QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
943 : QObject(dd, parent)
944{
945}
946
947/*!
948 Create an object instance from this component, within the specified \a context.
949 Returns \nullptr if creation failed.
950
951 If \a context is \nullptr (the default), it will create the instance in the
952 \l {QQmlEngine::rootContext()}{root context} of the engine.
953
954 The ownership of the returned object instance is transferred to the caller.
955
956 If the object being created from this component is a visual item, it must
957 have a visual parent, which can be set by calling
958 QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick}
959 for more details.
960
961 \sa QQmlEngine::ObjectOwnership
962*/
963QObject *QQmlComponent::create(QQmlContext *context)
964{
965 Q_D(QQmlComponent);
966 return d->createWithProperties(
967 nullptr, QVariantMap {}, context, QQmlComponentPrivate::CreateBehavior::Cpp);
968}
969
970/*!
971 Create an object instance of this component, within the specified \a context,
972 and initialize its top-level properties with \a initialProperties.
973
974 \omit
975 TODO: also mention errorString() when QTBUG-93239 is fixed
976 \endomit
977
978 If any of the \a initialProperties cannot be set, a warning is issued. If
979 there are unset required properties, the object creation fails and returns
980 \c nullptr, in which case \l isError() will return \c true.
981
982 If \a context is \nullptr (the default), it will create the instance in the
983 \l {QQmlEngine::rootContext()}{root context} of the engine.
984
985 The ownership of the returned object instance is transferred to the caller.
986
987 \sa QQmlComponent::create
988 \since 5.14
989*/
990QObject *QQmlComponent::createWithInitialProperties(
991 const QVariantMap& initialProperties, QQmlContext *context)
992{
993 Q_D(QQmlComponent);
994 return d->createWithProperties(
995 nullptr, initialProperties, context, QQmlComponentPrivate::CreateBehavior::Cpp);
996}
997
998static void QQmlComponent_setQmlParent(QObject *me, QObject *parent); // forward declaration
999
1000/*! \internal
1001 */
1002QObject *QQmlComponentPrivate::createWithProperties(
1003 QObject *parent, const QVariantMap &properties,
1004 QQmlContext *context, CreateBehavior behavior)
1005{
1006 Q_Q(QQmlComponent);
1007
1008 QObject *rv = doBeginCreate(q, context);
1009 if (!rv) {
1010 if (m_state.isCompletePending()) {
1011 // overridden completCreate might assume that
1012 // the object has actually been created
1013 ++creationDepth;
1014 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine);
1015 complete(ep, &m_state);
1016 --creationDepth;
1017 }
1018 return nullptr;
1019 }
1020
1021 QQmlComponent_setQmlParent(rv, parent); // internally checks if parent is nullptr
1022
1023 if (behavior == CreateBehavior::Qml) {
1024 bool ok = true;
1025 for (auto it = properties.cbegin(), end = properties.cend(); it != end; ++it)
1026 ok = setInitialProperty(rv, it.key(), it.value()) && ok;
1027 q->completeCreate();
1028 if (m_state.hasUnsetRequiredProperties()) {
1029 for (const auto &unsetRequiredProperty : std::as_const(*m_state.requiredProperties())) {
1030 const QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1031 qmlWarning(rv, error);
1032 }
1033 delete std::exchange(rv, nullptr);
1034 } else if (!ok) {
1035 // We've already warned about this before
1036 delete std::exchange(rv, nullptr);
1037 }
1038 } else {
1039 setInitialProperties(rv, properties);
1040 q->completeCreate();
1041 if (m_state.hasUnsetRequiredProperties())
1042 delete std::exchange(rv, nullptr);
1043 }
1044
1045 return rv;
1046}
1047
1048/*!
1049 Create an object instance from this component, within the specified \a context.
1050 Returns \nullptr if creation failed.
1051
1052 \note This method provides advanced control over component instance creation.
1053 In general, programmers should use QQmlComponent::create() to create object
1054 instances.
1055
1056 When QQmlComponent constructs an instance, it occurs in three steps:
1057
1058 \list 1
1059 \li The object hierarchy is created, and constant values are assigned.
1060 \li Property bindings are evaluated for the first time.
1061 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
1062 \endlist
1063
1064 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
1065 only performs step 1. QQmlComponent::completeCreate() must be called to
1066 complete steps 2 and 3.
1067
1068 This breaking point is sometimes useful when using attached properties to
1069 communicate information to an instantiated component, as it allows their
1070 initial values to be configured before property bindings take effect.
1071
1072 The ownership of the returned object instance is transferred to the caller.
1073
1074 \note The categorization of bindings into constant values and actual
1075 bindings is intentionally unspecified and may change between versions of Qt
1076 and depending on whether and how you are using \l{qmlcachegen}. You should
1077 not rely on any particular binding to be evaluated either before or after
1078 beginCreate() returns. For example a constant expression like
1079 \e{MyType.EnumValue} may be recognized as such at compile time or deferred
1080 to be executed as binding. The same holds for constant expressions like
1081 \e{-(5)} or \e{"a" + " constant string"}.
1082
1083 \sa completeCreate(), QQmlEngine::ObjectOwnership
1084*/
1085QObject *QQmlComponent::beginCreate(QQmlContext *context)
1086{
1087 Q_D(QQmlComponent);
1088 Q_ASSERT(context);
1089 return d->beginCreate(QQmlContextData::get(context));
1090}
1091
1092static QQmlParserStatus *parserStatusCast(const QQmlType &type, QObject *rv)
1093{
1094 const int parserStatusCast = type.parserStatusCast();
1095 return parserStatusCast == -1
1096 ? nullptr
1097 : reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(rv) + parserStatusCast);
1098}
1099
1100QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> context)
1101{
1102 Q_Q(QQmlComponent);
1103 auto cleanup = qScopeGuard([this] {
1104 if (!m_state.errors.isEmpty() && lcQmlComponentGeneral().isDebugEnabled()) {
1105 for (const auto &e : std::as_const(m_state.errors)) {
1106 qCDebug(lcQmlComponentGeneral) << "QQmlComponent: " << e.error.toString();
1107 }
1108 }
1109 });
1110 if (!context) {
1111 qWarning("QQmlComponent: Cannot create a component in a null context");
1112 return nullptr;
1113 }
1114
1115 if (!context->isValid()) {
1116 qWarning("QQmlComponent: Cannot create a component in an invalid context");
1117 return nullptr;
1118 }
1119
1120 if (context->engine() != m_engine) {
1121 qWarning("QQmlComponent: Must create component in context from the same QQmlEngine");
1122 return nullptr;
1123 }
1124
1125 if (m_state.isCompletePending()) {
1126 qWarning("QQmlComponent: Cannot create new component instance before completing the previous");
1127 return nullptr;
1128 }
1129
1130 // filter out temporary errors as they do not really affect component's
1131 // state (they are not part of the document compilation)
1132 m_state.errors.removeIf([](const auto &e) { return e.isTransient; });
1133 m_state.clearRequiredProperties();
1134
1135 if (!q->isReady()) {
1136 qWarning("QQmlComponent: Component is not ready");
1137 return nullptr;
1138 }
1139
1140 // Do not create infinite recursion in object creation
1141 static const int maxCreationDepth = 10;
1142 if (creationDepth >= maxCreationDepth) {
1143 qWarning("QQmlComponent: Component creation is recursing - aborting");
1144 return nullptr;
1145 }
1146
1147 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(m_engine);
1148
1149 enginePriv->inProgressCreations++;
1150 m_state.errors.clear();
1151 m_state.setCompletePending(true);
1152
1153 QObject *rv = nullptr;
1154
1155 const QQmlType type = loadedType();
1156 if (!type.isValid()) {
1157 enginePriv->referenceScarceResources();
1158 const QString *icName = m_inlineComponentName.get();
1159 m_state.initCreator(
1160 context, m_compilationUnit, m_creationContext, icName ? *icName : QString());
1161
1162 QQmlObjectCreator::CreationFlags flags;
1163 if (icName) {
1164 flags = QQmlObjectCreator::InlineComponent;
1165 if (m_start == -1)
1166 m_start = m_compilationUnit->inlineComponentId(*icName);
1167 Q_ASSERT(m_start > 0);
1168 } else {
1169 flags = QQmlObjectCreator::NormalObject;
1170 }
1171
1172 rv = m_state.creator()->create(m_start, nullptr, nullptr, flags);
1173 if (!rv)
1174 m_state.appendCreatorErrors();
1175 enginePriv->dereferenceScarceResources();
1176 } else {
1177 // TODO: extract into function
1178 rv = type.createWithQQmlData();
1179 QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(rv);
1180 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv)) {
1181 parserStatus->classBegin();
1182 m_state.ensureRequiredPropertyStorage(rv);
1183 } else if (type.finalizerCast() != -1) {
1184 m_state.ensureRequiredPropertyStorage(rv);
1185 }
1186
1187 if (propertyCache) {
1188 for (int i = 0, propertyCount = propertyCache->propertyCount(); i < propertyCount; ++i) {
1189 if (const QQmlPropertyData *propertyData = propertyCache->property(i); propertyData->isRequired()) {
1190 m_state.ensureRequiredPropertyStorage(rv);
1191 RequiredPropertyInfo info;
1192 info.propertyName = propertyData->name(rv);
1193 m_state.addPendingRequiredProperty(rv, propertyData, info);
1194 }
1195 }
1196 } else {
1197 // we couldn't get a propertyCache from ensurePropertyCache
1198 // it is unclear what we can do in that case
1199 // ### TOOD: QTBUG-136560
1200 }
1201 }
1202
1203 if (rv) {
1204 QQmlData *ddata = QQmlData::get(rv);
1205 Q_ASSERT(ddata);
1206 // top-level objects should never get JS ownership.
1207 // if JS ownership is needed this needs to be explicitly undone (like in createObject())
1208 ddata->indestructible = true;
1209 ddata->explicitIndestructibleSet = true;
1210 ddata->rootObjectInCreation = false;
1211
1212 // Assign parent context to the object if we haven't created one.
1213 if (!ddata->outerContext)
1214 ddata->outerContext = context.data();
1215 if (!ddata->context)
1216 ddata->context = context.data();
1217 }
1218
1219 return rv;
1220}
1221
1222void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
1223 QObject *object, DeferredState *deferredState)
1224{
1225 QQmlData *ddata = QQmlData::get(object);
1226 Q_ASSERT(!ddata->deferredData.isEmpty());
1227
1228 deferredState->reserve(ddata->deferredData.size());
1229
1230 for (QQmlData::DeferredData *deferredData : std::as_const(ddata->deferredData)) {
1231 enginePriv->inProgressCreations++;
1232
1233 ConstructionState state;
1234 state.setCompletePending(true);
1235
1236 auto creator = state.initCreator(
1237 deferredData->context->parent(),
1238 deferredData->compilationUnit,
1239 QQmlRefPointer<QQmlContextData>(),
1240 deferredData->inlineComponentName
1241 );
1242
1243 if (!creator->populateDeferredProperties(object, deferredData))
1244 state.appendCreatorErrors();
1245 deferredData->bindings.clear();
1246
1247 deferredState->push_back(std::move(state));
1248 }
1249}
1250
1251void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState)
1252{
1253 for (ConstructionState &state : *deferredState)
1254 complete(enginePriv, &state);
1255}
1256
1257void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
1258{
1259 if (state->isCompletePending()) {
1260 QQmlInstantiationInterrupt interrupt;
1261 state->creator()->finalize(interrupt);
1262
1263 state->setCompletePending(false);
1264
1265 enginePriv->inProgressCreations--;
1266
1267 if (0 == enginePriv->inProgressCreations) {
1268 while (enginePriv->erroredBindings) {
1269 enginePriv->warning(enginePriv->erroredBindings->removeError());
1270 }
1271 }
1272 }
1273}
1274
1275/*!
1276 \internal
1277 Finds the matching top-level property with name \a name of the component \a createdComponent.
1278 If it was a required property or an alias to a required property contained in \a
1279 requiredProperties, it is removed from it.
1280 \a requiredProperties must be non-null.
1281
1282 If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
1283 was found in requiredProperties.
1284
1285 Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
1286 for further processing (for instance, actually setting the property value).
1287
1288 Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
1289 classes which create components should not need it and should only need to call
1290 setInitialProperties.
1291 */
1292QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
1293 QObject *createdComponent, const QString &name,
1294 RequiredProperties *requiredProperties, QQmlEngine *engine,
1295 bool *wasInRequiredProperties)
1296{
1297 Q_ASSERT(requiredProperties);
1298 QQmlProperty prop(createdComponent, name, engine);
1299 auto privProp = QQmlPropertyPrivate::get(prop);
1300 if (prop.isValid()) {
1301 // resolve outstanding required properties
1302 const QQmlPropertyData *targetProp = &privProp->core;
1303 if (targetProp->isAlias()) {
1304 auto target = createdComponent;
1305 QQmlPropertyIndex originalIndex(targetProp->coreIndex());
1306 QQmlPropertyIndex propIndex;
1307 QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
1308 QQmlData *data = QQmlData::get(target);
1309 Q_ASSERT(data && data->propertyCache);
1310 targetProp = data->propertyCache->property(propIndex.coreIndex());
1311 } else {
1312 // we need to get the pointer from the property cache instead of directly using
1313 // targetProp else the lookup will fail
1314 QQmlData *data = QQmlData::get(createdComponent);
1315 Q_ASSERT(data && data->propertyCache);
1316 targetProp = data->propertyCache->property(targetProp->coreIndex());
1317 }
1318 auto it = requiredProperties->constFind({createdComponent, targetProp});
1319 if (it != requiredProperties->cend()) {
1320 if (wasInRequiredProperties)
1321 *wasInRequiredProperties = true;
1322 requiredProperties->erase(it);
1323 } else {
1324 if (wasInRequiredProperties)
1325 *wasInRequiredProperties = false;
1326 }
1327 }
1328 return prop;
1329}
1330
1331/*!
1332 This method provides advanced control over component instance creation.
1333 In general, programmers should use QQmlComponent::create() to create a
1334 component.
1335
1336 This function completes the component creation begun with QQmlComponent::beginCreate()
1337 and must be called afterwards.
1338
1339 \sa beginCreate()
1340*/
1341void QQmlComponent::completeCreate()
1342{
1343 Q_D(QQmlComponent);
1344
1345 d->completeCreate();
1346}
1347
1348void QQmlComponentPrivate::completeCreate()
1349{
1350 if (m_state.hasUnsetRequiredProperties()) {
1351 for (const auto& unsetRequiredProperty: std::as_const(*m_state.requiredProperties())) {
1352 QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1353 m_state.errors.push_back(QQmlComponentPrivate::AnnotatedQmlError { error, true });
1354 }
1355 }
1356
1357 const QQmlType type = loadedType();
1358 if (type.isValid()) {
1359 QObject *rv = m_state.target();
1360 if (QQmlParserStatus *parserStatus = parserStatusCast(type, rv))
1361 parserStatus->componentComplete();
1362
1363 if (const int finalizerCast = type.finalizerCast(); finalizerCast != -1) {
1364 auto *hook = reinterpret_cast<QQmlFinalizerHook *>(
1365 reinterpret_cast<char *>(rv) + finalizerCast);
1366 hook->componentFinalized();
1367 }
1368
1369 /*
1370 We can directly set completePending to false, as finalize is only concerned
1371 with setting up pending bindings, but that cannot happen here, as we're
1372 dealing with a pure C++ type, which cannot have pending bindings
1373 */
1374 m_state.setCompletePending(false);
1375 QQmlEnginePrivate::get(m_engine)->inProgressCreations--;
1376 } else if (m_state.isCompletePending()) {
1377 ++creationDepth;
1378 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine);
1379 complete(ep, &m_state);
1380 --creationDepth;
1381 }
1382}
1383
1384QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
1385: QObject(parent), m_prev(nullptr), m_next(nullptr)
1386{
1387}
1388
1389QQmlComponentAttached::~QQmlComponentAttached()
1390{
1391 if (m_prev) *m_prev = m_next;
1392 if (m_next) m_next->m_prev = m_prev;
1393 m_prev = nullptr;
1394 m_next = nullptr;
1395}
1396
1397/*!
1398 \internal
1399*/
1400QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
1401{
1402 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
1403
1404 QQmlEngine *engine = qmlEngine(obj);
1405 if (!engine)
1406 return a;
1407
1408 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
1409 if (p->activeObjectCreator) { // XXX should only be allowed during begin
1410 a->insertIntoList(p->activeObjectCreator->componentAttachment());
1411 } else {
1412 QQmlData *d = QQmlData::get(obj);
1413 Q_ASSERT(d);
1414 Q_ASSERT(d->context);
1415 d->context->addComponentAttached(a);
1416 }
1417
1418 return a;
1419}
1420
1421/*!
1422 Load the QQmlComponent for \a typeName in the module \a uri.
1423 If the type is implemented via a QML file, \a mode is used to
1424 load it. Types backed by C++ are always loaded synchronously.
1425
1426 \code
1427 QQmlEngine engine;
1428 QQmlComponent component(&engine);
1429 component.loadFromModule("QtQuick", "Item");
1430 // once the component is ready
1431 std::unique_ptr<QObject> item(component.create());
1432 Q_ASSERT(item->metaObject() == &QQuickItem::staticMetaObject);
1433 \endcode
1434
1435 \since 6.5
1436 \sa loadUrl()
1437 */
1438void QQmlComponent::loadFromModule(QAnyStringView uri, QAnyStringView typeName,
1439 QQmlComponent::CompilationMode mode)
1440{
1441 Q_D(QQmlComponent);
1442
1443 QQmlTypeLoader::Mode typeLoaderMode = QQmlTypeLoader::Synchronous;
1444 switch (mode) {
1445 case QQmlComponent::PreferSynchronous:
1446 typeLoaderMode = QQmlTypeLoader::PreferSynchronous;
1447 break;
1448 case QQmlComponent::Asynchronous:
1449 typeLoaderMode = QQmlTypeLoader::Asynchronous;
1450 break;
1451 }
1452
1453 d->prepareLoadFromModule(uri, typeName, typeLoaderMode);
1454 if (d->m_loadHelper->isCompleteOrError())
1455 d->completeLoadFromModule(uri, typeName);
1456 else
1457 d->m_loadHelper->registerCallback(d);
1458}
1459
1460void QQmlComponentPrivate::prepareLoadFromModule(
1461 QAnyStringView uri, QAnyStringView typeName, QQmlTypeLoader::Mode mode)
1462{
1463 // Don't let any old loadHelper call us back anymore.
1464 if (m_loadHelper)
1465 m_loadHelper->unregisterCallback(this);
1466
1467 // LoadHelper must be on the Heap as it derives from QQmlRefCount
1468 m_loadHelper = QQml::makeRefPointer<LoadHelper>(QQmlTypeLoader::get(m_engine), uri, typeName, mode);
1469}
1470
1471void QQmlComponentPrivate::completeLoadFromModule(QAnyStringView uri, QAnyStringView typeName)
1472{
1473 Q_Q(QQmlComponent);
1474
1475 // we always mimic the progressChanged behavior from loadUrl
1476 auto reportError = [&](QString msg) {
1477 QQmlError error;
1478 error.setDescription(msg);
1479 m_state.errors.push_back(std::move(error));
1480 setProgress(1);
1481 emit q->statusChanged(q->Error);
1482 };
1483 auto emitComplete = [&]() {
1484 setProgress(1);
1485 emit q->statusChanged(q->status());
1486 };
1487
1488 setProgress(0);
1489
1490 const QQmlType type = m_loadHelper->type();
1491
1492 if (m_loadHelper->resolveTypeResult() == LoadHelper::ResolveTypeResult::NoSuchModule) {
1493 reportError(QLatin1String(R"(No module named "%1" found)").arg(uri.toString()));
1494 } else if (!type.isValid()) {
1495 reportError(QLatin1String(R"(Module "%1" contains no type named "%2")")
1496 .arg(uri.toString(), typeName.toString()));
1497 } else if (type.isCreatable()) {
1498 emitComplete();
1499 } else if (type.isComposite()) {
1500 QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous;
1501 switch (m_loadHelper->mode()) {
1502 case QQmlTypeLoader::Asynchronous:
1503 mode = QQmlComponent::Asynchronous;
1504 break;
1505 case QQmlTypeLoader::PreferSynchronous:
1506 case QQmlTypeLoader::Synchronous:
1507 mode = QQmlComponent::PreferSynchronous;
1508 break;
1509 }
1510
1511 // loadUrl takes care of signal emission
1512 loadUrl(type.sourceUrl(), mode);
1513 } else if (type.isInlineComponentType()) {
1514 auto baseUrl = type.sourceUrl();
1515 baseUrl.setFragment(QString());
1516
1517 Q_ASSERT(m_progress == 0.0);
1518 {
1519 // we don't want to emit status changes from the "helper" loadUrl below
1520 // because it would signal success to early
1521 QSignalBlocker blockSignals(q);
1522 // we really need to continue in a synchronous way, otherwise we can't check the CU
1523 loadUrl(baseUrl, QQmlComponent::PreferSynchronous);
1524 }
1525 // We do want to emit any progress change that happened in the "helper" loadUrl.
1526 if (m_progress != 0.0)
1527 emit q->progressChanged(m_progress);
1528
1529 if (q->isError()) {
1530 emitComplete();
1531 return;
1532 }
1533 QString elementName = type.elementName();
1534 if (m_compilationUnit->inlineComponentId(elementName) == -1) {
1535 QString realTypeName = typeName.toString();
1536 realTypeName.truncate(realTypeName.indexOf(u'.'));
1537 QString errorMessage = R"(Type "%1" from module "%2" contains no inline component named "%3".)"_L1.arg(
1538 realTypeName, uri.toString(), elementName);
1539 if (elementName == u"qml")
1540 errorMessage += " To load the type \"%1\", drop the \".qml\" extension."_L1.arg(realTypeName);
1541 reportError(std::move(errorMessage));
1542 } else {
1543 m_inlineComponentName = std::make_unique<QString>(std::move(elementName));
1544 emitComplete();
1545 }
1546 } else if (type.isSingleton() || type.isCompositeSingleton()) {
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)