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