1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlcomponent.h"
41#include "qqmlcomponent_p.h"
42#include "qqmlcomponentattached_p.h"
43
44#include "qqmlcontext_p.h"
45#include "qqmlengine_p.h"
46#include "qqmlvme_p.h"
47#include "qqml.h"
48#include "qqmlengine.h"
49#include "qqmlbinding_p.h"
50#include "qqmlincubator.h"
51#include "qqmlincubator_p.h"
52#include <private/qqmljavascriptexpression_p.h>
53#include <private/qqmlsourcecoordinate_p.h>
54
55#include <private/qv4functionobject_p.h>
56#include <private/qv4script_p.h>
57#include <private/qv4scopedvalue_p.h>
58#include <private/qv4objectiterator_p.h>
59#include <private/qv4qobjectwrapper_p.h>
60#include <private/qv4jscall_p.h>
61
62#include <QDir>
63#include <QStack>
64#include <QStringList>
65#include <QThreadStorage>
66#include <QtCore/qdebug.h>
67#include <qqmlinfo.h>
68
69namespace {
70 QThreadStorage<int> creationDepth;
71}
72
73QT_BEGIN_NAMESPACE
74
75class QQmlComponentExtension : public QV4::ExecutionEngine::Deletable
76{
77public:
78 QQmlComponentExtension(QV4::ExecutionEngine *v4);
79 virtual ~QQmlComponentExtension();
80
81 QV4::PersistentValue incubationProto;
82};
83V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
84
85/*!
86 \class QQmlComponent
87 \since 5.0
88 \inmodule QtQml
89
90 \brief The QQmlComponent class encapsulates a QML component definition.
91
92 Components are reusable, encapsulated QML types with well-defined interfaces.
93
94 A QQmlComponent instance can be created from a QML file.
95 For example, if there is a \c main.qml file like this:
96
97 \qml
98 import QtQuick 2.0
99
100 Item {
101 width: 200
102 height: 200
103 }
104 \endqml
105
106 The following code loads this QML file as a component, creates an instance of
107 this component using create(), and then queries the \l Item's \l {Item::}{width}
108 value:
109
110 \code
111 QQmlEngine *engine = new QQmlEngine;
112 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
113
114 QObject *myObject = component.create();
115 QQuickItem *item = qobject_cast<QQuickItem*>(myObject);
116 int width = item->width(); // width = 200
117 \endcode
118
119 To create instances of a component in code where a QQmlEngine instance is
120 not available, you can use \l qmlContext() or \l qmlEngine(). For example,
121 in the scenario below, child items are being created within a QQuickItem
122 subclass:
123
124 \code
125 void MyCppItem::init()
126 {
127 QQmlEngine *engine = qmlEngine(this);
128 // Or:
129 // QQmlEngine *engine = qmlContext(this)->engine();
130 QQmlComponent component(engine, QUrl::fromLocalFile("MyItem.qml"));
131 QQuickItem *childItem = qobject_cast<QQuickItem*>(component.create());
132 childItem->setParentItem(this);
133 }
134 \endcode
135
136 Note that these functions will return \c null when called inside the
137 constructor of a QObject subclass, as the instance will not yet have
138 a context nor engine.
139
140 \section2 Network Components
141
142 If the URL passed to QQmlComponent is a network resource, or if the QML document references a
143 network resource, the QQmlComponent has to fetch the network data before it is able to create
144 objects. In this case, the QQmlComponent will have a \l {QQmlComponent::Loading}{Loading}
145 \l {QQmlComponent::status()}{status}. An application will have to wait until the component
146 is \l {QQmlComponent::Ready}{Ready} before calling \l {QQmlComponent::create()}.
147
148 The following example shows how to load a QML file from a network resource. After creating
149 the QQmlComponent, it tests whether the component is loading. If it is, it connects to the
150 QQmlComponent::statusChanged() signal and otherwise calls the \c {continueLoading()} method
151 directly. Note that QQmlComponent::isLoading() may be false for a network component if the
152 component has been cached and is ready immediately.
153
154 \code
155 MyApplication::MyApplication()
156 {
157 // ...
158 component = new QQmlComponent(engine, QUrl("http://www.example.com/main.qml"));
159 if (component->isLoading())
160 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
161 this, SLOT(continueLoading()));
162 else
163 continueLoading();
164 }
165
166 void MyApplication::continueLoading()
167 {
168 if (component->isError()) {
169 qWarning() << component->errors();
170 } else {
171 QObject *myObject = component->create();
172 }
173 }
174 \endcode
175*/
176
177/*!
178 \qmltype Component
179 \instantiates QQmlComponent
180 \ingroup qml-utility-elements
181 \inqmlmodule QtQml
182 \brief Encapsulates a QML component definition.
183
184 Components are reusable, encapsulated QML types with well-defined interfaces.
185
186 Components are often defined by \l {{QML Documents}}{component files} -
187 that is, \c .qml files. The \e Component type essentially allows QML components
188 to be defined inline, within a \l {QML Documents}{QML document}, rather than as a separate QML file.
189 This may be useful for reusing a small component within a QML file, or for defining
190 a component that logically belongs with other QML components within a file.
191
192 For example, here is a component that is used by multiple \l Loader objects.
193 It contains a single item, a \l Rectangle:
194
195 \snippet qml/component.qml 0
196
197 Notice that while a \l Rectangle by itself would be automatically
198 rendered and displayed, this is not the case for the above rectangle
199 because it is defined inside a \c Component. The component encapsulates the
200 QML types within, as if they were defined in a separate QML
201 file, and is not loaded until requested (in this case, by the
202 two \l Loader objects). Because Component is not derived from Item, you cannot
203 anchor anything to it.
204
205 Defining a \c Component is similar to defining a \l {QML Documents}{QML document}.
206 A QML document has a single top-level item that defines the behavior and
207 properties of that component, and cannot define properties or behavior outside
208 of that top-level item. In the same way, a \c Component definition contains a single
209 top level item (which in the above example is a \l Rectangle) and cannot define any
210 data outside of this item, with the exception of an \e id (which in the above example
211 is \e redSquare).
212
213 The \c Component type is commonly used to provide graphical components
214 for views. For example, the ListView::delegate property requires a \c Component
215 to specify how each list item is to be displayed.
216
217 \c Component objects can also be created dynamically using
218 \l{QtQml::Qt::createComponent()}{Qt.createComponent()}.
219
220 \section2 Creation Context
221
222 The creation context of a Component corresponds to the context where the Component was declared.
223 This context is used as the parent context (creating a \l{qtqml-documents-scope.html#component-instance-hierarchy}{context hierarchy})
224 when the component is instantiated by an object such as a ListView or a Loader.
225
226 In the following example, \c comp1 is created within the root context of MyItem.qml, and any objects
227 instantiated from this component will have access to the ids and properties within that context,
228 such as \c internalSettings.color. When \c comp1 is used as a ListView delegate in another context
229 (as in main.qml below), it will continue to have access to the properties of its creation context
230 (which would otherwise be private to external users).
231
232 \table
233 \row
234 \li MyItem.qml
235 \li \snippet qml/component/MyItem.qml 0
236 \row
237 \li main.qml
238 \li \snippet qml/component/main.qml 0
239 \endtable
240
241 It is important that the lifetime of the creation context outlive any created objects. See
242 \l{Maintaining Dynamically Created Objects} for more details.
243*/
244
245/*!
246 \qmlattachedsignal Component::completed()
247
248 Emitted after the object has been instantiated. This can be used to
249 execute script code at startup, once the full QML environment has been
250 established.
251
252 The \c onCompleted signal handler can be declared on any object. The order
253 of running the handlers is undefined.
254
255 \qml
256 Rectangle {
257 Component.onCompleted: console.log("Completed Running!")
258 Rectangle {
259 Component.onCompleted: console.log("Nested Completed Running!")
260 }
261 }
262 \endqml
263*/
264
265/*!
266 \qmlattachedsignal Component::destruction()
267
268 Emitted as the object begins destruction. This can be used to undo
269 work done in response to the \l {completed}{completed()} signal, or other
270 imperative code in your application.
271
272 The \c onDestruction signal handler can be declared on any object. The
273 order of running the handlers is undefined.
274
275 \qml
276 Rectangle {
277 Component.onDestruction: console.log("Destruction Beginning!")
278 Rectangle {
279 Component.onDestruction: console.log("Nested Destruction Beginning!")
280 }
281 }
282 \endqml
283
284 \sa {Qt QML}
285*/
286
287/*!
288 \enum QQmlComponent::Status
289
290 Specifies the loading status of the QQmlComponent.
291
292 \value Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content.
293 \value Ready This QQmlComponent is ready and create() may be called.
294 \value Loading This QQmlComponent is loading network data.
295 \value Error An error has occurred. Call errors() to retrieve a list of \l {QQmlError}{errors}.
296*/
297
298/*!
299 \enum QQmlComponent::CompilationMode
300
301 Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
302
303 \value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
304 This is not always possible; for example, remote URLs will always load asynchronously.
305 \value Asynchronous Load/compile the component in a background thread.
306*/
307
308void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
309{
310 Q_Q(QQmlComponent);
311
312 Q_ASSERT(typeData);
313
314 fromTypeData(data: typeData);
315 typeData = nullptr;
316 progress = 1.0;
317
318 emit q->statusChanged(q->status());
319 emit q->progressChanged(progress);
320}
321
322void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
323{
324 Q_Q(QQmlComponent);
325
326 progress = p;
327
328 emit q->progressChanged(p);
329}
330
331void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data)
332{
333 url = data->finalUrl();
334 compilationUnit = data->compilationUnit();
335
336 if (!compilationUnit) {
337 Q_ASSERT(data->isError());
338 state.errors = data->errors();
339 }
340}
341
342RequiredProperties &QQmlComponentPrivate::requiredProperties()
343{
344 return state.creator->requiredProperties();
345}
346
347bool QQmlComponentPrivate::hadRequiredProperties() const
348{
349 return state.creator->componentHadRequiredProperties();
350}
351
352void QQmlComponentPrivate::clear()
353{
354 if (typeData) {
355 typeData->unregisterCallback(this);
356 typeData = nullptr;
357 }
358
359 compilationUnit = nullptr;
360}
361
362QObject *QQmlComponentPrivate::doBeginCreate(QQmlComponent *q, QQmlContext *context)
363{
364 if (!engine) {
365 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
366 qWarning(msg: "QQmlComponent: Must provide an engine before calling create");
367 return nullptr;
368 }
369 if (!context)
370 context = engine->rootContext();
371 return q->beginCreate(context);
372}
373
374bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& name, const QVariant &value)
375{
376 QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(createdComponent: component, name, requiredProperties&: requiredProperties());
377 QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(p: prop);
378 const bool isValid = prop.isValid();
379 if (!isValid || !privProp->writeValueProperty(value, {})) {
380 QQmlError error{};
381 error.setUrl(url);
382 if (isValid)
383 error.setDescription(QLatin1String("Could not set initial property %1").arg(args: name));
384 else
385 error.setDescription(QLatin1String("Setting initial properties failed: %2 does not have a property called %1").arg(args: name,
386 args: QQmlMetaType::prettyTypeName(object: component)));
387 state.errors.push_back(t: error);
388 return false;
389 } else
390 return true;
391}
392
393/*!
394 \internal
395*/
396QQmlComponent::QQmlComponent(QObject *parent)
397 : QObject(*(new QQmlComponentPrivate), parent)
398{
399}
400
401/*!
402 Destruct the QQmlComponent.
403*/
404QQmlComponent::~QQmlComponent()
405{
406 Q_D(QQmlComponent);
407
408 if (d->state.completePending) {
409 qWarning(msg: "QQmlComponent: Component destroyed while completion pending");
410
411 if (isError()) {
412 qWarning() << "This may have been caused by one of the following errors:";
413 for (const QQmlError &error : qAsConst(t&: d->state.errors))
414 qWarning().nospace().noquote() << QLatin1String(" ") << error;
415 }
416
417 d->completeCreate();
418 }
419
420 if (d->typeData) {
421 d->typeData->unregisterCallback(d);
422 d->typeData = nullptr;
423 }
424}
425
426/*!
427 \qmlproperty enumeration Component::status
428 This property holds the status of component loading. The status can be one of the
429 following:
430 \list
431 \li Component.Null - no data is available for the component
432 \li Component.Ready - the component has been loaded, and can be used to create instances.
433 \li Component.Loading - the component is currently being loaded
434 \li Component.Error - an error occurred while loading the component.
435 Calling errorString() will provide a human-readable description of any errors.
436 \endlist
437 */
438
439/*!
440 \property QQmlComponent::status
441 The component's current \l{QQmlComponent::Status} {status}.
442 */
443QQmlComponent::Status QQmlComponent::status() const
444{
445 Q_D(const QQmlComponent);
446
447 if (d->typeData)
448 return Loading;
449 else if (!d->state.errors.isEmpty())
450 return Error;
451 else if (d->engine && d->compilationUnit)
452 return Ready;
453 else
454 return Null;
455}
456
457/*!
458 Returns true if status() == QQmlComponent::Null.
459*/
460bool QQmlComponent::isNull() const
461{
462 return status() == Null;
463}
464
465/*!
466 Returns true if status() == QQmlComponent::Ready.
467*/
468bool QQmlComponent::isReady() const
469{
470 return status() == Ready;
471}
472
473/*!
474 Returns true if status() == QQmlComponent::Error.
475*/
476bool QQmlComponent::isError() const
477{
478 return status() == Error;
479}
480
481/*!
482 Returns true if status() == QQmlComponent::Loading.
483*/
484bool QQmlComponent::isLoading() const
485{
486 return status() == Loading;
487}
488
489/*!
490 \qmlproperty real Component::progress
491 The progress of loading the component, from 0.0 (nothing loaded)
492 to 1.0 (finished).
493*/
494
495/*!
496 \property QQmlComponent::progress
497 The progress of loading the component, from 0.0 (nothing loaded)
498 to 1.0 (finished).
499*/
500qreal QQmlComponent::progress() const
501{
502 Q_D(const QQmlComponent);
503 return d->progress;
504}
505
506/*!
507 \fn void QQmlComponent::progressChanged(qreal progress)
508
509 Emitted whenever the component's loading progress changes. \a progress will be the
510 current progress between 0.0 (nothing loaded) and 1.0 (finished).
511*/
512
513/*!
514 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
515
516 Emitted whenever the component's status changes. \a status will be the
517 new status.
518*/
519
520/*!
521 Create a QQmlComponent with no data and give it the specified
522 \a engine and \a parent. Set the data with setData().
523*/
524QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
525 : QObject(*(new QQmlComponentPrivate), parent)
526{
527 Q_D(QQmlComponent);
528 d->engine = engine;
529}
530
531/*!
532 Create a QQmlComponent from the given \a url and give it the
533 specified \a parent and \a engine.
534
535 \include qqmlcomponent.qdoc url-note
536
537 \sa loadUrl()
538*/
539QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
540 : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent)
541{
542}
543
544/*!
545 Create a QQmlComponent from the given \a url and give it the
546 specified \a parent and \a engine. If \a mode is \l Asynchronous,
547 the component will be loaded and compiled asynchronously.
548
549 \include qqmlcomponent.qdoc url-note
550
551 \sa loadUrl()
552*/
553QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode,
554 QObject *parent)
555 : QQmlComponent(engine, parent)
556{
557 Q_D(QQmlComponent);
558 d->loadUrl(newUrl: url, mode);
559}
560
561/*!
562 Create a QQmlComponent from the given \a fileName and give it the specified
563 \a parent and \a engine.
564
565 \sa loadUrl()
566*/
567QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
568 QObject *parent)
569 : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent)
570{
571}
572
573/*!
574 Create a QQmlComponent from the given \a fileName and give it the specified
575 \a parent and \a engine. If \a mode is \l Asynchronous,
576 the component will be loaded and compiled asynchronously.
577
578 \sa loadUrl()
579*/
580QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
581 CompilationMode mode, QObject *parent)
582 : QQmlComponent(engine, parent)
583{
584 Q_D(QQmlComponent);
585 const QUrl url = QDir::isAbsolutePath(path: fileName) ? QUrl::fromLocalFile(localfile: fileName) : QUrl(fileName);
586 d->loadUrl(newUrl: url, mode);
587}
588
589/*!
590 \internal
591*/
592QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit,
593 int start, QObject *parent)
594 : QQmlComponent(engine, parent)
595{
596 Q_D(QQmlComponent);
597 d->compilationUnit = compilationUnit;
598 d->start = start;
599 d->url = compilationUnit->finalUrl();
600 d->progress = 1.0;
601}
602
603/*!
604 Sets the QQmlComponent to use the given QML \a data. If \a url
605 is provided, it is used to set the component name and to provide
606 a base path for items resolved by this component.
607*/
608void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
609{
610 Q_D(QQmlComponent);
611
612 if (!d->engine) {
613 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
614 qWarning(msg: "QQmlComponent: Must provide an engine before calling setData");
615 return;
616 }
617
618 d->clear();
619
620 d->url = url;
621
622 QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(e: d->engine)->typeLoader.getType(data, url);
623
624 if (typeData->isCompleteOrError()) {
625 d->fromTypeData(data: typeData);
626 } else {
627 d->typeData = typeData;
628 d->typeData->registerCallback(d);
629 }
630
631 d->progress = 1.0;
632 emit statusChanged(status());
633 emit progressChanged(d->progress);
634}
635
636/*!
637 Returns the QQmlContext the component was created in. This is only
638 valid for components created directly from QML.
639*/
640QQmlContext *QQmlComponent::creationContext() const
641{
642 Q_D(const QQmlComponent);
643 if(d->creationContext)
644 return d->creationContext->asQQmlContext();
645
646 return qmlContext(this);
647}
648
649/*!
650 Returns the QQmlEngine of this component.
651
652 \since 5.12
653*/
654QQmlEngine *QQmlComponent::engine() const
655{
656 Q_D(const QQmlComponent);
657 return d->engine;
658}
659
660/*!
661 Load the QQmlComponent from the provided \a url.
662
663 \include qqmlcomponent.qdoc url-note
664*/
665void QQmlComponent::loadUrl(const QUrl &url)
666{
667 Q_D(QQmlComponent);
668 d->loadUrl(newUrl: url);
669}
670
671/*!
672 Load the QQmlComponent from the provided \a url.
673 If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
674
675 \include qqmlcomponent.qdoc url-note
676*/
677void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
678{
679 Q_D(QQmlComponent);
680 d->loadUrl(newUrl: url, mode);
681}
682
683void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
684{
685 Q_Q(QQmlComponent);
686 clear();
687
688 if (newUrl.isRelative()) {
689 // The new URL is a relative URL like QUrl("main.qml").
690 url = engine->baseUrl().resolved(relative: QUrl(newUrl.toString()));
691 } else if (engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(path: newUrl.toLocalFile())) {
692 // The new URL is a file on disk but it's a relative path; e.g.:
693 // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml")
694 // We need to remove the scheme so that it becomes a relative URL with a relative path:
695 QUrl fixedUrl(newUrl);
696 fixedUrl.setScheme(QString());
697 // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl().
698 // This is a compatibility hack for QTBUG-58837.
699 url = engine->baseUrl().resolved(relative: fixedUrl);
700 } else {
701 url = newUrl;
702 }
703
704 if (newUrl.isEmpty()) {
705 QQmlError error;
706 error.setDescription(QQmlComponent::tr(s: "Invalid empty URL"));
707 state.errors << error;
708 return;
709 }
710
711 if (progress != 0.0) {
712 progress = 0.0;
713 emit q->progressChanged(progress);
714 }
715
716 QQmlTypeLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
717 ? QQmlTypeLoader::Asynchronous
718 : QQmlTypeLoader::PreferSynchronous;
719
720 QQmlRefPointer<QQmlTypeData> data = QQmlEnginePrivate::get(e: engine)->typeLoader.getType(unNormalizedUrl: url, mode: loaderMode);
721
722 if (data->isCompleteOrError()) {
723 fromTypeData(data);
724 progress = 1.0;
725 } else {
726 typeData = data;
727 typeData->registerCallback(this);
728 progress = data->progress();
729 }
730
731 emit q->statusChanged(q->status());
732 if (progress != 0.0)
733 emit q->progressChanged(progress);
734}
735
736/*!
737 Returns the list of errors that occurred during the last compile or create
738 operation. An empty list is returned if isError() is not set.
739*/
740QList<QQmlError> QQmlComponent::errors() const
741{
742 Q_D(const QQmlComponent);
743 if (isError())
744 return d->state.errors;
745 else
746 return QList<QQmlError>();
747}
748
749/*!
750 \qmlmethod string Component::errorString()
751
752 Returns a human-readable description of any error.
753
754 The string includes the file, location, and description of each error.
755 If multiple errors are present, they are separated by a newline character.
756
757 If no errors are present, an empty string is returned.
758*/
759
760/*!
761 \internal
762 errorString is only meant as a way to get the errors in script
763*/
764QString QQmlComponent::errorString() const
765{
766 Q_D(const QQmlComponent);
767 QString ret;
768 if(!isError())
769 return ret;
770 for (const QQmlError &e : d->state.errors) {
771 ret += e.url().toString() + QLatin1Char(':') +
772 QString::number(e.line()) + QLatin1Char(' ') +
773 e.description() + QLatin1Char('\n');
774 }
775 return ret;
776}
777
778/*!
779 \qmlproperty url Component::url
780 The component URL. This is the URL that was used to construct the component.
781*/
782
783/*!
784 \property QQmlComponent::url
785 The component URL. This is the URL passed to either the constructor,
786 or the loadUrl(), or setData() methods.
787*/
788QUrl QQmlComponent::url() const
789{
790 Q_D(const QQmlComponent);
791 return d->url;
792}
793
794/*!
795 \internal
796*/
797QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
798 : QObject(dd, parent)
799{
800}
801
802/*!
803 Create an object instance from this component. Returns \nullptr if creation
804 failed. \a context specifies the context within which to create the object
805 instance.
806
807 If \a context is \nullptr (the default), it will create the instance in the
808 \l {QQmlEngine::rootContext()}{root context} of the engine.
809
810 The ownership of the returned object instance is transferred to the caller.
811
812 If the object being created from this component is a visual item, it must
813 have a visual parent, which can be set by calling
814 QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick}
815 for more details.
816
817 \sa QQmlEngine::ObjectOwnership
818*/
819QObject *QQmlComponent::create(QQmlContext *context)
820{
821 Q_D(QQmlComponent);
822
823 QObject *rv = d->doBeginCreate(q: this, context);
824 if (rv)
825 completeCreate();
826 if (rv && !d->requiredProperties().empty()) {
827 delete rv;
828 return nullptr;
829 }
830 return rv;
831}
832
833/*!
834 Create an object instance of this component, and initialize its toplevel
835 properties with \a initialProperties. \a context specifies the context
836 where the object instance is to be created.
837
838 \omit
839 TODO: also mention errorString() when QTBUG-93239 is fixed
840 \endomit
841
842 If any of the \c initialProperties cannot be set, \l isError() will return
843 \c true, and the \l errors() function can be used to
844 get detailed information about the error(s).
845
846 \sa QQmlComponent::create
847 \since 5.14
848*/
849QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context)
850{
851 Q_D(QQmlComponent);
852
853 QObject *rv = d->doBeginCreate(q: this, context);
854 if (rv) {
855 setInitialProperties(component: rv, properties: initialProperties);
856 completeCreate();
857 }
858 if (!d->requiredProperties().empty()) {
859 d->requiredProperties().clear();
860 return nullptr;
861 }
862 return rv;
863}
864
865/*!
866 This method provides advanced control over component instance creation.
867 In general, programmers should use QQmlComponent::create() to create object
868 instances.
869
870 Create an object instance from this component. Returns \nullptr if creation
871 failed. \a publicContext specifies the context within which to create the object
872 instance.
873
874 When QQmlComponent constructs an instance, it occurs in three steps:
875 \list 1
876 \li The object hierarchy is created, and constant values are assigned.
877 \li Property bindings are evaluated for the first time.
878 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
879 \endlist
880 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
881 only performs step 1. QQmlComponent::completeCreate() must be called to
882 complete steps 2 and 3.
883
884 This breaking point is sometimes useful when using attached properties to
885 communicate information to an instantiated component, as it allows their
886 initial values to be configured before property bindings take effect.
887
888 The ownership of the returned object instance is transferred to the caller.
889
890 \sa completeCreate(), QQmlEngine::ObjectOwnership
891*/
892QObject *QQmlComponent::beginCreate(QQmlContext *publicContext)
893{
894 Q_D(QQmlComponent);
895
896 Q_ASSERT(publicContext);
897 QQmlContextData *context = QQmlContextData::get(context: publicContext);
898
899 return d->beginCreate(context);
900}
901
902QObject *
903QQmlComponentPrivate::beginCreate(QQmlContextData *context)
904{
905 Q_Q(QQmlComponent);
906 if (!context) {
907 qWarning(msg: "QQmlComponent: Cannot create a component in a null context");
908 return nullptr;
909 }
910
911 if (!context->isValid()) {
912 qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context");
913 return nullptr;
914 }
915
916 if (context->engine != engine) {
917 qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine");
918 return nullptr;
919 }
920
921 if (state.completePending) {
922 qWarning(msg: "QQmlComponent: Cannot create new component instance before completing the previous");
923 return nullptr;
924 }
925
926 if (!q->isReady()) {
927 qWarning(msg: "QQmlComponent: Component is not ready");
928 return nullptr;
929 }
930
931 // Do not create infinite recursion in object creation
932 static const int maxCreationDepth = 10;
933 if (creationDepth.localData() >= maxCreationDepth) {
934 qWarning(msg: "QQmlComponent: Component creation is recursing - aborting");
935 return nullptr;
936 }
937
938 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine);
939
940 enginePriv->inProgressCreations++;
941 state.errors.clear();
942 state.completePending = true;
943
944 enginePriv->referenceScarceResources();
945 QObject *rv = nullptr;
946 state.creator.reset(other: new QQmlObjectCreator(context, compilationUnit, creationContext));
947 rv = state.creator->create(subComponentIndex: start);
948 if (!rv)
949 state.errors = state.creator->errors;
950 enginePriv->dereferenceScarceResources();
951
952 if (rv) {
953 QQmlData *ddata = QQmlData::get(object: rv);
954 Q_ASSERT(ddata);
955 //top level objects should never get JS ownership.
956 //if JS ownership is needed this needs to be explicitly undone (like in component.createObject())
957 ddata->indestructible = true;
958 ddata->explicitIndestructibleSet = true;
959 ddata->rootObjectInCreation = false;
960 }
961
962 return rv;
963}
964
965void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
966 QObject *object, DeferredState *deferredState)
967{
968 QQmlData *ddata = QQmlData::get(object);
969 Q_ASSERT(!ddata->deferredData.isEmpty());
970
971 deferredState->constructionStates.reserve(size: ddata->deferredData.size());
972
973 for (QQmlData::DeferredData *deferredData : qAsConst(t&: ddata->deferredData)) {
974 enginePriv->inProgressCreations++;
975
976 ConstructionState *state = new ConstructionState;
977 state->completePending = true;
978
979 QQmlContextData *creationContext = nullptr;
980 state->creator.reset(other: new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext));
981
982 if (!state->creator->populateDeferredProperties(instance: object, deferredData))
983 state->errors << state->creator->errors;
984 deferredData->bindings.clear();
985
986 deferredState->constructionStates += state;
987 }
988}
989
990void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState)
991{
992 for (ConstructionState *state : qAsConst(t&: deferredState->constructionStates))
993 complete(enginePriv, state);
994}
995
996void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
997{
998 if (state->completePending) {
999 QQmlInstantiationInterrupt interrupt;
1000 state->creator->finalize(interrupt);
1001
1002 state->completePending = false;
1003
1004 enginePriv->inProgressCreations--;
1005
1006 if (0 == enginePriv->inProgressCreations) {
1007 while (enginePriv->erroredBindings) {
1008 enginePriv->warning(enginePriv->erroredBindings->removeError());
1009 }
1010 }
1011 }
1012}
1013
1014/*!
1015 * \internal
1016 * Finds the matching toplevel property with name \a name of the component \a createdComponent.
1017 * If it was a required property or an alias to a required property contained in \a
1018 * requiredProperties, it is removed from it.
1019 *
1020 * If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
1021 * was found in requiredProperties.
1022 *
1023 * Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
1024 * for further processing (for instance, actually setting the property value).
1025 *
1026 * Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
1027 * classes which create components should not need it and should only need to call
1028 * setInitialProperties.
1029 */
1030QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties &requiredProperties, bool* wasInRequiredProperties)
1031{
1032 QQmlProperty prop(createdComponent, name);
1033 auto privProp = QQmlPropertyPrivate::get(p: prop);
1034 if (prop.isValid()) {
1035 // resolve outstanding required properties
1036 auto targetProp = &privProp->core;
1037 if (targetProp->isAlias()) {
1038 auto target = createdComponent;
1039 QQmlPropertyIndex originalIndex(targetProp->coreIndex());
1040 QQmlPropertyIndex propIndex;
1041 QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
1042 QQmlData *data = QQmlData::get(object: target);
1043 Q_ASSERT(data && data->propertyCache);
1044 targetProp = data->propertyCache->property(index: propIndex.coreIndex());
1045 } else {
1046 // we need to get the pointer from the property cache instead of directly using
1047 // targetProp else the lookup will fail
1048 QQmlData *data = QQmlData::get(object: createdComponent);
1049 Q_ASSERT(data && data->propertyCache);
1050 targetProp = data->propertyCache->property(index: targetProp->coreIndex());
1051 }
1052 auto it = requiredProperties.find(key: targetProp);
1053 if (it != requiredProperties.end()) {
1054 if (wasInRequiredProperties)
1055 *wasInRequiredProperties = true;
1056 requiredProperties.erase(it);
1057 } else {
1058 if (wasInRequiredProperties)
1059 *wasInRequiredProperties = false;
1060 }
1061 }
1062 return prop;
1063}
1064
1065/*!
1066 This method provides advanced control over component instance creation.
1067 In general, programmers should use QQmlComponent::create() to create a
1068 component.
1069
1070 This function completes the component creation begun with QQmlComponent::beginCreate()
1071 and must be called afterwards.
1072
1073 \sa beginCreate()
1074*/
1075void QQmlComponent::completeCreate()
1076{
1077 Q_D(QQmlComponent);
1078
1079 d->completeCreate();
1080}
1081
1082void QQmlComponentPrivate::completeCreate()
1083{
1084 const RequiredProperties& unsetRequiredProperties = requiredProperties();
1085 for (const auto& unsetRequiredProperty: unsetRequiredProperties) {
1086 QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1087 state.errors.push_back(t: error);
1088 }
1089 if (state.completePending) {
1090 ++creationDepth.localData();
1091 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine);
1092 complete(enginePriv: ep, state: &state);
1093 --creationDepth.localData();
1094 }
1095}
1096
1097QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
1098: QObject(parent), prev(nullptr), next(nullptr)
1099{
1100}
1101
1102QQmlComponentAttached::~QQmlComponentAttached()
1103{
1104 if (prev) *prev = next;
1105 if (next) next->prev = prev;
1106 prev = nullptr;
1107 next = nullptr;
1108}
1109
1110/*!
1111 \internal
1112*/
1113QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
1114{
1115 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
1116
1117 QQmlEngine *engine = qmlEngine(obj);
1118 if (!engine)
1119 return a;
1120
1121 QQmlEnginePrivate *p = QQmlEnginePrivate::get(e: engine);
1122 if (p->activeObjectCreator) { // XXX should only be allowed during begin
1123 a->add(a: p->activeObjectCreator->componentAttachment());
1124 } else {
1125 QQmlData *d = QQmlData::get(object: obj);
1126 Q_ASSERT(d);
1127 Q_ASSERT(d->context);
1128 a->add(a: &d->context->componentAttached);
1129 }
1130
1131 return a;
1132}
1133
1134/*!
1135 Create an object instance from this component using the provided
1136 \a incubator. \a context specifies the context within which to create the object
1137 instance.
1138
1139 If \a context is 0 (the default), it will create the instance in the
1140 engine's \l {QQmlEngine::rootContext()}{root context}.
1141
1142 \a forContext specifies a context that this object creation depends upon.
1143 If the \a forContext is being created asynchronously, and the
1144 \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
1145 this object will also be created asynchronously. If \a forContext is 0
1146 (the default), the \a context will be used for this decision.
1147
1148 The created object and its creation status are available via the
1149 \a incubator.
1150
1151 \sa QQmlIncubator
1152*/
1153
1154void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context,
1155 QQmlContext *forContext)
1156{
1157 Q_D(QQmlComponent);
1158
1159 if (!context)
1160 context = d->engine->rootContext();
1161
1162 QQmlContextData *contextData = QQmlContextData::get(context);
1163 QQmlContextData *forContextData = contextData;
1164 if (forContext) forContextData = QQmlContextData::get(context: forContext);
1165
1166 if (!contextData->isValid()) {
1167 qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context");
1168 return;
1169 }
1170
1171 if (contextData->engine != d->engine) {
1172 qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine");
1173 return;
1174 }
1175
1176 if (!isReady()) {
1177 qWarning(msg: "QQmlComponent: Component is not ready");
1178 return;
1179 }
1180
1181 incubator.clear();
1182 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d);
1183
1184 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: d->engine);
1185
1186 p->compilationUnit = d->compilationUnit;
1187 p->enginePriv = enginePriv;
1188 p->creator.reset(other: new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data()));
1189 p->subComponentToCreate = d->start;
1190
1191 enginePriv->incubate(incubator, forContextData);
1192}
1193
1194/*!
1195 Set toplevel \a properties of the \a component.
1196
1197
1198 This method provides advanced control over component instance creation.
1199 In general, programmers should use
1200 \l QQmlComponent::createWithInitialProperties to create a component.
1201
1202 Use this method after beginCreate and before completeCreate has been called.
1203 If a provided property does not exist, a warning is issued.
1204
1205 \since 5.14
1206*/
1207void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties)
1208{
1209 Q_D(QQmlComponent);
1210 for (auto it = properties.constBegin(); it != properties.constEnd(); ++it)
1211 d->setInitialProperty(component, name: it.key(), value: it.value());
1212}
1213
1214/*
1215 This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
1216 arguments instead of QQmlContext which means we don't have to construct the rather weighty
1217 wrapper class for every delegate item.
1218
1219 This is used by QQmlDelegateModel.
1220*/
1221void QQmlComponentPrivate::incubateObject(
1222 QQmlIncubator *incubationTask,
1223 QQmlComponent *component,
1224 QQmlEngine *engine,
1225 QQmlContextData *context,
1226 QQmlContextData *forContext)
1227{
1228 QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubator: incubationTask);
1229 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine);
1230 QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(c: component);
1231
1232 incubatorPriv->compilationUnit = componentPriv->compilationUnit;
1233 incubatorPriv->enginePriv = enginePriv;
1234 incubatorPriv->creator.reset(other: new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext));
1235 incubatorPriv->subComponentToCreate = componentPriv->start;
1236
1237 enginePriv->incubate(*incubationTask, forContext);
1238}
1239
1240
1241
1242class QQmlComponentIncubator;
1243
1244namespace QV4 {
1245
1246namespace Heap {
1247
1248#define QmlIncubatorObjectMembers(class, Member) \
1249 Member(class, HeapValue, HeapValue, valuemap) \
1250 Member(class, HeapValue, HeapValue, statusChanged) \
1251 Member(class, Pointer, QmlContext *, qmlContext) \
1252 Member(class, NoMark, QQmlComponentIncubator *, incubator) \
1253 Member(class, NoMark, QQmlQPointer<QObject>, parent)
1254
1255DECLARE_HEAP_OBJECT(QmlIncubatorObject, Object) {
1256 DECLARE_MARKOBJECTS(QmlIncubatorObject);
1257
1258 void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
1259 inline void destroy();
1260};
1261
1262}
1263
1264struct QmlIncubatorObject : public QV4::Object
1265{
1266 V4_OBJECT2(QmlIncubatorObject, Object)
1267 V4_NEEDS_DESTROY
1268
1269 static ReturnedValue method_get_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1270 static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1271 static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1272 static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1273 static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1274
1275 void statusChanged(QQmlIncubator::Status);
1276 void setInitialState(QObject *, RequiredProperties &requiredProperties);
1277};
1278
1279}
1280
1281DEFINE_OBJECT_VTABLE(QV4::QmlIncubatorObject);
1282
1283class QQmlComponentIncubator : public QQmlIncubator
1284{
1285public:
1286 QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode)
1287 : QQmlIncubator(mode)
1288 {
1289 incubatorObject.set(engine: inc->internalClass->engine, obj: inc);
1290 }
1291
1292 void statusChanged(Status s) override {
1293 QV4::Scope scope(incubatorObject.engine());
1294 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1295 i->statusChanged(s);
1296 }
1297
1298 void setInitialState(QObject *o) override {
1299 QV4::Scope scope(incubatorObject.engine());
1300 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1301 auto d = QQmlIncubatorPrivate::get(incubator: this);
1302 i->setInitialState(o, requiredProperties&: d->requiredProperties());
1303 }
1304
1305 QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating
1306};
1307
1308
1309static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
1310{
1311 if (parent) {
1312 me->setParent(parent);
1313 typedef QQmlPrivate::AutoParentFunction APF;
1314 QList<APF> functions = QQmlMetaType::parentFunctions();
1315
1316 bool needParent = false;
1317 for (int ii = 0; ii < functions.count(); ++ii) {
1318 QQmlPrivate::AutoParentResult res = functions.at(i: ii)(me, parent);
1319 if (res == QQmlPrivate::Parented) {
1320 needParent = false;
1321 break;
1322 } else if (res == QQmlPrivate::IncompatibleParent) {
1323 needParent = true;
1324 }
1325 }
1326 if (needParent)
1327 qWarning(msg: "QQmlComponent: Created graphical object was not "
1328 "placed in the graphics scene.");
1329 }
1330}
1331
1332/*!
1333 \qmlmethod object Component::createObject(QtObject parent, object properties)
1334
1335 Creates and returns an object instance of this component that will have
1336 the given \a parent and \a properties. The \a properties argument is optional.
1337 Returns null if object creation fails.
1338
1339 The object will be created in the same context as the one in which the component
1340 was created. This function will always return null when called on components
1341 which were not created in QML.
1342
1343 If you wish to create an object without setting a parent, specify \c null for
1344 the \a parent value. Note that if the returned object is to be displayed, you
1345 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
1346 property, otherwise the object will not be visible.
1347
1348 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
1349 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
1350 because setting the Item parent does not change object ownership. Only the graphical parent is changed.
1351
1352 As of \c {QtQuick 1.1}, this method accepts an optional \a properties argument that specifies a
1353 map of initial property values for the created object. These values are applied before the object
1354 creation is finalized. This is more efficient than setting property values after object creation,
1355 particularly where large sets of property values are defined, and also allows property bindings
1356 to be set up (using \l{Qt::binding}{Qt.binding}) before the object is created.
1357
1358 The \a properties argument is specified as a map of property-value items. For example, the code
1359 below creates an object with initial \c x and \c y values of 100 and 100, respectively:
1360
1361 \js
1362 var component = Qt.createComponent("Button.qml");
1363 if (component.status == Component.Ready)
1364 component.createObject(parent, {x: 100, y: 100});
1365 \endjs
1366
1367 Dynamically created instances can be deleted with the \c destroy() method.
1368 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1369
1370 \sa incubateObject()
1371*/
1372
1373
1374void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties &requiredProperties, QObject *createdComponent)
1375{
1376 QV4::Scope scope(engine);
1377 QV4::ScopedObject object(scope);
1378 QV4::ScopedObject valueMap(scope, v);
1379 QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly);
1380 QV4::ScopedString name(scope);
1381 QV4::ScopedValue val(scope);
1382 if (engine->hasException)
1383 return;
1384
1385 QV4::ScopedStackFrame frame(scope, qmlContext->d());
1386
1387 while (1) {
1388 name = it.nextPropertyNameAsString(value: val);
1389 if (!name)
1390 break;
1391 object = o;
1392 const QStringList properties = name->toQString().split(sep: QLatin1Char('.'));
1393 bool isTopLevelProperty = properties.size() == 1;
1394 for (int i = 0; i < properties.length() - 1; ++i) {
1395 name = engine->newString(s: properties.at(i));
1396 object = object->get(name);
1397 if (engine->hasException || !object) {
1398 break;
1399 }
1400 }
1401 if (engine->hasException || !object) {
1402 engine->hasException = false;
1403 continue;
1404 }
1405 name = engine->newString(s: properties.last());
1406 object->put(name, v: val);
1407 if (engine->hasException) {
1408 engine->hasException = false;
1409 continue;
1410 } else if (isTopLevelProperty) {
1411 auto prop = removePropertyFromRequired(createdComponent, name: name->toQString(), requiredProperties);
1412 }
1413 }
1414
1415 engine->hasException = false;
1416}
1417
1418QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty)
1419{
1420 QQmlError error;
1421 QString description = QLatin1String("Required property %1 was not initialized").arg(args: unsetRequiredProperty.propertyName);
1422 switch (unsetRequiredProperty.aliasesToRequired.size()) {
1423 case 0:
1424 break;
1425 case 1: {
1426 const auto info = unsetRequiredProperty.aliasesToRequired.first();
1427 description += QLatin1String("\nIt can be set via the alias property %1 from %2\n").arg(args: info.propertyName, args: info.fileUrl.toString());
1428 break;
1429 }
1430 default:
1431 description += QLatin1String("\nIt can be set via one of the following alias properties:");
1432 for (auto aliasInfo: unsetRequiredProperty.aliasesToRequired) {
1433 description += QLatin1String("\n- %1 (%2)").arg(args&: aliasInfo.propertyName, args: aliasInfo.fileUrl.toString());
1434 }
1435 description += QLatin1Char('\n');
1436 }
1437 error.setDescription(description);
1438 error.setUrl(unsetRequiredProperty.fileUrl);
1439 error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.line));
1440 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.column));
1441 return error;
1442}
1443
1444/*!
1445 \internal
1446*/
1447void QQmlComponent::createObject(QQmlV4Function *args)
1448{
1449 Q_D(QQmlComponent);
1450 Q_ASSERT(d->engine);
1451 Q_ASSERT(args);
1452
1453 QObject *parent = nullptr;
1454 QV4::ExecutionEngine *v4 = args->v4engine();
1455 QV4::Scope scope(v4);
1456 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
1457
1458 if (args->length() >= 1) {
1459 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
1460 if (qobjectWrapper)
1461 parent = qobjectWrapper->object();
1462 }
1463
1464 if (args->length() >= 2) {
1465 QV4::ScopedValue v(scope, (*args)[1]);
1466 if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
1467 qmlWarning(me: this) << tr(s: "createObject: value is not an object");
1468 args->setReturnValue(QV4::Encode::null());
1469 return;
1470 }
1471 valuemap = v;
1472 }
1473
1474 QQmlContext *ctxt = creationContext();
1475 if (!ctxt) ctxt = d->engine->rootContext();
1476
1477 QObject *rv = beginCreate(publicContext: ctxt);
1478
1479 if (!rv) {
1480 args->setReturnValue(QV4::Encode::null());
1481 return;
1482 }
1483
1484 QQmlComponent_setQmlParent(me: rv, parent);
1485
1486 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4, object: rv));
1487 Q_ASSERT(object->isObject());
1488
1489 if (!valuemap->isUndefined()) {
1490 QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
1491 QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext, o: object, v: valuemap, requiredProperties&: d->requiredProperties(), createdComponent: rv);
1492 }
1493 if (!d->requiredProperties().empty()) {
1494 QList<QQmlError> errors;
1495 for (const auto &requiredProperty: d->requiredProperties()) {
1496 errors.push_back(t: QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty: requiredProperty));
1497 }
1498 qmlWarning(me: rv, errors);
1499 args->setReturnValue(QV4::Encode::null());
1500 delete rv;
1501 return;
1502 }
1503
1504 d->completeCreate();
1505
1506 Q_ASSERT(QQmlData::get(rv));
1507 QQmlData::get(object: rv)->explicitIndestructibleSet = false;
1508 QQmlData::get(object: rv)->indestructible = false;
1509
1510 args->setReturnValue(object->asReturnedValue());
1511}
1512
1513/*!
1514 \qmlmethod object Component::incubateObject(Item parent, object properties, enumeration mode)
1515
1516 Creates an incubator for an instance of this component. Incubators allow new component
1517 instances to be instantiated asynchronously and do not cause freezes in the UI.
1518
1519 The \a parent argument specifies the parent the created instance will have. Omitting the
1520 parameter or passing null will create an object with no parent. In this case, a reference
1521 to the created object must be held so that it is not destroyed by the garbage collector.
1522
1523 The \a properties argument is specified as a map of property-value items which will be
1524 set on the created object during its construction. \a mode may be Qt.Synchronous or
1525 Qt.Asynchronous, and controls whether the instance is created synchronously or asynchronously.
1526 The default is asynchronous. In some circumstances, even if Qt.Synchronous is specified,
1527 the incubator may create the object asynchronously. This happens if the component calling
1528 incubateObject() is itself being created asynchronously.
1529
1530 All three arguments are optional.
1531
1532 If successful, the method returns an incubator, otherwise null. The incubator has the following
1533 properties:
1534
1535 \list
1536 \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and
1537 Component.Error.
1538 \li object The created object instance. Will only be available once the incubator is in the
1539 Ready status.
1540 \li onStatusChanged Specifies a callback function to be invoked when the status changes. The
1541 status is passed as a parameter to the callback.
1542 \li forceCompletion() Call to complete incubation synchronously.
1543 \endlist
1544
1545 The following example demonstrates how to use an incubator:
1546
1547 \js
1548 var component = Qt.createComponent("Button.qml");
1549
1550 var incubator = component.incubateObject(parent, { x: 10, y: 10 });
1551 if (incubator.status != Component.Ready) {
1552 incubator.onStatusChanged = function(status) {
1553 if (status == Component.Ready) {
1554 print ("Object", incubator.object, "is now ready!");
1555 }
1556 }
1557 } else {
1558 print ("Object", incubator.object, "is ready immediately!");
1559 }
1560 \endjs
1561
1562 Dynamically created instances can be deleted with the \c destroy() method.
1563 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1564
1565 \sa createObject()
1566*/
1567
1568/*!
1569 \internal
1570*/
1571void QQmlComponent::incubateObject(QQmlV4Function *args)
1572{
1573 Q_D(QQmlComponent);
1574 Q_ASSERT(d->engine);
1575 Q_UNUSED(d);
1576 Q_ASSERT(args);
1577 QV4::ExecutionEngine *v4 = args->v4engine();
1578 QV4::Scope scope(v4);
1579
1580 QObject *parent = nullptr;
1581 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
1582 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
1583
1584 if (args->length() >= 1) {
1585 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
1586 if (qobjectWrapper)
1587 parent = qobjectWrapper->object();
1588 }
1589
1590 if (args->length() >= 2) {
1591 QV4::ScopedValue v(scope, (*args)[1]);
1592 if (v->isNull()) {
1593 } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
1594 qmlWarning(me: this) << tr(s: "createObject: value is not an object");
1595 args->setReturnValue(QV4::Encode::null());
1596 return;
1597 } else {
1598 valuemap = v;
1599 }
1600 }
1601
1602 if (args->length() >= 3) {
1603 QV4::ScopedValue val(scope, (*args)[2]);
1604 quint32 v = val->toUInt32();
1605 if (v == 0)
1606 mode = QQmlIncubator::Asynchronous;
1607 else if (v == 1)
1608 mode = QQmlIncubator::AsynchronousIfNested;
1609 }
1610
1611 QQmlComponentExtension *e = componentExtension(engine: args->v4engine());
1612
1613 QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(args: mode));
1614 QV4::ScopedObject p(scope, e->incubationProto.value());
1615 r->setPrototypeOf(p);
1616
1617 if (!valuemap->isUndefined())
1618 r->d()->valuemap.set(e: scope.engine, newVal: valuemap);
1619 r->d()->qmlContext.set(e: scope.engine, newVal: v4->qmlContext());
1620 r->d()->parent = parent;
1621
1622 QQmlIncubator *incubator = r->d()->incubator;
1623 create(incubator&: *incubator, context: creationContext());
1624
1625 if (incubator->status() == QQmlIncubator::Null) {
1626 args->setReturnValue(QV4::Encode::null());
1627 } else {
1628 args->setReturnValue(r.asReturnedValue());
1629 }
1630}
1631
1632// XXX used by QSGLoader
1633void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties &requiredProperties)
1634{
1635 QV4::ExecutionEngine *v4engine = engine->handle();
1636 QV4::Scope scope(v4engine);
1637
1638 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4engine, object: toCreate));
1639 Q_ASSERT(object->as<QV4::Object>());
1640
1641 if (!valuemap.isUndefined())
1642 setInitialProperties(engine: v4engine, qmlContext, o: object, v: valuemap, requiredProperties, createdComponent: toCreate);
1643}
1644
1645QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
1646{
1647 QV4::Scope scope(v4);
1648 QV4::ScopedObject proto(scope, v4->newObject());
1649 proto->defineAccessorProperty(QStringLiteral("onStatusChanged"),
1650 getter: QV4::QmlIncubatorObject::method_get_statusChanged, setter: QV4::QmlIncubatorObject::method_set_statusChanged);
1651 proto->defineAccessorProperty(QStringLiteral("status"), getter: QV4::QmlIncubatorObject::method_get_status, setter: nullptr);
1652 proto->defineAccessorProperty(QStringLiteral("object"), getter: QV4::QmlIncubatorObject::method_get_object, setter: nullptr);
1653 proto->defineDefaultProperty(QStringLiteral("forceCompletion"), code: QV4::QmlIncubatorObject::method_forceCompletion);
1654
1655 incubationProto.set(engine: v4, value: proto);
1656}
1657
1658QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const FunctionObject *b, const Value *thisObject, const Value *, int)
1659{
1660 QV4::Scope scope(b);
1661 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1662 if (!o)
1663 THROW_TYPE_ERROR();
1664
1665 return QV4::QObjectWrapper::wrap(engine: scope.engine, object: o->d()->incubator->object());
1666}
1667
1668QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const FunctionObject *b, const Value *thisObject, const Value *, int)
1669{
1670 QV4::Scope scope(b);
1671 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1672 if (!o)
1673 THROW_TYPE_ERROR();
1674
1675 o->d()->incubator->forceCompletion();
1676
1677 RETURN_UNDEFINED();
1678}
1679
1680QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int)
1681{
1682 QV4::Scope scope(b);
1683 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1684 if (!o)
1685 THROW_TYPE_ERROR();
1686
1687 return QV4::Encode(o->d()->incubator->status());
1688}
1689
1690QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *, int)
1691{
1692 QV4::Scope scope(b);
1693 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1694 if (!o)
1695 THROW_TYPE_ERROR();
1696
1697 return QV4::Encode(o->d()->statusChanged);
1698}
1699
1700QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1701{
1702 QV4::Scope scope(b);
1703 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1704 if (!o || argc < 1)
1705 THROW_TYPE_ERROR();
1706
1707 o->d()->statusChanged.set(e: scope.engine, newVal: argv[0]);
1708
1709 RETURN_UNDEFINED();
1710}
1711
1712QQmlComponentExtension::~QQmlComponentExtension()
1713{
1714}
1715
1716void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
1717{
1718 Object::init();
1719 valuemap.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue());
1720 statusChanged.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue());
1721 parent.init();
1722 qmlContext.set(e: internalClass->engine, newVal: nullptr);
1723 incubator = new QQmlComponentIncubator(this, m);
1724}
1725
1726void QV4::Heap::QmlIncubatorObject::destroy() {
1727 delete incubator;
1728 parent.destroy();
1729 Object::destroy();
1730}
1731
1732void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties &requiredProperties)
1733{
1734 QQmlComponent_setQmlParent(me: o, parent: d()->parent);
1735
1736 if (!d()->valuemap.isUndefined()) {
1737 QV4::ExecutionEngine *v4 = engine();
1738 QV4::Scope scope(v4);
1739 QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(engine: v4, object: o));
1740 QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
1741 QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext: qmlCtxt, o: obj, v: d()->valuemap, requiredProperties, createdComponent: o);
1742 }
1743}
1744
1745void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
1746{
1747 QV4::Scope scope(engine());
1748 // hold the incubated object in a scoped value to prevent it's destruction before this method returns
1749 QV4::ScopedObject incubatedObject(scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object: d()->incubator->object()));
1750
1751 if (s == QQmlIncubator::Ready) {
1752 Q_ASSERT(QQmlData::get(d()->incubator->object()));
1753 QQmlData::get(object: d()->incubator->object())->explicitIndestructibleSet = false;
1754 QQmlData::get(object: d()->incubator->object())->indestructible = false;
1755 }
1756
1757 QV4::ScopedFunctionObject f(scope, d()->statusChanged);
1758 if (f) {
1759 QV4::JSCallData jsCallData(scope, 1);
1760 *jsCallData->thisObject = this;
1761 jsCallData->args[0] = QV4::Value::fromUInt32(i: s);
1762 f->call(data: jsCallData);
1763 if (scope.hasException()) {
1764 QQmlError error = scope.engine->catchExceptionAsQmlError();
1765 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(e: scope.engine->qmlEngine()), error);
1766 }
1767 }
1768
1769 if (s != QQmlIncubator::Loading)
1770 d()->incubator->incubatorObject.clear();
1771}
1772
1773#undef INITIALPROPERTIES_SOURCE
1774
1775QT_END_NAMESPACE
1776
1777#include "moc_qqmlcomponent.cpp"
1778

source code of qtdeclarative/src/qml/qml/qqmlcomponent.cpp