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