1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQMLCOMPONENT_P_H
5#define QQMLCOMPONENT_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qqmlcomponent.h"
19
20#include "qqmlengine_p.h"
21#include "qqmlerror.h"
22#include <private/qqmlobjectcreator_p.h>
23#include <private/qqmltypedata_p.h>
24#include <private/qqmlguardedcontextdata_p.h>
25
26#include <QtCore/QString>
27#include <QtCore/QStringList>
28#include <QtCore/QList>
29#include <QtCore/qtclasshelpermacros.h>
30
31#include <private/qobject_p.h>
32
33QT_BEGIN_NAMESPACE
34
35class QQmlComponent;
36class QQmlEngine;
37
38class QQmlComponentAttached;
39class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback
40{
41 Q_DECLARE_PUBLIC(QQmlComponent)
42
43public:
44 QQmlComponentPrivate()
45 : progress(0.), start(-1), engine(nullptr) {}
46
47 void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);
48
49 QObject *beginCreate(QQmlRefPointer<QQmlContextData>);
50 void completeCreate();
51 void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties *requiredProperties);
52 static void setInitialProperties(
53 QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o,
54 const QV4::Value &v, RequiredProperties *requiredProperties, QObject *createdComponent,
55 QQmlObjectCreator *creator);
56 static QQmlError unsetRequiredPropertyToQQmlError(const RequiredPropertyInfo &unsetRequiredProperty);
57
58 virtual void incubateObject(
59 QQmlIncubator *incubationTask,
60 QQmlComponent *component,
61 QQmlEngine *engine,
62 const QQmlRefPointer<QQmlContextData> &context,
63 const QQmlRefPointer<QQmlContextData> &forContext);
64
65 QQmlRefPointer<QQmlTypeData> typeData;
66 void typeDataReady(QQmlTypeData *) override;
67 void typeDataProgress(QQmlTypeData *, qreal) override;
68
69 void fromTypeData(const QQmlRefPointer<QQmlTypeData> &data);
70
71 QUrl url;
72 qreal progress;
73 std::unique_ptr<QString> inlineComponentName;
74
75 /* points to the sub-object in a QML file that should be instantiated
76 used create instances of QtQml's Component type and indirectly for inline components */
77 int start;
78
79 bool hadTopLevelRequiredProperties() const;
80 // TODO: merge compilation unit and type
81 QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
82 QQmlType loadedType;
83
84 struct AnnotatedQmlError
85 {
86 AnnotatedQmlError() = default;
87
88 AnnotatedQmlError(QQmlError error)
89 : error(std::move(error))
90 {
91 }
92
93
94 AnnotatedQmlError(QQmlError error, bool transient)
95 : error(std::move(error)), isTransient(transient)
96 {
97 }
98 QQmlError error;
99 bool isTransient = false; // tells if the error is temporary (e.g. unset required property)
100 };
101
102 struct ConstructionState {
103 ConstructionState() = default;
104 inline ~ConstructionState();
105 Q_DISABLE_COPY(ConstructionState)
106 inline ConstructionState(ConstructionState &&other) noexcept;
107
108 void swap(ConstructionState &other)
109 {
110 m_creatorOrRequiredProperties.swap(other&: other.m_creatorOrRequiredProperties);
111 }
112
113 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QQmlComponentPrivate::ConstructionState);
114
115 inline void ensureRequiredPropertyStorage();
116 inline RequiredProperties *requiredProperties();
117 inline void addPendingRequiredProperty(
118 const QObject *object, const QQmlPropertyData *propData,
119 const RequiredPropertyInfo &info);
120 inline bool hasUnsetRequiredProperties() const;
121 inline void clearRequiredProperties();
122
123 inline void appendErrors(const QList<QQmlError> &qmlErrors);
124 inline void appendCreatorErrors();
125
126 inline QQmlObjectCreator *creator();
127 inline const QQmlObjectCreator *creator() const;
128 inline void clear();
129 inline bool hasCreator() const;
130 inline QQmlObjectCreator *initCreator(QQmlRefPointer<QQmlContextData> parentContext,
131 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
132 const QQmlRefPointer<QQmlContextData> &creationContext);
133
134 QList<AnnotatedQmlError> errors;
135 inline bool isCompletePending() const;
136 inline void setCompletePending(bool isPending);
137
138 private:
139 QBiPointer<QQmlObjectCreator, RequiredProperties> m_creatorOrRequiredProperties;
140 };
141 ConstructionState state;
142
143 using DeferredState = std::vector<ConstructionState>;
144 static void beginDeferred(QQmlEnginePrivate *enginePriv, QObject *object, DeferredState* deferredState);
145 static void completeDeferred(QQmlEnginePrivate *enginePriv, DeferredState *deferredState);
146
147 static void complete(QQmlEnginePrivate *enginePriv, ConstructionState *state);
148 static QQmlProperty removePropertyFromRequired(QObject *createdComponent, const QString &name, RequiredProperties *requiredProperties,
149 QQmlEngine *engine, bool *wasInRequiredProperties = nullptr);
150
151 QQmlEngine *engine;
152 QQmlGuardedContextData creationContext;
153
154 void clear();
155
156 static QQmlComponentPrivate *get(QQmlComponent *c) {
157 return static_cast<QQmlComponentPrivate *>(QObjectPrivate::get(o: c));
158 }
159
160 QObject *doBeginCreate(QQmlComponent *q, QQmlContext *context);
161 bool setInitialProperty(QObject *component, const QString &name, const QVariant& value);
162
163 enum CreateBehavior {
164 CreateDefault,
165 CreateWarnAboutRequiredProperties,
166 };
167 QObject *createWithProperties(QObject *parent, const QVariantMap &properties,
168 QQmlContext *context, CreateBehavior behavior = CreateDefault);
169
170 bool isBound() const {
171 return compilationUnit
172 && (compilationUnit->unitData()->flags & QV4::CompiledData::Unit::ComponentsBound);
173 }
174};
175
176QQmlComponentPrivate::ConstructionState::~ConstructionState()
177{
178 if (m_creatorOrRequiredProperties.isT1())
179 delete m_creatorOrRequiredProperties.asT1();
180 else
181 delete m_creatorOrRequiredProperties.asT2();
182}
183
184QQmlComponentPrivate::ConstructionState::ConstructionState(ConstructionState &&other) noexcept
185{
186 errors = std::move(other.errors);
187 m_creatorOrRequiredProperties = std::exchange(obj&: other.m_creatorOrRequiredProperties, new_val: {});
188}
189
190/*!
191 \internal A list of pending required properties that need
192 to be set in order for object construction to be successful.
193 */
194inline RequiredProperties *QQmlComponentPrivate::ConstructionState::requiredProperties() {
195 if (m_creatorOrRequiredProperties.isNull())
196 return nullptr;
197 else if (m_creatorOrRequiredProperties.isT1())
198 return m_creatorOrRequiredProperties.asT1()->requiredProperties();
199 else
200 return m_creatorOrRequiredProperties.asT2();
201}
202
203inline void QQmlComponentPrivate::ConstructionState::addPendingRequiredProperty(
204 const QObject *object, const QQmlPropertyData *propData, const RequiredPropertyInfo &info)
205{
206 Q_ASSERT(requiredProperties());
207 requiredProperties()->insert(key: {object, propData}, value: info);
208}
209
210inline bool QQmlComponentPrivate::ConstructionState::hasUnsetRequiredProperties() const {
211 auto properties = const_cast<ConstructionState *>(this)->requiredProperties();
212 return properties && !properties->isEmpty();
213}
214
215inline void QQmlComponentPrivate::ConstructionState::clearRequiredProperties()
216{
217 if (auto reqProps = requiredProperties())
218 reqProps->clear();
219}
220
221inline void QQmlComponentPrivate::ConstructionState::appendErrors(const QList<QQmlError> &qmlErrors)
222{
223 for (const QQmlError &e : qmlErrors)
224 errors.emplaceBack(args: e);
225}
226
227//! \internal Moves errors from creator into construction state itself
228inline void QQmlComponentPrivate::ConstructionState::appendCreatorErrors()
229{
230 if (!hasCreator())
231 return;
232 auto creatorErrorCount = creator()->errors.size();
233 if (creatorErrorCount == 0)
234 return;
235 auto existingErrorCount = errors.size();
236 errors.resize(size: existingErrorCount + creatorErrorCount);
237 for (qsizetype i = 0; i < creatorErrorCount; ++i)
238 errors[existingErrorCount + i] = AnnotatedQmlError { std::move(creator()->errors[i]) };
239 creator()->errors.clear();
240}
241
242inline QQmlObjectCreator *QQmlComponentPrivate::ConstructionState::creator()
243{
244 if (m_creatorOrRequiredProperties.isT1())
245 return m_creatorOrRequiredProperties.asT1();
246 return nullptr;
247}
248
249inline const QQmlObjectCreator *QQmlComponentPrivate::ConstructionState::creator() const
250{
251 if (m_creatorOrRequiredProperties.isT1())
252 return m_creatorOrRequiredProperties.asT1();
253 return nullptr;
254}
255
256inline bool QQmlComponentPrivate::ConstructionState::hasCreator() const
257{
258 return creator() != nullptr;
259}
260
261inline void QQmlComponentPrivate::ConstructionState::clear()
262{
263 if (m_creatorOrRequiredProperties.isT1()) {
264 delete m_creatorOrRequiredProperties.asT1();
265 m_creatorOrRequiredProperties = static_cast<QQmlObjectCreator *>(nullptr);
266 }
267}
268
269inline QQmlObjectCreator *QQmlComponentPrivate::ConstructionState::initCreator(QQmlRefPointer<QQmlContextData> parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QQmlRefPointer<QQmlContextData> &creationContext)
270{
271 if (m_creatorOrRequiredProperties.isT1())
272 delete m_creatorOrRequiredProperties.asT1();
273 else
274 delete m_creatorOrRequiredProperties.asT2();
275 m_creatorOrRequiredProperties = new QQmlObjectCreator(
276 std::move(parentContext), compilationUnit,
277 creationContext);
278 return m_creatorOrRequiredProperties.asT1();
279}
280
281inline bool QQmlComponentPrivate::ConstructionState::isCompletePending() const
282{
283 return m_creatorOrRequiredProperties.flag();
284}
285
286inline void QQmlComponentPrivate::ConstructionState::setCompletePending(bool isPending)
287{
288 m_creatorOrRequiredProperties.setFlagValue(isPending);
289}
290
291/*!
292 \internal
293 This is meant to be used in the context of QQmlComponent::loadFromModule,
294 when dealing with a C++ type. In that case, we do not have a creator,
295 and need a separate storage for required properties.
296 */
297inline void QQmlComponentPrivate::ConstructionState::ensureRequiredPropertyStorage()
298{
299 Q_ASSERT(m_creatorOrRequiredProperties.isT2() || m_creatorOrRequiredProperties.isNull());
300 if (m_creatorOrRequiredProperties.isNull())
301 m_creatorOrRequiredProperties = new RequiredProperties;
302}
303
304QT_END_NAMESPACE
305
306#endif // QQMLCOMPONENT_P_H
307

source code of qtdeclarative/src/qml/qml/qqmlcomponent_p.h