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 if (!prop.isValid() || !privProp->writeValueProperty(value, {})) {
379 QQmlError error{};
380 error.setUrl(url);
381 error.setDescription(QLatin1String("Could not set property %1").arg(args: name));
382 state.errors.push_back(t: error);
383 return false;
384 } else
385 return true;
386}
387
388/*!
389 \internal
390*/
391QQmlComponent::QQmlComponent(QObject *parent)
392 : QObject(*(new QQmlComponentPrivate), parent)
393{
394}
395
396/*!
397 Destruct the QQmlComponent.
398*/
399QQmlComponent::~QQmlComponent()
400{
401 Q_D(QQmlComponent);
402
403 if (d->state.completePending) {
404 qWarning(msg: "QQmlComponent: Component destroyed while completion pending");
405
406 if (isError()) {
407 qWarning() << "This may have been caused by one of the following errors:";
408 for (const QQmlError &error : qAsConst(t&: d->state.errors))
409 qWarning().nospace().noquote() << QLatin1String(" ") << error;
410 }
411
412 d->completeCreate();
413 }
414
415 if (d->typeData) {
416 d->typeData->unregisterCallback(d);
417 d->typeData = nullptr;
418 }
419}
420
421/*!
422 \qmlproperty enumeration Component::status
423 This property holds the status of component loading. The status can be one of the
424 following:
425 \list
426 \li Component.Null - no data is available for the component
427 \li Component.Ready - the component has been loaded, and can be used to create instances.
428 \li Component.Loading - the component is currently being loaded
429 \li Component.Error - an error occurred while loading the component.
430 Calling errorString() will provide a human-readable description of any errors.
431 \endlist
432 */
433
434/*!
435 \property QQmlComponent::status
436 The component's current \l{QQmlComponent::Status} {status}.
437 */
438QQmlComponent::Status QQmlComponent::status() const
439{
440 Q_D(const QQmlComponent);
441
442 if (d->typeData)
443 return Loading;
444 else if (!d->state.errors.isEmpty())
445 return Error;
446 else if (d->engine && d->compilationUnit)
447 return Ready;
448 else
449 return Null;
450}
451
452/*!
453 Returns true if status() == QQmlComponent::Null.
454*/
455bool QQmlComponent::isNull() const
456{
457 return status() == Null;
458}
459
460/*!
461 Returns true if status() == QQmlComponent::Ready.
462*/
463bool QQmlComponent::isReady() const
464{
465 return status() == Ready;
466}
467
468/*!
469 Returns true if status() == QQmlComponent::Error.
470*/
471bool QQmlComponent::isError() const
472{
473 return status() == Error;
474}
475
476/*!
477 Returns true if status() == QQmlComponent::Loading.
478*/
479bool QQmlComponent::isLoading() const
480{
481 return status() == Loading;
482}
483
484/*!
485 \qmlproperty real Component::progress
486 The progress of loading the component, from 0.0 (nothing loaded)
487 to 1.0 (finished).
488*/
489
490/*!
491 \property QQmlComponent::progress
492 The progress of loading the component, from 0.0 (nothing loaded)
493 to 1.0 (finished).
494*/
495qreal QQmlComponent::progress() const
496{
497 Q_D(const QQmlComponent);
498 return d->progress;
499}
500
501/*!
502 \fn void QQmlComponent::progressChanged(qreal progress)
503
504 Emitted whenever the component's loading progress changes. \a progress will be the
505 current progress between 0.0 (nothing loaded) and 1.0 (finished).
506*/
507
508/*!
509 \fn void QQmlComponent::statusChanged(QQmlComponent::Status status)
510
511 Emitted whenever the component's status changes. \a status will be the
512 new status.
513*/
514
515/*!
516 Create a QQmlComponent with no data and give it the specified
517 \a engine and \a parent. Set the data with setData().
518*/
519QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
520 : QObject(*(new QQmlComponentPrivate), parent)
521{
522 Q_D(QQmlComponent);
523 d->engine = engine;
524}
525
526/*!
527 Create a QQmlComponent from the given \a url and give it the
528 specified \a parent and \a engine.
529
530 \include qqmlcomponent.qdoc url-note
531
532 \sa loadUrl()
533*/
534QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
535 : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent)
536{
537}
538
539/*!
540 Create a QQmlComponent from the given \a url and give it the
541 specified \a parent and \a engine. If \a mode is \l Asynchronous,
542 the component will be loaded and compiled asynchronously.
543
544 \include qqmlcomponent.qdoc url-note
545
546 \sa loadUrl()
547*/
548QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode,
549 QObject *parent)
550 : QQmlComponent(engine, parent)
551{
552 Q_D(QQmlComponent);
553 d->loadUrl(newUrl: url, mode);
554}
555
556/*!
557 Create a QQmlComponent from the given \a fileName and give it the specified
558 \a parent and \a engine.
559
560 \sa loadUrl()
561*/
562QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
563 QObject *parent)
564 : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent)
565{
566}
567
568/*!
569 Create a QQmlComponent from the given \a fileName and give it the specified
570 \a parent and \a engine. If \a mode is \l Asynchronous,
571 the component will be loaded and compiled asynchronously.
572
573 \sa loadUrl()
574*/
575QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
576 CompilationMode mode, QObject *parent)
577 : QQmlComponent(engine, parent)
578{
579 Q_D(QQmlComponent);
580 const QUrl url = QDir::isAbsolutePath(path: fileName) ? QUrl::fromLocalFile(localfile: fileName) : QUrl(fileName);
581 d->loadUrl(newUrl: url, mode);
582}
583
584/*!
585 \internal
586*/
587QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::ExecutableCompilationUnit *compilationUnit,
588 int start, QObject *parent)
589 : QQmlComponent(engine, parent)
590{
591 Q_D(QQmlComponent);
592 d->compilationUnit = compilationUnit;
593 d->start = start;
594 d->url = compilationUnit->finalUrl();
595 d->progress = 1.0;
596}
597
598/*!
599 Sets the QQmlComponent to use the given QML \a data. If \a url
600 is provided, it is used to set the component name and to provide
601 a base path for items resolved by this component.
602*/
603void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
604{
605 Q_D(QQmlComponent);
606
607 if (!d->engine) {
608 // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check
609 qWarning(msg: "QQmlComponent: Must provide an engine before calling setData");
610 return;
611 }
612
613 d->clear();
614
615 d->url = url;
616
617 QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(e: d->engine)->typeLoader.getType(data, url);
618
619 if (typeData->isCompleteOrError()) {
620 d->fromTypeData(data: typeData);
621 } else {
622 d->typeData = typeData;
623 d->typeData->registerCallback(d);
624 }
625
626 d->progress = 1.0;
627 emit statusChanged(status());
628 emit progressChanged(d->progress);
629}
630
631/*!
632 Returns the QQmlContext the component was created in. This is only
633 valid for components created directly from QML.
634*/
635QQmlContext *QQmlComponent::creationContext() const
636{
637 Q_D(const QQmlComponent);
638 if(d->creationContext)
639 return d->creationContext->asQQmlContext();
640
641 return qmlContext(this);
642}
643
644/*!
645 Returns the QQmlEngine of this component.
646
647 \since 5.12
648*/
649QQmlEngine *QQmlComponent::engine() const
650{
651 Q_D(const QQmlComponent);
652 return d->engine;
653}
654
655/*!
656 Load the QQmlComponent from the provided \a url.
657
658 \include qqmlcomponent.qdoc url-note
659*/
660void QQmlComponent::loadUrl(const QUrl &url)
661{
662 Q_D(QQmlComponent);
663 d->loadUrl(newUrl: url);
664}
665
666/*!
667 Load the QQmlComponent from the provided \a url.
668 If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
669
670 \include qqmlcomponent.qdoc url-note
671*/
672void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
673{
674 Q_D(QQmlComponent);
675 d->loadUrl(newUrl: url, mode);
676}
677
678void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
679{
680 Q_Q(QQmlComponent);
681 clear();
682
683 if (newUrl.isRelative()) {
684 // The new URL is a relative URL like QUrl("main.qml").
685 url = engine->baseUrl().resolved(relative: QUrl(newUrl.toString()));
686 } else if (engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(path: newUrl.toLocalFile())) {
687 // The new URL is a file on disk but it's a relative path; e.g.:
688 // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml")
689 // We need to remove the scheme so that it becomes a relative URL with a relative path:
690 QUrl fixedUrl(newUrl);
691 fixedUrl.setScheme(QString());
692 // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl().
693 // This is a compatibility hack for QTBUG-58837.
694 url = engine->baseUrl().resolved(relative: fixedUrl);
695 } else {
696 url = newUrl;
697 }
698
699 if (newUrl.isEmpty()) {
700 QQmlError error;
701 error.setDescription(QQmlComponent::tr(s: "Invalid empty URL"));
702 state.errors << error;
703 return;
704 }
705
706 if (progress != 0.0) {
707 progress = 0.0;
708 emit q->progressChanged(progress);
709 }
710
711 QQmlTypeLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
712 ? QQmlTypeLoader::Asynchronous
713 : QQmlTypeLoader::PreferSynchronous;
714
715 QQmlRefPointer<QQmlTypeData> data = QQmlEnginePrivate::get(e: engine)->typeLoader.getType(unNormalizedUrl: url, mode: loaderMode);
716
717 if (data->isCompleteOrError()) {
718 fromTypeData(data);
719 progress = 1.0;
720 } else {
721 typeData = data;
722 typeData->registerCallback(this);
723 progress = data->progress();
724 }
725
726 emit q->statusChanged(q->status());
727 if (progress != 0.0)
728 emit q->progressChanged(progress);
729}
730
731/*!
732 Returns the list of errors that occurred during the last compile or create
733 operation. An empty list is returned if isError() is not set.
734*/
735QList<QQmlError> QQmlComponent::errors() const
736{
737 Q_D(const QQmlComponent);
738 if (isError())
739 return d->state.errors;
740 else
741 return QList<QQmlError>();
742}
743
744/*!
745 \qmlmethod string Component::errorString()
746
747 Returns a human-readable description of any error.
748
749 The string includes the file, location, and description of each error.
750 If multiple errors are present, they are separated by a newline character.
751
752 If no errors are present, an empty string is returned.
753*/
754
755/*!
756 \internal
757 errorString is only meant as a way to get the errors in script
758*/
759QString QQmlComponent::errorString() const
760{
761 Q_D(const QQmlComponent);
762 QString ret;
763 if(!isError())
764 return ret;
765 for (const QQmlError &e : d->state.errors) {
766 ret += e.url().toString() + QLatin1Char(':') +
767 QString::number(e.line()) + QLatin1Char(' ') +
768 e.description() + QLatin1Char('\n');
769 }
770 return ret;
771}
772
773/*!
774 \qmlproperty url Component::url
775 The component URL. This is the URL that was used to construct the component.
776*/
777
778/*!
779 \property QQmlComponent::url
780 The component URL. This is the URL passed to either the constructor,
781 or the loadUrl(), or setData() methods.
782*/
783QUrl QQmlComponent::url() const
784{
785 Q_D(const QQmlComponent);
786 return d->url;
787}
788
789/*!
790 \internal
791*/
792QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
793 : QObject(dd, parent)
794{
795}
796
797/*!
798 Create an object instance from this component. Returns \nullptr if creation
799 failed. \a context specifies the context within which to create the object
800 instance.
801
802 If \a context is \nullptr (the default), it will create the instance in the
803 \l {QQmlEngine::rootContext()}{root context} of the engine.
804
805 The ownership of the returned object instance is transferred to the caller.
806
807 If the object being created from this component is a visual item, it must
808 have a visual parent, which can be set by calling
809 QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick}
810 for more details.
811
812 \sa QQmlEngine::ObjectOwnership
813*/
814QObject *QQmlComponent::create(QQmlContext *context)
815{
816 Q_D(QQmlComponent);
817
818 QObject *rv = d->doBeginCreate(q: this, context);
819 if (rv)
820 completeCreate();
821 if (rv && !d->requiredProperties().empty()) {
822 delete rv;
823 return nullptr;
824 }
825 return rv;
826}
827
828/*!
829 Create an object instance of this component, and initialize its toplevel
830 properties with \a initialProperties. \a context specifies the context
831 where the object instance is to be created.
832
833 \sa QQmlComponent::create
834 \since 5.14
835*/
836QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialProperties, QQmlContext *context)
837{
838 Q_D(QQmlComponent);
839
840 QObject *rv = d->doBeginCreate(q: this, context);
841 if (rv) {
842 setInitialProperties(component: rv, properties: initialProperties);
843 completeCreate();
844 }
845 if (!d->requiredProperties().empty()) {
846 d->requiredProperties().clear();
847 return nullptr;
848 }
849 return rv;
850}
851
852/*!
853 This method provides advanced control over component instance creation.
854 In general, programmers should use QQmlComponent::create() to create object
855 instances.
856
857 Create an object instance from this component. Returns \nullptr if creation
858 failed. \a publicContext specifies the context within which to create the object
859 instance.
860
861 When QQmlComponent constructs an instance, it occurs in three steps:
862 \list 1
863 \li The object hierarchy is created, and constant values are assigned.
864 \li Property bindings are evaluated for the first time.
865 \li If applicable, QQmlParserStatus::componentComplete() is called on objects.
866 \endlist
867 QQmlComponent::beginCreate() differs from QQmlComponent::create() in that it
868 only performs step 1. QQmlComponent::completeCreate() must be called to
869 complete steps 2 and 3.
870
871 This breaking point is sometimes useful when using attached properties to
872 communicate information to an instantiated component, as it allows their
873 initial values to be configured before property bindings take effect.
874
875 The ownership of the returned object instance is transferred to the caller.
876
877 \sa completeCreate(), QQmlEngine::ObjectOwnership
878*/
879QObject *QQmlComponent::beginCreate(QQmlContext *publicContext)
880{
881 Q_D(QQmlComponent);
882
883 Q_ASSERT(publicContext);
884 QQmlContextData *context = QQmlContextData::get(context: publicContext);
885
886 return d->beginCreate(context);
887}
888
889QObject *
890QQmlComponentPrivate::beginCreate(QQmlContextData *context)
891{
892 Q_Q(QQmlComponent);
893 if (!context) {
894 qWarning(msg: "QQmlComponent: Cannot create a component in a null context");
895 return nullptr;
896 }
897
898 if (!context->isValid()) {
899 qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context");
900 return nullptr;
901 }
902
903 if (context->engine != engine) {
904 qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine");
905 return nullptr;
906 }
907
908 if (state.completePending) {
909 qWarning(msg: "QQmlComponent: Cannot create new component instance before completing the previous");
910 return nullptr;
911 }
912
913 if (!q->isReady()) {
914 qWarning(msg: "QQmlComponent: Component is not ready");
915 return nullptr;
916 }
917
918 // Do not create infinite recursion in object creation
919 static const int maxCreationDepth = 10;
920 if (creationDepth.localData() >= maxCreationDepth) {
921 qWarning(msg: "QQmlComponent: Component creation is recursing - aborting");
922 return nullptr;
923 }
924
925 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine);
926
927 enginePriv->inProgressCreations++;
928 state.errors.clear();
929 state.completePending = true;
930
931 enginePriv->referenceScarceResources();
932 QObject *rv = nullptr;
933 state.creator.reset(other: new QQmlObjectCreator(context, compilationUnit, creationContext));
934 rv = state.creator->create(subComponentIndex: start);
935 if (!rv)
936 state.errors = state.creator->errors;
937 enginePriv->dereferenceScarceResources();
938
939 if (rv) {
940 QQmlData *ddata = QQmlData::get(object: rv);
941 Q_ASSERT(ddata);
942 //top level objects should never get JS ownership.
943 //if JS ownership is needed this needs to be explicitly undone (like in component.createObject())
944 ddata->indestructible = true;
945 ddata->explicitIndestructibleSet = true;
946 ddata->rootObjectInCreation = false;
947 }
948
949 return rv;
950}
951
952void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
953 QObject *object, DeferredState *deferredState)
954{
955 QQmlData *ddata = QQmlData::get(object);
956 Q_ASSERT(!ddata->deferredData.isEmpty());
957
958 deferredState->constructionStates.reserve(size: ddata->deferredData.size());
959
960 for (QQmlData::DeferredData *deferredData : qAsConst(t&: ddata->deferredData)) {
961 enginePriv->inProgressCreations++;
962
963 ConstructionState *state = new ConstructionState;
964 state->completePending = true;
965
966 QQmlContextData *creationContext = nullptr;
967 state->creator.reset(other: new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext));
968
969 if (!state->creator->populateDeferredProperties(instance: object, deferredData))
970 state->errors << state->creator->errors;
971 deferredData->bindings.clear();
972
973 deferredState->constructionStates += state;
974 }
975}
976
977void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState)
978{
979 for (ConstructionState *state : qAsConst(t&: deferredState->constructionStates))
980 complete(enginePriv, state);
981}
982
983void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
984{
985 if (state->completePending) {
986 QQmlInstantiationInterrupt interrupt;
987 state->creator->finalize(interrupt);
988
989 state->completePending = false;
990
991 enginePriv->inProgressCreations--;
992
993 if (0 == enginePriv->inProgressCreations) {
994 while (enginePriv->erroredBindings) {
995 enginePriv->warning(enginePriv->erroredBindings->removeError());
996 }
997 }
998 }
999}
1000
1001/*!
1002 * \internal
1003 * Finds the matching toplevel property with name \a name of the component \a createdComponent.
1004 * If it was a required property or an alias to a required property contained in \a
1005 * requiredProperties, it is removed from it.
1006 *
1007 * If wasInRequiredProperties is non-null, the referenced boolean is set to true iff the property
1008 * was found in requiredProperties.
1009 *
1010 * Returns the QQmlProperty with name \a name (which might be invalid if there is no such property),
1011 * for further processing (for instance, actually setting the property value).
1012 *
1013 * Note: This method is used in QQmlComponent and QQmlIncubator to manage required properties. Most
1014 * classes which create components should not need it and should only need to call
1015 * setInitialProperties.
1016 */
1017QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties &requiredProperties, bool* wasInRequiredProperties)
1018{
1019 QQmlProperty prop(createdComponent, name);
1020 auto privProp = QQmlPropertyPrivate::get(p: prop);
1021 if (prop.isValid()) {
1022 // resolve outstanding required properties
1023 auto targetProp = &privProp->core;
1024 if (targetProp->isAlias()) {
1025 auto target = createdComponent;
1026 QQmlPropertyIndex originalIndex(targetProp->coreIndex());
1027 QQmlPropertyIndex propIndex;
1028 QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
1029 QQmlData *data = QQmlData::get(object: target);
1030 Q_ASSERT(data && data->propertyCache);
1031 targetProp = data->propertyCache->property(index: propIndex.coreIndex());
1032 } else {
1033 // we need to get the pointer from the property cache instead of directly using
1034 // targetProp else the lookup will fail
1035 QQmlData *data = QQmlData::get(object: createdComponent);
1036 Q_ASSERT(data && data->propertyCache);
1037 targetProp = data->propertyCache->property(index: targetProp->coreIndex());
1038 }
1039 auto it = requiredProperties.find(key: targetProp);
1040 if (it != requiredProperties.end()) {
1041 if (wasInRequiredProperties)
1042 *wasInRequiredProperties = true;
1043 requiredProperties.erase(it);
1044 } else {
1045 if (wasInRequiredProperties)
1046 *wasInRequiredProperties = false;
1047 }
1048 }
1049 return prop;
1050}
1051
1052/*!
1053 This method provides advanced control over component instance creation.
1054 In general, programmers should use QQmlComponent::create() to create a
1055 component.
1056
1057 This function completes the component creation begun with QQmlComponent::beginCreate()
1058 and must be called afterwards.
1059
1060 \sa beginCreate()
1061*/
1062void QQmlComponent::completeCreate()
1063{
1064 Q_D(QQmlComponent);
1065
1066 d->completeCreate();
1067}
1068
1069void QQmlComponentPrivate::completeCreate()
1070{
1071 const RequiredProperties& unsetRequiredProperties = requiredProperties();
1072 for (const auto& unsetRequiredProperty: unsetRequiredProperties) {
1073 QQmlError error = unsetRequiredPropertyToQQmlError(unsetRequiredProperty);
1074 state.errors.push_back(t: error);
1075 }
1076 if (state.completePending) {
1077 ++creationDepth.localData();
1078 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine);
1079 complete(enginePriv: ep, state: &state);
1080 --creationDepth.localData();
1081 }
1082}
1083
1084QQmlComponentAttached::QQmlComponentAttached(QObject *parent)
1085: QObject(parent), prev(nullptr), next(nullptr)
1086{
1087}
1088
1089QQmlComponentAttached::~QQmlComponentAttached()
1090{
1091 if (prev) *prev = next;
1092 if (next) next->prev = prev;
1093 prev = nullptr;
1094 next = nullptr;
1095}
1096
1097/*!
1098 \internal
1099*/
1100QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj)
1101{
1102 QQmlComponentAttached *a = new QQmlComponentAttached(obj);
1103
1104 QQmlEngine *engine = qmlEngine(obj);
1105 if (!engine)
1106 return a;
1107
1108 QQmlEnginePrivate *p = QQmlEnginePrivate::get(e: engine);
1109 if (p->activeObjectCreator) { // XXX should only be allowed during begin
1110 a->add(a: p->activeObjectCreator->componentAttachment());
1111 } else {
1112 QQmlData *d = QQmlData::get(object: obj);
1113 Q_ASSERT(d);
1114 Q_ASSERT(d->context);
1115 a->add(a: &d->context->componentAttached);
1116 }
1117
1118 return a;
1119}
1120
1121/*!
1122 Create an object instance from this component using the provided
1123 \a incubator. \a context specifies the context within which to create the object
1124 instance.
1125
1126 If \a context is 0 (the default), it will create the instance in the
1127 engine's \l {QQmlEngine::rootContext()}{root context}.
1128
1129 \a forContext specifies a context that this object creation depends upon.
1130 If the \a forContext is being created asynchronously, and the
1131 \l QQmlIncubator::IncubationMode is \l QQmlIncubator::AsynchronousIfNested,
1132 this object will also be created asynchronously. If \a forContext is 0
1133 (the default), the \a context will be used for this decision.
1134
1135 The created object and its creation status are available via the
1136 \a incubator.
1137
1138 \sa QQmlIncubator
1139*/
1140
1141void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context,
1142 QQmlContext *forContext)
1143{
1144 Q_D(QQmlComponent);
1145
1146 if (!context)
1147 context = d->engine->rootContext();
1148
1149 QQmlContextData *contextData = QQmlContextData::get(context);
1150 QQmlContextData *forContextData = contextData;
1151 if (forContext) forContextData = QQmlContextData::get(context: forContext);
1152
1153 if (!contextData->isValid()) {
1154 qWarning(msg: "QQmlComponent: Cannot create a component in an invalid context");
1155 return;
1156 }
1157
1158 if (contextData->engine != d->engine) {
1159 qWarning(msg: "QQmlComponent: Must create component in context from the same QQmlEngine");
1160 return;
1161 }
1162
1163 if (!isReady()) {
1164 qWarning(msg: "QQmlComponent: Component is not ready");
1165 return;
1166 }
1167
1168 incubator.clear();
1169 QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(incubator.d);
1170
1171 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: d->engine);
1172
1173 p->compilationUnit = d->compilationUnit;
1174 p->enginePriv = enginePriv;
1175 p->creator.reset(other: new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data()));
1176 p->subComponentToCreate = d->start;
1177
1178 enginePriv->incubate(incubator, forContextData);
1179}
1180
1181/*!
1182 Set toplevel \a properties of the \a component.
1183
1184
1185 This method provides advanced control over component instance creation.
1186 In general, programmers should use
1187 \l QQmlComponent::createWithInitialProperties to create a component.
1188
1189 Use this method after beginCreate and before completeCreate has been called.
1190 If a provided property does not exist, a warning is issued.
1191
1192 \since 5.14
1193*/
1194void QQmlComponent::setInitialProperties(QObject *component, const QVariantMap &properties)
1195{
1196 Q_D(QQmlComponent);
1197 for (auto it = properties.constBegin(); it != properties.constEnd(); ++it)
1198 d->setInitialProperty(component, name: it.key(), value: it.value());
1199}
1200
1201/*
1202 This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
1203 arguments instead of QQmlContext which means we don't have to construct the rather weighty
1204 wrapper class for every delegate item.
1205
1206 This is used by QQmlDelegateModel.
1207*/
1208void QQmlComponentPrivate::incubateObject(
1209 QQmlIncubator *incubationTask,
1210 QQmlComponent *component,
1211 QQmlEngine *engine,
1212 QQmlContextData *context,
1213 QQmlContextData *forContext)
1214{
1215 QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubator: incubationTask);
1216 QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(e: engine);
1217 QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(c: component);
1218
1219 incubatorPriv->compilationUnit = componentPriv->compilationUnit;
1220 incubatorPriv->enginePriv = enginePriv;
1221 incubatorPriv->creator.reset(other: new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext));
1222 incubatorPriv->subComponentToCreate = componentPriv->start;
1223
1224 enginePriv->incubate(*incubationTask, forContext);
1225}
1226
1227
1228
1229class QQmlComponentIncubator;
1230
1231namespace QV4 {
1232
1233namespace Heap {
1234
1235#define QmlIncubatorObjectMembers(class, Member) \
1236 Member(class, HeapValue, HeapValue, valuemap) \
1237 Member(class, HeapValue, HeapValue, statusChanged) \
1238 Member(class, Pointer, QmlContext *, qmlContext) \
1239 Member(class, NoMark, QQmlComponentIncubator *, incubator) \
1240 Member(class, NoMark, QQmlQPointer<QObject>, parent)
1241
1242DECLARE_HEAP_OBJECT(QmlIncubatorObject, Object) {
1243 DECLARE_MARKOBJECTS(QmlIncubatorObject);
1244
1245 void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
1246 inline void destroy();
1247};
1248
1249}
1250
1251struct QmlIncubatorObject : public QV4::Object
1252{
1253 V4_OBJECT2(QmlIncubatorObject, Object)
1254 V4_NEEDS_DESTROY
1255
1256 static ReturnedValue method_get_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1257 static ReturnedValue method_set_statusChanged(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1258 static ReturnedValue method_get_status(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1259 static ReturnedValue method_get_object(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1260 static ReturnedValue method_forceCompletion(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
1261
1262 void statusChanged(QQmlIncubator::Status);
1263 void setInitialState(QObject *, RequiredProperties &requiredProperties);
1264};
1265
1266}
1267
1268DEFINE_OBJECT_VTABLE(QV4::QmlIncubatorObject);
1269
1270class QQmlComponentIncubator : public QQmlIncubator
1271{
1272public:
1273 QQmlComponentIncubator(QV4::Heap::QmlIncubatorObject *inc, IncubationMode mode)
1274 : QQmlIncubator(mode)
1275 {
1276 incubatorObject.set(engine: inc->internalClass->engine, obj: inc);
1277 }
1278
1279 void statusChanged(Status s) override {
1280 QV4::Scope scope(incubatorObject.engine());
1281 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1282 i->statusChanged(s);
1283 }
1284
1285 void setInitialState(QObject *o) override {
1286 QV4::Scope scope(incubatorObject.engine());
1287 QV4::Scoped<QV4::QmlIncubatorObject> i(scope, incubatorObject.as<QV4::QmlIncubatorObject>());
1288 auto d = QQmlIncubatorPrivate::get(incubator: this);
1289 i->setInitialState(o, requiredProperties&: d->requiredProperties());
1290 }
1291
1292 QV4::PersistentValue incubatorObject; // keep a strong internal reference while incubating
1293};
1294
1295
1296static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
1297{
1298 if (parent) {
1299 me->setParent(parent);
1300 typedef QQmlPrivate::AutoParentFunction APF;
1301 QList<APF> functions = QQmlMetaType::parentFunctions();
1302
1303 bool needParent = false;
1304 for (int ii = 0; ii < functions.count(); ++ii) {
1305 QQmlPrivate::AutoParentResult res = functions.at(i: ii)(me, parent);
1306 if (res == QQmlPrivate::Parented) {
1307 needParent = false;
1308 break;
1309 } else if (res == QQmlPrivate::IncompatibleParent) {
1310 needParent = true;
1311 }
1312 }
1313 if (needParent)
1314 qWarning(msg: "QQmlComponent: Created graphical object was not "
1315 "placed in the graphics scene.");
1316 }
1317}
1318
1319/*!
1320 \qmlmethod object Component::createObject(QtObject parent, object properties)
1321
1322 Creates and returns an object instance of this component that will have
1323 the given \a parent and \a properties. The \a properties argument is optional.
1324 Returns null if object creation fails.
1325
1326 The object will be created in the same context as the one in which the component
1327 was created. This function will always return null when called on components
1328 which were not created in QML.
1329
1330 If you wish to create an object without setting a parent, specify \c null for
1331 the \a parent value. Note that if the returned object is to be displayed, you
1332 must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent}
1333 property, otherwise the object will not be visible.
1334
1335 If a \a parent is not provided to createObject(), a reference to the returned object must be held so that
1336 it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards,
1337 because setting the Item parent does not change object ownership. Only the graphical parent is changed.
1338
1339 As of \c {QtQuick 1.1}, this method accepts an optional \a properties argument that specifies a
1340 map of initial property values for the created object. These values are applied before the object
1341 creation is finalized. This is more efficient than setting property values after object creation,
1342 particularly where large sets of property values are defined, and also allows property bindings
1343 to be set up (using \l{Qt::binding}{Qt.binding}) before the object is created.
1344
1345 The \a properties argument is specified as a map of property-value items. For example, the code
1346 below creates an object with initial \c x and \c y values of 100 and 100, respectively:
1347
1348 \js
1349 var component = Qt.createComponent("Button.qml");
1350 if (component.status == Component.Ready)
1351 component.createObject(parent, {x: 100, y: 100});
1352 \endjs
1353
1354 Dynamically created instances can be deleted with the \c destroy() method.
1355 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1356
1357 \sa incubateObject()
1358*/
1359
1360
1361void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties &requiredProperties, QObject *createdComponent)
1362{
1363 QV4::Scope scope(engine);
1364 QV4::ScopedObject object(scope);
1365 QV4::ScopedObject valueMap(scope, v);
1366 QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly);
1367 QV4::ScopedString name(scope);
1368 QV4::ScopedValue val(scope);
1369 if (engine->hasException)
1370 return;
1371
1372 QV4::ScopedStackFrame frame(scope, qmlContext->d());
1373
1374 while (1) {
1375 name = it.nextPropertyNameAsString(value: val);
1376 if (!name)
1377 break;
1378 object = o;
1379 const QStringList properties = name->toQString().split(sep: QLatin1Char('.'));
1380 bool isTopLevelProperty = properties.size() == 1;
1381 for (int i = 0; i < properties.length() - 1; ++i) {
1382 name = engine->newString(s: properties.at(i));
1383 object = object->get(name);
1384 if (engine->hasException || !object) {
1385 break;
1386 }
1387 }
1388 if (engine->hasException || !object) {
1389 engine->hasException = false;
1390 continue;
1391 }
1392 name = engine->newString(s: properties.last());
1393 object->put(name, v: val);
1394 if (engine->hasException) {
1395 engine->hasException = false;
1396 continue;
1397 } else if (isTopLevelProperty) {
1398 auto prop = removePropertyFromRequired(createdComponent, name: name->toQString(), requiredProperties);
1399 }
1400 }
1401
1402 engine->hasException = false;
1403}
1404
1405QQmlError QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty)
1406{
1407 QQmlError error;
1408 QString description = QLatin1String("Required property %1 was not initialized").arg(args: unsetRequiredProperty.propertyName);
1409 switch (unsetRequiredProperty.aliasesToRequired.size()) {
1410 case 0:
1411 break;
1412 case 1: {
1413 const auto info = unsetRequiredProperty.aliasesToRequired.first();
1414 description += QLatin1String("\nIt can be set via the alias property %1 from %2\n").arg(args: info.propertyName, args: info.fileUrl.toString());
1415 break;
1416 }
1417 default:
1418 description += QLatin1String("\nIt can be set via one of the following alias properties:");
1419 for (auto aliasInfo: unsetRequiredProperty.aliasesToRequired) {
1420 description += QLatin1String("\n- %1 (%2)").arg(args&: aliasInfo.propertyName, args: aliasInfo.fileUrl.toString());
1421 }
1422 description += QLatin1Char('\n');
1423 }
1424 error.setDescription(description);
1425 error.setUrl(unsetRequiredProperty.fileUrl);
1426 error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.line));
1427 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: unsetRequiredProperty.location.column));
1428 return error;
1429}
1430
1431/*!
1432 \internal
1433*/
1434void QQmlComponent::createObject(QQmlV4Function *args)
1435{
1436 Q_D(QQmlComponent);
1437 Q_ASSERT(d->engine);
1438 Q_ASSERT(args);
1439
1440 QObject *parent = nullptr;
1441 QV4::ExecutionEngine *v4 = args->v4engine();
1442 QV4::Scope scope(v4);
1443 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
1444
1445 if (args->length() >= 1) {
1446 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
1447 if (qobjectWrapper)
1448 parent = qobjectWrapper->object();
1449 }
1450
1451 if (args->length() >= 2) {
1452 QV4::ScopedValue v(scope, (*args)[1]);
1453 if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
1454 qmlWarning(me: this) << tr(s: "createObject: value is not an object");
1455 args->setReturnValue(QV4::Encode::null());
1456 return;
1457 }
1458 valuemap = v;
1459 }
1460
1461 QQmlContext *ctxt = creationContext();
1462 if (!ctxt) ctxt = d->engine->rootContext();
1463
1464 QObject *rv = beginCreate(publicContext: ctxt);
1465
1466 if (!rv) {
1467 args->setReturnValue(QV4::Encode::null());
1468 return;
1469 }
1470
1471 QQmlComponent_setQmlParent(me: rv, parent);
1472
1473 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4, object: rv));
1474 Q_ASSERT(object->isObject());
1475
1476 if (!valuemap->isUndefined()) {
1477 QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
1478 QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext, o: object, v: valuemap, requiredProperties&: d->requiredProperties(), createdComponent: rv);
1479 }
1480 if (!d->requiredProperties().empty()) {
1481 QList<QQmlError> errors;
1482 for (const auto &requiredProperty: d->requiredProperties()) {
1483 errors.push_back(t: QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(unsetRequiredProperty: requiredProperty));
1484 }
1485 qmlWarning(me: rv, errors);
1486 args->setReturnValue(QV4::Encode::null());
1487 delete rv;
1488 return;
1489 }
1490
1491 d->completeCreate();
1492
1493 Q_ASSERT(QQmlData::get(rv));
1494 QQmlData::get(object: rv)->explicitIndestructibleSet = false;
1495 QQmlData::get(object: rv)->indestructible = false;
1496
1497 args->setReturnValue(object->asReturnedValue());
1498}
1499
1500/*!
1501 \qmlmethod object Component::incubateObject(Item parent, object properties, enumeration mode)
1502
1503 Creates an incubator for an instance of this component. Incubators allow new component
1504 instances to be instantiated asynchronously and do not cause freezes in the UI.
1505
1506 The \a parent argument specifies the parent the created instance will have. Omitting the
1507 parameter or passing null will create an object with no parent. In this case, a reference
1508 to the created object must be held so that it is not destroyed by the garbage collector.
1509
1510 The \a properties argument is specified as a map of property-value items which will be
1511 set on the created object during its construction. \a mode may be Qt.Synchronous or
1512 Qt.Asynchronous, and controls whether the instance is created synchronously or asynchronously.
1513 The default is asynchronous. In some circumstances, even if Qt.Synchronous is specified,
1514 the incubator may create the object asynchronously. This happens if the component calling
1515 incubateObject() is itself being created asynchronously.
1516
1517 All three arguments are optional.
1518
1519 If successful, the method returns an incubator, otherwise null. The incubator has the following
1520 properties:
1521
1522 \list
1523 \li status The status of the incubator. Valid values are Component.Ready, Component.Loading and
1524 Component.Error.
1525 \li object The created object instance. Will only be available once the incubator is in the
1526 Ready status.
1527 \li onStatusChanged Specifies a callback function to be invoked when the status changes. The
1528 status is passed as a parameter to the callback.
1529 \li forceCompletion() Call to complete incubation synchronously.
1530 \endlist
1531
1532 The following example demonstrates how to use an incubator:
1533
1534 \js
1535 var component = Qt.createComponent("Button.qml");
1536
1537 var incubator = component.incubateObject(parent, { x: 10, y: 10 });
1538 if (incubator.status != Component.Ready) {
1539 incubator.onStatusChanged = function(status) {
1540 if (status == Component.Ready) {
1541 print ("Object", incubator.object, "is now ready!");
1542 }
1543 }
1544 } else {
1545 print ("Object", incubator.object, "is ready immediately!");
1546 }
1547 \endjs
1548
1549 Dynamically created instances can be deleted with the \c destroy() method.
1550 See \l {Dynamic QML Object Creation from JavaScript} for more information.
1551
1552 \sa createObject()
1553*/
1554
1555/*!
1556 \internal
1557*/
1558void QQmlComponent::incubateObject(QQmlV4Function *args)
1559{
1560 Q_D(QQmlComponent);
1561 Q_ASSERT(d->engine);
1562 Q_UNUSED(d);
1563 Q_ASSERT(args);
1564 QV4::ExecutionEngine *v4 = args->v4engine();
1565 QV4::Scope scope(v4);
1566
1567 QObject *parent = nullptr;
1568 QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
1569 QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
1570
1571 if (args->length() >= 1) {
1572 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
1573 if (qobjectWrapper)
1574 parent = qobjectWrapper->object();
1575 }
1576
1577 if (args->length() >= 2) {
1578 QV4::ScopedValue v(scope, (*args)[1]);
1579 if (v->isNull()) {
1580 } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) {
1581 qmlWarning(me: this) << tr(s: "createObject: value is not an object");
1582 args->setReturnValue(QV4::Encode::null());
1583 return;
1584 } else {
1585 valuemap = v;
1586 }
1587 }
1588
1589 if (args->length() >= 3) {
1590 QV4::ScopedValue val(scope, (*args)[2]);
1591 quint32 v = val->toUInt32();
1592 if (v == 0)
1593 mode = QQmlIncubator::Asynchronous;
1594 else if (v == 1)
1595 mode = QQmlIncubator::AsynchronousIfNested;
1596 }
1597
1598 QQmlComponentExtension *e = componentExtension(engine: args->v4engine());
1599
1600 QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(args: mode));
1601 QV4::ScopedObject p(scope, e->incubationProto.value());
1602 r->setPrototypeOf(p);
1603
1604 if (!valuemap->isUndefined())
1605 r->d()->valuemap.set(e: scope.engine, newVal: valuemap);
1606 r->d()->qmlContext.set(e: scope.engine, newVal: v4->qmlContext());
1607 r->d()->parent = parent;
1608
1609 QQmlIncubator *incubator = r->d()->incubator;
1610 create(incubator&: *incubator, context: creationContext());
1611
1612 if (incubator->status() == QQmlIncubator::Null) {
1613 args->setReturnValue(QV4::Encode::null());
1614 } else {
1615 args->setReturnValue(r.asReturnedValue());
1616 }
1617}
1618
1619// XXX used by QSGLoader
1620void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties &requiredProperties)
1621{
1622 QV4::ExecutionEngine *v4engine = engine->handle();
1623 QV4::Scope scope(v4engine);
1624
1625 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine: v4engine, object: toCreate));
1626 Q_ASSERT(object->as<QV4::Object>());
1627
1628 if (!valuemap.isUndefined())
1629 setInitialProperties(engine: v4engine, qmlContext, o: object, v: valuemap, requiredProperties, createdComponent: toCreate);
1630}
1631
1632QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
1633{
1634 QV4::Scope scope(v4);
1635 QV4::ScopedObject proto(scope, v4->newObject());
1636 proto->defineAccessorProperty(QStringLiteral("onStatusChanged"),
1637 getter: QV4::QmlIncubatorObject::method_get_statusChanged, setter: QV4::QmlIncubatorObject::method_set_statusChanged);
1638 proto->defineAccessorProperty(QStringLiteral("status"), getter: QV4::QmlIncubatorObject::method_get_status, setter: nullptr);
1639 proto->defineAccessorProperty(QStringLiteral("object"), getter: QV4::QmlIncubatorObject::method_get_object, setter: nullptr);
1640 proto->defineDefaultProperty(QStringLiteral("forceCompletion"), code: QV4::QmlIncubatorObject::method_forceCompletion);
1641
1642 incubationProto.set(engine: v4, value: proto);
1643}
1644
1645QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const FunctionObject *b, const Value *thisObject, const Value *, int)
1646{
1647 QV4::Scope scope(b);
1648 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1649 if (!o)
1650 THROW_TYPE_ERROR();
1651
1652 return QV4::QObjectWrapper::wrap(engine: scope.engine, object: o->d()->incubator->object());
1653}
1654
1655QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const FunctionObject *b, const Value *thisObject, const Value *, int)
1656{
1657 QV4::Scope scope(b);
1658 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1659 if (!o)
1660 THROW_TYPE_ERROR();
1661
1662 o->d()->incubator->forceCompletion();
1663
1664 RETURN_UNDEFINED();
1665}
1666
1667QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int)
1668{
1669 QV4::Scope scope(b);
1670 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1671 if (!o)
1672 THROW_TYPE_ERROR();
1673
1674 return QV4::Encode(o->d()->incubator->status());
1675}
1676
1677QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *, int)
1678{
1679 QV4::Scope scope(b);
1680 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1681 if (!o)
1682 THROW_TYPE_ERROR();
1683
1684 return QV4::Encode(o->d()->statusChanged);
1685}
1686
1687QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1688{
1689 QV4::Scope scope(b);
1690 QV4::Scoped<QmlIncubatorObject> o(scope, thisObject->as<QmlIncubatorObject>());
1691 if (!o || argc < 1)
1692 THROW_TYPE_ERROR();
1693
1694 o->d()->statusChanged.set(e: scope.engine, newVal: argv[0]);
1695
1696 RETURN_UNDEFINED();
1697}
1698
1699QQmlComponentExtension::~QQmlComponentExtension()
1700{
1701}
1702
1703void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
1704{
1705 Object::init();
1706 valuemap.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue());
1707 statusChanged.set(e: internalClass->engine, newVal: QV4::Value::undefinedValue());
1708 parent.init();
1709 qmlContext.set(e: internalClass->engine, newVal: nullptr);
1710 incubator = new QQmlComponentIncubator(this, m);
1711}
1712
1713void QV4::Heap::QmlIncubatorObject::destroy() {
1714 delete incubator;
1715 parent.destroy();
1716 Object::destroy();
1717}
1718
1719void QV4::QmlIncubatorObject::setInitialState(QObject *o, RequiredProperties &requiredProperties)
1720{
1721 QQmlComponent_setQmlParent(me: o, parent: d()->parent);
1722
1723 if (!d()->valuemap.isUndefined()) {
1724 QV4::ExecutionEngine *v4 = engine();
1725 QV4::Scope scope(v4);
1726 QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(engine: v4, object: o));
1727 QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
1728 QQmlComponentPrivate::setInitialProperties(engine: v4, qmlContext: qmlCtxt, o: obj, v: d()->valuemap, requiredProperties, createdComponent: o);
1729 }
1730}
1731
1732void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
1733{
1734 QV4::Scope scope(engine());
1735 // hold the incubated object in a scoped value to prevent it's destruction before this method returns
1736 QV4::ScopedObject incubatedObject(scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object: d()->incubator->object()));
1737
1738 if (s == QQmlIncubator::Ready) {
1739 Q_ASSERT(QQmlData::get(d()->incubator->object()));
1740 QQmlData::get(object: d()->incubator->object())->explicitIndestructibleSet = false;
1741 QQmlData::get(object: d()->incubator->object())->indestructible = false;
1742 }
1743
1744 QV4::ScopedFunctionObject f(scope, d()->statusChanged);
1745 if (f) {
1746 QV4::JSCallData jsCallData(scope, 1);
1747 *jsCallData->thisObject = this;
1748 jsCallData->args[0] = QV4::Value::fromUInt32(i: s);
1749 f->call(data: jsCallData);
1750 if (scope.hasException()) {
1751 QQmlError error = scope.engine->catchExceptionAsQmlError();
1752 QQmlEnginePrivate::warning(QQmlEnginePrivate::get(e: scope.engine->qmlEngine()), error);
1753 }
1754 }
1755
1756 if (s != QQmlIncubator::Loading)
1757 d()->incubator->incubatorObject.clear();
1758}
1759
1760#undef INITIALPROPERTIES_SOURCE
1761
1762QT_END_NAMESPACE
1763
1764#include "moc_qqmlcomponent.cpp"
1765

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