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 | |
33 | QT_BEGIN_NAMESPACE |
34 | |
35 | class QQmlComponent; |
36 | class QQmlEngine; |
37 | |
38 | class QQmlComponentAttached; |
39 | class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback |
40 | { |
41 | Q_DECLARE_PUBLIC(QQmlComponent) |
42 | |
43 | public: |
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 | |
176 | QQmlComponentPrivate::ConstructionState::~ConstructionState() |
177 | { |
178 | if (m_creatorOrRequiredProperties.isT1()) |
179 | delete m_creatorOrRequiredProperties.asT1(); |
180 | else |
181 | delete m_creatorOrRequiredProperties.asT2(); |
182 | } |
183 | |
184 | QQmlComponentPrivate::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 | */ |
194 | inline 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 | |
203 | inline 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 | |
210 | inline bool QQmlComponentPrivate::ConstructionState::hasUnsetRequiredProperties() const { |
211 | auto properties = const_cast<ConstructionState *>(this)->requiredProperties(); |
212 | return properties && !properties->isEmpty(); |
213 | } |
214 | |
215 | inline void QQmlComponentPrivate::ConstructionState::clearRequiredProperties() |
216 | { |
217 | if (auto reqProps = requiredProperties()) |
218 | reqProps->clear(); |
219 | } |
220 | |
221 | inline 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 |
228 | inline 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 | |
242 | inline QQmlObjectCreator *QQmlComponentPrivate::ConstructionState::creator() |
243 | { |
244 | if (m_creatorOrRequiredProperties.isT1()) |
245 | return m_creatorOrRequiredProperties.asT1(); |
246 | return nullptr; |
247 | } |
248 | |
249 | inline const QQmlObjectCreator *QQmlComponentPrivate::ConstructionState::creator() const |
250 | { |
251 | if (m_creatorOrRequiredProperties.isT1()) |
252 | return m_creatorOrRequiredProperties.asT1(); |
253 | return nullptr; |
254 | } |
255 | |
256 | inline bool QQmlComponentPrivate::ConstructionState::hasCreator() const |
257 | { |
258 | return creator() != nullptr; |
259 | } |
260 | |
261 | inline 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 | |
269 | inline 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 | |
281 | inline bool QQmlComponentPrivate::ConstructionState::isCompletePending() const |
282 | { |
283 | return m_creatorOrRequiredProperties.flag(); |
284 | } |
285 | |
286 | inline 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 | */ |
297 | inline 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 | |
304 | QT_END_NAMESPACE |
305 | |
306 | #endif // QQMLCOMPONENT_P_H |
307 | |