Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmlcomponentandaliasresolver_p.h
Go to the documentation of this file.
1// Copyright (C) 2023 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#ifndef QQMLCOMPONENTANDALIASRESOLVER_P_H
4#define QQMLCOMPONENTANDALIASRESOLVER_P_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <QtQml/qqmlcomponent.h>
18#include <QtQml/qqmlerror.h>
19
20#include <QtCore/qglobal.h>
21#include <QtCore/qhash.h>
22
23#include <private/qqmltypeloader_p.h>
24#include <private/qqmlpropertycachecreator_p.h>
25
27
28Q_DECLARE_LOGGING_CATEGORY(lcQmlTypeCompiler);
29
30template<typename ObjectContainer>
32{
34public:
35 using CompiledObject = typename ObjectContainer::CompiledObject;
36 using CompiledBinding = typename ObjectContainer::CompiledBinding;
37
39 ObjectContainer *compiler,
40 QQmlEnginePrivate *enginePrivate,
41 QQmlPropertyCacheVector *propertyCaches);
42
43 [[nodiscard]] QQmlError resolve(int root = 0);
44
45private:
46 enum AliasResolutionResult {
47 NoAliasResolved,
48 SomeAliasesResolved,
49 AllAliasesResolved
50 };
51
52 // To be specialized for each container
53 void allocateNamedObjects(CompiledObject *object) const;
54 void setObjectId(int index) const;
55 [[nodiscard]] bool markAsComponent(int index) const;
56 [[nodiscard]] AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlError *error);
57 [[nodiscard]] bool wrapImplicitComponent(CompiledBinding *binding);
58
59 [[nodiscard]] QQmlError findAndRegisterImplicitComponents(
60 const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
61 [[nodiscard]] QQmlError collectIdsAndAliases(int objectIndex);
62 [[nodiscard]] QQmlError resolveAliases(int componentIndex);
63
64 QString stringAt(int idx) const { return m_compiler->stringAt(idx); }
65 QV4::ResolvedTypeReference *resolvedType(int id) const { return m_compiler->resolvedType(id); }
66
67 [[nodiscard]] QQmlError error(
69 const QString &description)
70 {
74 error.setDescription(description);
75 error.setUrl(m_compiler->url());
76 return error;
77 }
78
79 template<typename Token>
80 [[nodiscard]] QQmlError error(Token token, const QString &description)
81 {
82 return error(token->location, description);
83 }
84
85 static bool isUsableComponent(const QMetaObject *metaObject)
86 {
87 // The metaObject is a component we're interested in if it either is a QQmlComponent itself
88 // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
89 // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
90
91 if (metaObject == &QQmlComponent::staticMetaObject)
92 return true;
93
94 for (; metaObject; metaObject = metaObject->superClass()) {
95 if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
96 return true;
97 }
98
99 return false;
100 }
101
102 ObjectContainer *m_compiler = nullptr;
103 QQmlEnginePrivate *m_enginePrivate = nullptr;
104
105 // Implicit component insertion may have added objects and thus we also need
106 // to extend the symmetric propertyCaches. Therefore, non-const propertyCaches.
107 QQmlPropertyCacheVector *m_propertyCaches = nullptr;
108
109 // indices of the objects that are actually Component {}
110 QVector<quint32> m_componentRoots;
111 QVector<int> m_objectsWithAliases;
112 typename ObjectContainer::IdToObjectMap m_idToObjectIndex;
113};
114
115template<typename ObjectContainer>
117 ObjectContainer *compiler,
118 QQmlEnginePrivate *enginePrivate,
119 QQmlPropertyCacheVector *propertyCaches)
120 : m_compiler(compiler)
121 , m_enginePrivate(enginePrivate)
122 , m_propertyCaches(propertyCaches)
123{
124}
125
126template<typename ObjectContainer>
128 const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
129{
130 QQmlPropertyResolver propertyResolver(propertyCache);
131
132 const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1
133 ? propertyCache->parent()->defaultProperty()
134 : propertyCache->defaultProperty();
135
136 for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
137 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
138 continue;
140 continue;
141
142 auto targetObject = m_compiler->objectAt(binding->value.objectIndex);
143 auto typeReference = resolvedType(targetObject->inheritedTypeNameIndex);
144 Q_ASSERT(typeReference);
145
146 const QMetaObject *firstMetaObject = nullptr;
147 const auto type = typeReference->type();
148 if (type.isValid())
149 firstMetaObject = type.metaObject();
150 else if (const auto compilationUnit = typeReference->compilationUnit())
151 firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
152 if (isUsableComponent(firstMetaObject))
153 continue;
154
155 // if here, not a QQmlComponent, so needs wrapping
156 const QQmlPropertyData *pd = nullptr;
157 if (binding->propertyNameIndex != quint32(0)) {
158 bool notInRevision = false;
159 pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
160 } else {
161 pd = defaultProperty;
162 }
163 if (!pd || !pd->isQObject())
164 continue;
165
166 // If the version is given, use it and look up by QQmlType.
167 // Otherwise, make sure we look up by metaobject.
168 // TODO: Is this correct?
171 : QQmlMetaType::rawPropertyCacheForType(pd->propType());
172 const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
173 while (mo) {
174 if (mo == &QQmlComponent::staticMetaObject)
175 break;
176 mo = mo->superClass();
177 }
178
179 if (!mo)
180 continue;
181
182 if (!wrapImplicitComponent(binding))
183 return error(binding, tr("Cannot wrap implicit component"));
184 }
185
186 return QQmlError();
187}
188
189// Resolve ignores everything relating to inline components, except for implicit components.
190template<typename ObjectContainer>
192{
193 // Detect real Component {} objects as well as implicitly defined components, such as
194 // someItemDelegate: Item {}
195 // In the implicit case Item is surrounded by a synthetic Component {} because the property
196 // on the left hand side is of QQmlComponent type.
197 const int objCountWithoutSynthesizedComponents = m_compiler->objectCount();
198
199 // root+1, as ic root is handled at the end
200 const int startObjectIndex = root == 0 ? root : root+1;
201
202 for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
203 auto obj = m_compiler->objectAt(i);
204 const bool isInlineComponentRoot
206 const bool isPartOfInlineComponent
208 QQmlPropertyCache::ConstPtr cache = m_propertyCaches->at(i);
209
210 bool isExplicitComponent = false;
211 if (obj->inheritedTypeNameIndex) {
212 auto *tref = resolvedType(obj->inheritedTypeNameIndex);
213 Q_ASSERT(tref);
214 if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
215 isExplicitComponent = true;
216 }
217
218 if (isInlineComponentRoot && isExplicitComponent) {
219 qCWarning(lcQmlTypeCompiler).nospace().noquote()
220 << m_compiler->url().toString() << ":" << obj->location.line() << ":"
221 << obj->location.column()
222 << ": Using a Component as the root of an inline component is deprecated: "
223 "inline components are "
224 "automatically wrapped into Components when needed.";
225 }
226
227 if (root == 0) {
228 // normal component root, skip over anything inline component related
229 if (isInlineComponentRoot || isPartOfInlineComponent)
230 continue;
231 } else if (!isPartOfInlineComponent || isInlineComponentRoot) {
232 // We've left the current inline component (potentially entered a new one),
233 // but we still need to resolve implicit components which are part of inline components.
234 if (cache && !isExplicitComponent) {
235 const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
236 if (error.isValid())
237 return error;
238 }
239 break;
240 }
241
242 if (obj->inheritedTypeNameIndex == 0 && !cache)
243 continue;
244
245 if (!isExplicitComponent) {
246 if (cache) {
247 const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
248 if (error.isValid())
249 return error;
250 }
251 continue;
252 }
253
254 if (!markAsComponent(i))
255 return error(obj, tr("Cannot mark object as component"));
256
257 // check if this object is the root
258 if (i == 0) {
259 if (isExplicitComponent)
260 qCWarning(lcQmlTypeCompiler).nospace().noquote()
261 << m_compiler->url().toString() << ":" << obj->location.line() << ":"
262 << obj->location.column()
263 << ": Using a Component as the root of a QML document is deprecated: types "
264 "defined in qml documents are "
265 "automatically wrapped into Components when needed.";
266 }
267
268 if (obj->functionCount() > 0)
269 return error(obj, tr("Component objects cannot declare new functions."));
270 if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
271 return error(obj, tr("Component objects cannot declare new properties."));
272 if (obj->signalCount() > 0)
273 return error(obj, tr("Component objects cannot declare new signals."));
274
275 if (obj->bindingCount() == 0)
276 return error(obj, tr("Cannot create empty component specification"));
277
278 const auto rootBinding = obj->bindingsBegin();
279 const auto bindingsEnd = obj->bindingsEnd();
280
281 // Produce the more specific "no properties" error rather than the "invalid body" error
282 // where possible.
283 for (auto b = rootBinding; b != bindingsEnd; ++b) {
284 if (b->propertyNameIndex == 0)
285 continue;
286
287 return error(b, tr("Component elements may not contain properties other than id"));
288 }
289
290 if (auto b = rootBinding;
291 b->type() != QV4::CompiledData::Binding::Type_Object || ++b != bindingsEnd) {
292 return error(obj, tr("Invalid component body specification"));
293 }
294
295 // For the root object, we are going to collect ids/aliases and resolve them for as a
296 // separate last pass.
297 if (i != 0)
298 m_componentRoots.append(i);
299 }
300
301 for (int i = 0; i < m_componentRoots.size(); ++i) {
302 CompiledObject *component = m_compiler->objectAt(m_componentRoots.at(i));
303 const auto rootBinding = component->bindingsBegin();
304
305 m_idToObjectIndex.clear();
306 m_objectsWithAliases.clear();
307
308 if (const QQmlError error = collectIdsAndAliases(rootBinding->value.objectIndex);
309 error.isValid()) {
310 return error;
311 }
312
313 allocateNamedObjects(component);
314
315 if (const QQmlError error = resolveAliases(m_componentRoots.at(i)); error.isValid())
316 return error;
317 }
318
319 // Collect ids and aliases for root
320 m_idToObjectIndex.clear();
321 m_objectsWithAliases.clear();
322
323 if (const QQmlError error = collectIdsAndAliases(root); error.isValid())
324 return error;
325
326 allocateNamedObjects(m_compiler->objectAt(root));
327 return resolveAliases(root);
328}
329
330template<typename ObjectContainer>
332{
333 auto obj = m_compiler->objectAt(objectIndex);
334
335 if (obj->idNameIndex != 0) {
336 if (m_idToObjectIndex.contains(obj->idNameIndex))
337 return error(obj->locationOfIdProperty, tr("id is not unique"));
338 setObjectId(objectIndex);
339 m_idToObjectIndex.insert(obj->idNameIndex, objectIndex);
340 }
341
342 if (obj->aliasCount() > 0)
343 m_objectsWithAliases.append(objectIndex);
344
345 // Stop at Component boundary
346 if (obj->hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
347 return QQmlError();
348
349 for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd();
350 binding != end; ++binding) {
351 switch (binding->type()) {
355 if (const QQmlError error = collectIdsAndAliases(binding->value.objectIndex);
356 error.isValid()) {
357 return error;
358 }
359 break;
360 default:
361 break;
362 }
363 }
364
365 return QQmlError();
366}
367
368template<typename ObjectContainer>
370{
371 if (m_objectsWithAliases.isEmpty())
372 return QQmlError();
373
374 QQmlPropertyCacheAliasCreator<ObjectContainer> aliasCacheCreator(m_propertyCaches, m_compiler);
375
376 bool atLeastOneAliasResolved;
377 do {
378 atLeastOneAliasResolved = false;
379 QVector<int> pendingObjects;
380
381 for (int objectIndex: std::as_const(m_objectsWithAliases)) {
382
384 const auto result = resolveAliasesInObject(objectIndex, &error);
385 if (error.isValid())
386 return error;
387
388 if (result == AllAliasesResolved) {
389 QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(
390 *m_compiler->objectAt(componentIndex), objectIndex, m_enginePrivate);
391 if (error.isValid())
392 return error;
393 atLeastOneAliasResolved = true;
394 } else if (result == SomeAliasesResolved) {
395 atLeastOneAliasResolved = true;
396 pendingObjects.append(objectIndex);
397 } else {
398 pendingObjects.append(objectIndex);
399 }
400 }
401 qSwap(m_objectsWithAliases, pendingObjects);
402 } while (!m_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
403
404 if (!atLeastOneAliasResolved && !m_objectsWithAliases.isEmpty()) {
405 const CompiledObject *obj = m_compiler->objectAt(m_objectsWithAliases.first());
406 for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
407 if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved))
408 return error(alias->location, tr("Circular alias reference detected"));
409 }
410 }
411
412 return QQmlError();
413}
414
416
417#endif // QQMLCOMPONENTANDALIASRESOLVER_P_H
Definition qlist.h:74
void append(parameter_type t)
Definition qlist.h:441
QQmlComponentAndAliasResolver(ObjectContainer *compiler, QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches)
typename ObjectContainer::CompiledObject CompiledObject
typename ObjectContainer::CompiledBinding CompiledBinding
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:18
static QQmlPropertyCache::ConstPtr rawPropertyCacheForType(QMetaType metaType)
QTypeRevision typeVersion() const
QMetaType propType() const
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
constexpr bool hasMinorVersion() const
Returns true if the minor version is known, otherwise false.
qSwap(pi, e)
QCache< int, Employee > cache
[0]
auto mo
[7]
Token token
Definition keywords.cpp:444
Combined button and popup list for selecting options.
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
#define Q_DECLARE_TR_FUNCTIONS(context)
DBusConnection const char DBusError * error
#define qCWarning(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLint location
GLboolean GLboolean GLboolean b
GLuint index
[2]
GLuint GLuint end
GLenum type
GLhandleARB obj
[2]
GLuint64EXT * result
[6]
static qreal component(const QPointF &point, unsigned int i)
int qmlConvertSourceCoordinate< quint32, int >(quint32 n)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define tr(X)
unsigned int quint32
Definition qtypes.h:45
obj metaObject() -> className()
\inmodule QtCore
Token
Definition token.h:194