Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
30// This class primarily resolves component boundaries in a document.
31// With the information about boundaries, it then goes on to resolve aliases and generalized
32// group properties. Both rely on IDs as first part of their expressions and the IDs have
33// to be located in surrounding components. That's why we have to do this with the component
34// boundaries in mind.
35
36template<typename ObjectContainer>
38{
40public:
41 using CompiledObject = typename ObjectContainer::CompiledObject;
42 using CompiledBinding = typename ObjectContainer::CompiledBinding;
43
45 ObjectContainer *compiler,
46 QQmlEnginePrivate *enginePrivate,
47 QQmlPropertyCacheVector *propertyCaches);
48
49 [[nodiscard]] QQmlError resolve(int root = 0);
50
51private:
52 enum AliasResolutionResult {
53 NoAliasResolved,
54 SomeAliasesResolved,
55 AllAliasesResolved
56 };
57
58 // To be specialized for each container
59 void allocateNamedObjects(CompiledObject *object) const;
60 void setObjectId(int index) const;
61 [[nodiscard]] bool markAsComponent(int index) const;
62 [[nodiscard]] AliasResolutionResult resolveAliasesInObject(
63 const CompiledObject &component, int objectIndex, QQmlError *error);
64 void resolveGeneralizedGroupProperty(const CompiledObject &component, CompiledBinding *binding);
65 [[nodiscard]] bool wrapImplicitComponent(CompiledBinding *binding);
66
67 [[nodiscard]] QQmlError findAndRegisterImplicitComponents(
68 const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache);
69 [[nodiscard]] QQmlError collectIdsAndAliases(int objectIndex);
70 [[nodiscard]] QQmlError resolveAliases(int componentIndex);
71 void resolveGeneralizedGroupProperties(int componentIndex);
72 [[nodiscard]] QQmlError resolveComponentsInInlineComponentRoot(int root);
73
74 QString stringAt(int idx) const { return m_compiler->stringAt(idx); }
75 QV4::ResolvedTypeReference *resolvedType(int id) const { return m_compiler->resolvedType(id); }
76
77 [[nodiscard]] QQmlError error(
79 const QString &description)
80 {
82 error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
83 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
84 error.setDescription(description);
85 error.setUrl(m_compiler->url());
86 return error;
87 }
88
89 template<typename Token>
90 [[nodiscard]] QQmlError error(Token token, const QString &description)
91 {
92 return error(token->location, description);
93 }
94
95 static bool isUsableComponent(const QMetaObject *metaObject)
96 {
97 // The metaObject is a component we're interested in if it either is a QQmlComponent itself
98 // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
99 // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
100
101 if (metaObject == &QQmlComponent::staticMetaObject)
102 return true;
103
104 for (; metaObject; metaObject = metaObject->superClass()) {
105 if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
106 return true;
107 }
108
109 return false;
110 }
111
112 ObjectContainer *m_compiler = nullptr;
113 QQmlEnginePrivate *m_enginePrivate = nullptr;
114
115 // Implicit component insertion may have added objects and thus we also need
116 // to extend the symmetric propertyCaches. Therefore, non-const propertyCaches.
117 QQmlPropertyCacheVector *m_propertyCaches = nullptr;
118
119 // indices of the objects that are actually Component {}
120 QVector<quint32> m_componentRoots;
121 QVector<int> m_objectsWithAliases;
122 QVector<CompiledBinding *> m_generalizedGroupProperties;
123 typename ObjectContainer::IdToObjectMap m_idToObjectIndex;
124};
125
126template<typename ObjectContainer>
128 ObjectContainer *compiler,
129 QQmlEnginePrivate *enginePrivate,
130 QQmlPropertyCacheVector *propertyCaches)
131 : m_compiler(compiler)
132 , m_enginePrivate(enginePrivate)
133 , m_propertyCaches(propertyCaches)
134{
135}
136
137template<typename ObjectContainer>
139 const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &propertyCache)
140{
141 QQmlPropertyResolver propertyResolver(propertyCache);
142
143 const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1
144 ? propertyCache->parent()->defaultProperty()
145 : propertyCache->defaultProperty();
146
147 for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
148 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
149 continue;
151 continue;
152
153 auto targetObject = m_compiler->objectAt(binding->value.objectIndex);
154 auto typeReference = resolvedType(targetObject->inheritedTypeNameIndex);
155 Q_ASSERT(typeReference);
156
157 const QMetaObject *firstMetaObject = nullptr;
158 const auto type = typeReference->type();
159 if (type.isValid())
160 firstMetaObject = type.metaObject();
161 else if (const auto compilationUnit = typeReference->compilationUnit())
162 firstMetaObject = compilationUnit->rootPropertyCache()->firstCppMetaObject();
163 if (isUsableComponent(firstMetaObject))
164 continue;
165
166 // if here, not a QQmlComponent, so needs wrapping
167 const QQmlPropertyData *pd = nullptr;
168 if (binding->propertyNameIndex != quint32(0)) {
169 bool notInRevision = false;
170 pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
171 } else {
172 pd = defaultProperty;
173 }
174 if (!pd || !pd->isQObject())
175 continue;
176
177 // If the version is given, use it and look up by QQmlType.
178 // Otherwise, make sure we look up by metaobject.
179 // TODO: Is this correct?
182 : QQmlMetaType::rawPropertyCacheForType(pd->propType());
183 const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
184 while (mo) {
185 if (mo == &QQmlComponent::staticMetaObject)
186 break;
187 mo = mo->superClass();
188 }
189
190 if (!mo)
191 continue;
192
193 if (!wrapImplicitComponent(binding))
194 return error(binding, tr("Cannot wrap implicit component"));
195 }
196
197 return QQmlError();
198}
199
200template<typename ObjectContainer>
202 int root)
203{
204 // Find implicit components in the inline component itself. Also warn about inline
205 // components being explicit components.
206
207 const auto rootObj = m_compiler->objectAt(root);
209
210 if (const int typeName = rootObj->inheritedTypeNameIndex) {
211 const auto *tref = resolvedType(typeName);
212 Q_ASSERT(tref);
213 if (tref->type().metaObject() == &QQmlComponent::staticMetaObject) {
214 qCWarning(lcQmlTypeCompiler).nospace().noquote()
215 << m_compiler->url().toString() << ":" << rootObj->location.line() << ":"
216 << rootObj->location.column()
217 << ": Using a Component as the root of an inline component is deprecated: "
218 "inline components are "
219 "automatically wrapped into Components when needed.";
220 return QQmlError();
221 }
222 }
223
224 const QQmlPropertyCache::ConstPtr rootCache = m_propertyCaches->at(root);
225 Q_ASSERT(rootCache);
226
227 return findAndRegisterImplicitComponents(rootObj, rootCache);
228}
229
230// Resolve ignores everything relating to inline components, except for implicit components.
231template<typename ObjectContainer>
233{
234 // Detect real Component {} objects as well as implicitly defined components, such as
235 // someItemDelegate: Item {}
236 // In the implicit case Item is surrounded by a synthetic Component {} because the property
237 // on the left hand side is of QQmlComponent type.
238 const int objCountWithoutSynthesizedComponents = m_compiler->objectCount();
239
240 if (root != 0) {
241 const QQmlError error = resolveComponentsInInlineComponentRoot(root);
242 if (error.isValid())
243 return error;
244 }
245
246 // root+1, as ic root is handled at the end
247 const int startObjectIndex = root == 0 ? root : root+1;
248
249 for (int i = startObjectIndex; i < objCountWithoutSynthesizedComponents; ++i) {
250 auto obj = m_compiler->objectAt(i);
251 const bool isInlineComponentRoot
253 const bool isPartOfInlineComponent
255 QQmlPropertyCache::ConstPtr cache = m_propertyCaches->at(i);
256
257 if (root == 0) {
258 // normal component root, skip over anything inline component related
259 if (isInlineComponentRoot || isPartOfInlineComponent)
260 continue;
261 } else if (!isPartOfInlineComponent || isInlineComponentRoot) {
262 // When handling an inline component, stop where the inline component ends
263 // Note: We do not support nested inline components. Therefore, isInlineComponentRoot
264 // tells us that the element after the current inline component is again an
265 // inline component
266 break;
267 }
268
269 if (obj->inheritedTypeNameIndex == 0 && !cache)
270 continue;
271
272 bool isExplicitComponent = false;
273 if (obj->inheritedTypeNameIndex) {
274 auto *tref = resolvedType(obj->inheritedTypeNameIndex);
275 Q_ASSERT(tref);
276 if (tref->type().metaObject() == &QQmlComponent::staticMetaObject)
277 isExplicitComponent = true;
278 }
279
280 if (!isExplicitComponent) {
281 if (cache) {
282 const QQmlError error = findAndRegisterImplicitComponents(obj, cache);
283 if (error.isValid())
284 return error;
285 }
286 continue;
287 }
288
289 if (!markAsComponent(i))
290 return error(obj, tr("Cannot mark object as component"));
291
292 // check if this object is the root
293 if (i == 0) {
294 if (isExplicitComponent)
295 qCWarning(lcQmlTypeCompiler).nospace().noquote()
296 << m_compiler->url().toString() << ":" << obj->location.line() << ":"
297 << obj->location.column()
298 << ": Using a Component as the root of a QML document is deprecated: types "
299 "defined in qml documents are "
300 "automatically wrapped into Components when needed.";
301 }
302
303 if (obj->functionCount() > 0)
304 return error(obj, tr("Component objects cannot declare new functions."));
305 if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
306 return error(obj, tr("Component objects cannot declare new properties."));
307 if (obj->signalCount() > 0)
308 return error(obj, tr("Component objects cannot declare new signals."));
309
310 if (obj->bindingCount() == 0)
311 return error(obj, tr("Cannot create empty component specification"));
312
313 const auto rootBinding = obj->bindingsBegin();
314 const auto bindingsEnd = obj->bindingsEnd();
315
316 // Produce the more specific "no properties" error rather than the "invalid body" error
317 // where possible.
318 for (auto b = rootBinding; b != bindingsEnd; ++b) {
319 if (b->propertyNameIndex == 0)
320 continue;
321
322 return error(b, tr("Component elements may not contain properties other than id"));
323 }
324
325 if (auto b = rootBinding;
326 b->type() != QV4::CompiledData::Binding::Type_Object || ++b != bindingsEnd) {
327 return error(obj, tr("Invalid component body specification"));
328 }
329
330 // For the root object, we are going to collect ids/aliases and resolve them for as a
331 // separate last pass.
332 if (i != 0)
333 m_componentRoots.append(i);
334 }
335
336 for (int i = 0; i < m_componentRoots.size(); ++i) {
337 CompiledObject *component = m_compiler->objectAt(m_componentRoots.at(i));
338 const auto rootBinding = component->bindingsBegin();
339
340 m_idToObjectIndex.clear();
341 m_objectsWithAliases.clear();
342 m_generalizedGroupProperties.clear();
343
344 if (const QQmlError error = collectIdsAndAliases(rootBinding->value.objectIndex);
345 error.isValid()) {
346 return error;
347 }
348
349 allocateNamedObjects(component);
350
351 if (const QQmlError error = resolveAliases(m_componentRoots.at(i)); error.isValid())
352 return error;
353
354 resolveGeneralizedGroupProperties(m_componentRoots.at(i));
355 }
356
357 // Collect ids and aliases for root
358 m_idToObjectIndex.clear();
359 m_objectsWithAliases.clear();
360 m_generalizedGroupProperties.clear();
361
362 if (const QQmlError error = collectIdsAndAliases(root); error.isValid())
363 return error;
364
365 allocateNamedObjects(m_compiler->objectAt(root));
366 if (const QQmlError error = resolveAliases(root); error.isValid())
367 return error;
368
369 resolveGeneralizedGroupProperties(root);
370 return QQmlError();
371}
372
373template<typename ObjectContainer>
375{
376 auto obj = m_compiler->objectAt(objectIndex);
377
378 if (obj->idNameIndex != 0) {
379 if (m_idToObjectIndex.contains(obj->idNameIndex))
380 return error(obj->locationOfIdProperty, tr("id is not unique"));
381 setObjectId(objectIndex);
382 m_idToObjectIndex.insert(obj->idNameIndex, objectIndex);
383 }
384
385 if (obj->aliasCount() > 0)
386 m_objectsWithAliases.append(objectIndex);
387
388 // Stop at Component boundary
389 if (obj->hasFlag(QV4::CompiledData::Object::IsComponent) && objectIndex != /*root object*/0)
390 return QQmlError();
391
392 for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd();
393 binding != end; ++binding) {
394 switch (binding->type()) {
396 const auto *inner = m_compiler->objectAt(binding->value.objectIndex);
397 if (m_compiler->stringAt(inner->inheritedTypeNameIndex).isEmpty()) {
398 const auto cache = m_propertyCaches->at(objectIndex);
399 if (!cache || !cache->property(
400 m_compiler->stringAt(binding->propertyNameIndex), nullptr, nullptr)) {
401 m_generalizedGroupProperties.append(binding);
402 }
403 }
404 }
408 if (const QQmlError error = collectIdsAndAliases(binding->value.objectIndex);
409 error.isValid()) {
410 return error;
411 }
412 break;
413 default:
414 break;
415 }
416 }
417
418 return QQmlError();
419}
420
421template<typename ObjectContainer>
423{
424 if (m_objectsWithAliases.isEmpty())
425 return QQmlError();
426
427 QQmlPropertyCacheAliasCreator<ObjectContainer> aliasCacheCreator(m_propertyCaches, m_compiler);
428
429 bool atLeastOneAliasResolved;
430 do {
431 atLeastOneAliasResolved = false;
432 QVector<int> pendingObjects;
433
434 for (int objectIndex: std::as_const(m_objectsWithAliases)) {
435
437 const auto &component = *m_compiler->objectAt(componentIndex);
438 const auto result = resolveAliasesInObject(component, objectIndex, &error);
439 if (error.isValid())
440 return error;
441
442 if (result == AllAliasesResolved) {
443 QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(
444 component, objectIndex, m_enginePrivate);
445 if (error.isValid())
446 return error;
447 atLeastOneAliasResolved = true;
448 } else if (result == SomeAliasesResolved) {
449 atLeastOneAliasResolved = true;
450 pendingObjects.append(objectIndex);
451 } else {
452 pendingObjects.append(objectIndex);
453 }
454 }
455 qSwap(m_objectsWithAliases, pendingObjects);
456 } while (!m_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
457
458 if (!atLeastOneAliasResolved && !m_objectsWithAliases.isEmpty()) {
459 const CompiledObject *obj = m_compiler->objectAt(m_objectsWithAliases.first());
460 for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
461 if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved))
462 return error(alias->location, tr("Circular alias reference detected"));
463 }
464 }
465
466 return QQmlError();
467}
468
469template<typename ObjectContainer>
471 int componentIndex)
472{
473 const auto &component = *m_compiler->objectAt(componentIndex);
474 for (CompiledBinding *binding : m_generalizedGroupProperties)
475 resolveGeneralizedGroupProperty(component, binding);
476}
477
479
480#endif // QQMLCOMPONENTANDALIASRESOLVER_P_H
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:129
constexpr bool hasMinorVersion() const
Returns true if the minor version is known, otherwise false.
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_FALLTHROUGH()
#define Q_DECLARE_TR_FUNCTIONS(context)
DBusConnection const char DBusError * error
#define qCWarning(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
const char * typeName
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)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define tr(X)
unsigned int quint32
Definition qtypes.h:50
obj metaObject() -> className()
\inmodule QtCore