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 tools applications 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#ifndef QQMLPROPERTYCACHECREATOR_P_H
40#define QQMLPROPERTYCACHECREATOR_P_H
41
42//
43// W A R N I N G
44// -------------
45//
46// This file is not part of the Qt API. It exists purely as an
47// implementation detail. This header file may change from version to
48// version without notice, or even be removed.
49//
50// We mean it.
51//
52
53#include <private/qqmlvaluetype_p.h>
54#include <private/qqmlengine_p.h>
55#include <private/qqmlmetaobject_p.h>
56#include <private/qqmlpropertyresolver_p.h>
57#include <private/qqmltypedata_p.h>
58#include <private/inlinecomponentutils_p.h>
59#include <private/qqmlsourcecoordinate_p.h>
60
61#include <QScopedValueRollback>
62#include <vector>
63
64QT_BEGIN_NAMESPACE
65
66inline QQmlError qQmlCompileError(const QV4::CompiledData::Location &location,
67 const QString &description)
68{
69 QQmlError error;
70 error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: location.line));
71 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: location.column));
72 error.setDescription(description);
73 return error;
74}
75
76struct QQmlBindingInstantiationContext {
77 QQmlBindingInstantiationContext() {}
78 QQmlBindingInstantiationContext(int referencingObjectIndex,
79 const QV4::CompiledData::Binding *instantiatingBinding,
80 const QString &instantiatingPropertyName,
81 QQmlPropertyCache *referencingObjectPropertyCache);
82
83 bool resolveInstantiatingProperty();
84 QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const;
85
86 int referencingObjectIndex = -1;
87 const QV4::CompiledData::Binding *instantiatingBinding = nullptr;
88 QString instantiatingPropertyName;
89 QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache;
90 QQmlPropertyData *instantiatingProperty = nullptr;
91};
92
93struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext>
94{
95 void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const;
96};
97
98struct QQmlPropertyCacheCreatorBase
99{
100 Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase)
101public:
102 static QAtomicInt classIndexCounter;
103
104 static int metaTypeForPropertyType(QV4::CompiledData::BuiltinType type);
105
106 static QByteArray createClassNameTypeByUrl(const QUrl &url);
107
108 static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, int icId);
109};
110
111template <typename ObjectContainer>
112class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase
113{
114public:
115 typedef typename ObjectContainer::CompiledObject CompiledObject;
116
117 QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
118 QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
119 QQmlEnginePrivate *enginePrivate,
120 const ObjectContainer *objectContainer, const QQmlImports *imports,
121 const QByteArray &typeClassName);
122
123 QQmlError buildMetaObjects();
124
125 enum class VMEMetaObjectIsRequired {
126 Maybe,
127 Always
128 };
129protected:
130 QQmlError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired);
131 QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const;
132 QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
133
134 int metaTypeForParameter(const QV4::CompiledData::ParameterType &param, QString *customTypeName = nullptr);
135
136 QString stringAt(int index) const { return objectContainer->stringAt(index); }
137
138 QQmlEnginePrivate * const enginePrivate;
139 const ObjectContainer * const objectContainer;
140 const QQmlImports * const imports;
141 QQmlPropertyCacheVector *propertyCaches;
142 QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings;
143 QByteArray typeClassName; // not const as we temporarily chang it for inline components
144 unsigned int currentRoot; // set to objectID of inline component root when handling inline components
145};
146
147template <typename ObjectContainer>
148inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
149 QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
150 QQmlEnginePrivate *enginePrivate,
151 const ObjectContainer *objectContainer, const QQmlImports *imports,
152 const QByteArray &typeClassName)
153 : enginePrivate(enginePrivate)
154 , objectContainer(objectContainer)
155 , imports(imports)
156 , propertyCaches(propertyCaches)
157 , pendingGroupPropertyBindings(pendingGroupPropertyBindings)
158 , typeClassName(typeClassName)
159 , currentRoot(-1)
160{
161 propertyCaches->resize(size: objectContainer->objectCount());
162}
163
164template <typename ObjectContainer>
165inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
166{
167 using namespace icutils;
168 QQmlBindingInstantiationContext context;
169
170 // get a list of all inline components
171 using InlineComponent = typename std::remove_reference<decltype (*(std::declval<CompiledObject>().inlineComponentsBegin()))>::type;
172 std::vector<InlineComponent> allICs {};
173 for (int i=0; i != objectContainer->objectCount(); ++i) {
174 const CompiledObject *obj = objectContainer->objectAt(i);
175 for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
176 allICs.push_back(*it);
177 }
178 }
179
180 // create a graph on inline components referencing inline components
181 std::vector<Node> nodes;
182 nodes.resize(allICs.size());
183 std::iota(first: nodes.begin(), last: nodes.end(), value: 0);
184 AdjacencyList adjacencyList;
185 adjacencyList.resize(new_size: nodes.size());
186 fillAdjacencyListForInlineComponents(objectContainer, adjacencyList, nodes, allICs);
187
188 bool hasCycle = false;
189 auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
190
191 if (hasCycle) {
192 QQmlError diag;
193 diag.setDescription(QLatin1String("Inline components form a cycle!"));
194 return diag;
195 }
196
197 // create meta objects for inline components before compiling actual root component
198 for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
199 const auto &ic = allICs[nodeIt->index];
200 QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex);
201 Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr);
202 Q_ASSERT(typeRef->typePropertyCache.isNull()); // not set yet
203
204 QByteArray icTypeName { objectContainer->stringAt(ic.nameIndex).toUtf8() };
205 QScopedValueRollback<QByteArray> nameChange {typeClassName, icTypeName};
206 QScopedValueRollback<unsigned int> rootChange {currentRoot, ic.objectIndex};
207 QQmlError diag = buildMetaObjectRecursively(objectIndex: ic.objectIndex, context, isVMERequired: VMEMetaObjectIsRequired::Always);
208 if (diag.isValid()) {
209 return diag;
210 }
211 typeRef->typePropertyCache = propertyCaches->at(index: ic.objectIndex);
212 Q_ASSERT(!typeRef->typePropertyCache.isNull());
213 }
214
215 return buildMetaObjectRecursively(/*root object*/objectIndex: 0, context, isVMERequired: VMEMetaObjectIsRequired::Maybe);
216}
217
218template <typename ObjectContainer>
219inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
220{
221 auto isAddressable = [](const QUrl &url) {
222 const QString fileName = url.fileName();
223 return !fileName.isEmpty() && fileName.front().isUpper();
224 };
225
226 const CompiledObject *obj = objectContainer->objectAt(objectIndex);
227 bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0
228 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
229 || (((obj->flags & QV4::CompiledData::Object::IsComponent)
230 || (objectIndex == 0 && isAddressable(objectContainer->url())))
231 && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType);
232
233 if (!needVMEMetaObject) {
234 auto binding = obj->bindingsBegin();
235 auto end = obj->bindingsEnd();
236 for ( ; binding != end; ++binding) {
237 if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
238 // If the on assignment is inside a group property, we need to distinguish between QObject based
239 // group properties and value type group properties. For the former the base type is derived from
240 // the property that references us, for the latter we only need a meta-object on the referencing object
241 // because interceptors can't go to the shared value type instances.
242 if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(idx: context.instantiatingProperty->propType())) {
243 if (!propertyCaches->needsVMEMetaObject(index: context.referencingObjectIndex)) {
244 const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
245 auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
246 Q_ASSERT(typeRef);
247 QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(p: enginePrivate));
248 QQmlError error = createMetaObject(objectIndex: context.referencingObjectIndex, obj, baseTypeCache);
249 if (error.isValid())
250 return error;
251 }
252 } else {
253 // On assignments are implemented using value interceptors, which require a VME meta object.
254 needVMEMetaObject = true;
255 }
256 break;
257 }
258 }
259 }
260
261 QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
262 {
263 QQmlError error;
264 baseTypeCache = propertyCacheForObject(obj, context, error: &error);
265 if (error.isValid())
266 return error;
267 }
268
269 if (baseTypeCache) {
270 if (needVMEMetaObject) {
271 QQmlError error = createMetaObject(objectIndex, obj, baseTypeCache);
272 if (error.isValid())
273 return error;
274 } else {
275 propertyCaches->set(index: objectIndex, replacement: baseTypeCache);
276 }
277 }
278
279 if (QQmlPropertyCache *thisCache = propertyCaches->at(index: objectIndex)) {
280 auto binding = obj->bindingsBegin();
281 auto end = obj->bindingsEnd();
282 for ( ; binding != end; ++binding)
283 if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
284 QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(index: binding->propertyNameIndex), thisCache);
285
286 // Binding to group property where we failed to look up the type of the
287 // property? Possibly a group property that is an alias that's not resolved yet.
288 // Let's attempt to resolve it after we're done with the aliases and fill in the
289 // propertyCaches entry then.
290 if (!context.resolveInstantiatingProperty())
291 pendingGroupPropertyBindings->append(t: context);
292
293 QQmlError error = buildMetaObjectRecursively(objectIndex: binding->value.objectIndex, context, isVMERequired: VMEMetaObjectIsRequired::Maybe);
294 if (error.isValid())
295 return error;
296 }
297 }
298
299 QQmlError noError;
300 return noError;
301}
302
303template <typename ObjectContainer>
304inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const
305{
306 if (context.instantiatingProperty) {
307 return context.instantiatingPropertyCache(enginePrivate);
308 } else if (obj->inheritedTypeNameIndex != 0) {
309 auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
310 QQmlType qmltype = typeRef->type;
311 Q_ASSERT(typeRef);
312
313 if (typeRef->isFullyDynamicType) {
314 if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
315 *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Fully dynamic types cannot declare new properties."));
316 return nullptr;
317 }
318 if (obj->signalCount() > 0) {
319 *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Fully dynamic types cannot declare new signals."));
320 return nullptr;
321 }
322 if (obj->functionCount() > 0) {
323 *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Fully Dynamic types cannot declare new functions."));
324 return nullptr;
325 }
326 }
327
328 return typeRef->createPropertyCache(QQmlEnginePrivate::get(p: enginePrivate));
329 } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) {
330 auto *typeRef = objectContainer->resolvedType(
331 context.instantiatingBinding->propertyNameIndex);
332 Q_ASSERT(typeRef);
333 QQmlType qmltype = typeRef->type;
334 if (!qmltype.isValid()) {
335 imports->resolveType(stringAt(index: context.instantiatingBinding->propertyNameIndex),
336 &qmltype, nullptr, nullptr, nullptr);
337 }
338
339 const QMetaObject *attachedMo = qmltype.attachedPropertiesType(engine: enginePrivate);
340 if (!attachedMo) {
341 *error = qQmlCompileError(location: context.instantiatingBinding->location, description: QQmlPropertyCacheCreatorBase::tr(sourceText: "Non-existent attached object"));
342 return nullptr;
343 }
344 return enginePrivate->cache(metaObject: attachedMo);
345 }
346 return nullptr;
347}
348
349template <typename ObjectContainer>
350inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
351{
352 QQmlRefPointer<QQmlPropertyCache> cache;
353 cache.adopt(baseTypeCache->copyAndReserve(propertyCount: obj->propertyCount() + obj->aliasCount(),
354 methodCount: obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(),
355 signalCount: obj->signalCount() + obj->propertyCount() + obj->aliasCount(), enumCount: obj->enumCount()));
356
357 propertyCaches->set(index: objectIndex, replacement: cache);
358 propertyCaches->setNeedsVMEMetaObject(objectIndex);
359
360 QByteArray newClassName;
361
362 if (objectIndex == /*root object*/0 || int(currentRoot) == objectIndex) {
363 newClassName = typeClassName;
364 }
365 if (newClassName.isEmpty()) {
366 newClassName = QQmlMetaObject(baseTypeCache.data()).className();
367 newClassName.append(s: "_QML_");
368 newClassName.append(a: QByteArray::number(classIndexCounter.fetchAndAddRelaxed(valueToAdd: 1)));
369 }
370
371 cache->_dynamicClassName = newClassName;
372
373 int varPropCount = 0;
374
375 QQmlPropertyResolver resolver(baseTypeCache);
376
377 auto p = obj->propertiesBegin();
378 auto pend = obj->propertiesEnd();
379 for ( ; p != pend; ++p) {
380 if (p->builtinType() == QV4::CompiledData::BuiltinType::Var)
381 varPropCount++;
382
383 bool notInRevision = false;
384 QQmlPropertyData *d = resolver.property(stringAt(index: p->nameIndex), &notInRevision);
385 if (d && d->isFinal())
386 return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Cannot override FINAL property"));
387 }
388
389 auto a = obj->aliasesBegin();
390 auto aend = obj->aliasesEnd();
391 for ( ; a != aend; ++a) {
392 bool notInRevision = false;
393 QQmlPropertyData *d = resolver.property(stringAt(index: a->nameIndex), &notInRevision);
394 if (d && d->isFinal())
395 return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Cannot override FINAL property"));
396 }
397
398 int effectivePropertyIndex = cache->propertyIndexCacheStart;
399 int effectiveMethodIndex = cache->methodIndexCacheStart;
400
401 // For property change signal override detection.
402 // We prepopulate a set of signal names which already exist in the object,
403 // and throw an error if there is a signal/method defined as an override.
404 QSet<QString> seenSignals;
405 seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
406 QQmlPropertyCache *parentCache = cache.data();
407 while ((parentCache = parentCache->parent())) {
408 if (int pSigCount = parentCache->signalCount()) {
409 int pSigOffset = parentCache->signalOffset();
410 for (int i = pSigOffset; i < pSigCount; ++i) {
411 QQmlPropertyData *currPSig = parentCache->signal(index: i);
412 // XXX TODO: find a better way to get signal name from the property data :-/
413 for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
414 iter != parentCache->stringCache.end(); ++iter) {
415 if (currPSig == (*iter).second) {
416 seenSignals.insert(value: iter.key());
417 break;
418 }
419 }
420 }
421 }
422 }
423
424 // Set up notify signals for properties - first normal, then alias
425 p = obj->propertiesBegin();
426 pend = obj->propertiesEnd();
427 for ( ; p != pend; ++p) {
428 auto flags = QQmlPropertyData::defaultSignalFlags();
429
430 QString changedSigName = stringAt(index: p->nameIndex) + QLatin1String("Changed");
431 seenSignals.insert(value: changedSigName);
432
433 cache->appendSignal(changedSigName, flags, coreIndex: effectiveMethodIndex++);
434 }
435
436 a = obj->aliasesBegin();
437 aend = obj->aliasesEnd();
438 for ( ; a != aend; ++a) {
439 auto flags = QQmlPropertyData::defaultSignalFlags();
440
441 QString changedSigName = stringAt(index: a->nameIndex) + QLatin1String("Changed");
442 seenSignals.insert(value: changedSigName);
443
444 cache->appendSignal(changedSigName, flags, coreIndex: effectiveMethodIndex++);
445 }
446
447 auto e = obj->enumsBegin();
448 auto eend = obj->enumsEnd();
449 for ( ; e != eend; ++e) {
450 const int enumValueCount = e->enumValueCount();
451 QVector<QQmlEnumValue> values;
452 values.reserve(size: enumValueCount);
453
454 auto enumValue = e->enumValuesBegin();
455 auto end = e->enumValuesEnd();
456 for ( ; enumValue != end; ++enumValue)
457 values.append(t: QQmlEnumValue(stringAt(index: enumValue->nameIndex), enumValue->value));
458
459 cache->appendEnum(stringAt(index: e->nameIndex), values);
460 }
461
462 // Dynamic signals
463 auto s = obj->signalsBegin();
464 auto send = obj->signalsEnd();
465 for ( ; s != send; ++s) {
466 const int paramCount = s->parameterCount();
467
468 QList<QByteArray> names;
469 names.reserve(size: paramCount);
470 QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
471
472 if (paramCount) {
473 paramTypes[0] = paramCount;
474
475 int i = 0;
476 auto param = s->parametersBegin();
477 auto end = s->parametersEnd();
478 for ( ; param != end; ++param, ++i) {
479 names.append(stringAt(index: param->nameIndex).toUtf8());
480
481 QString customTypeName;
482 auto type = metaTypeForParameter(param: param->type, customTypeName: &customTypeName);
483 if (type == QMetaType::UnknownType)
484 return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid signal parameter type: %1").arg(a: customTypeName));
485
486 paramTypes[i + 1] = type;
487 }
488 }
489
490 auto flags = QQmlPropertyData::defaultSignalFlags();
491 if (paramCount)
492 flags.setHasArguments(true);
493
494 QString signalName = stringAt(index: s->nameIndex);
495 if (seenSignals.contains(value: signalName))
496 return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Duplicate signal name: invalid override of property change signal or superclass signal"));
497 seenSignals.insert(value: signalName);
498
499 cache->appendSignal(signalName, flags, coreIndex: effectiveMethodIndex++,
500 types: paramCount?paramTypes.constData():nullptr, names);
501 }
502
503
504 // Dynamic slots
505 auto function = objectContainer->objectFunctionsBegin(obj);
506 auto fend = objectContainer->objectFunctionsEnd(obj);
507 for ( ; function != fend; ++function) {
508 auto flags = QQmlPropertyData::defaultSlotFlags();
509
510 const QString slotName = stringAt(index: function->nameIndex);
511 if (seenSignals.contains(value: slotName))
512 return qQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Duplicate method name: invalid override of property change signal or superclass signal"));
513 // Note: we don't append slotName to the seenSignals list, since we don't
514 // protect against overriding change signals or methods with properties.
515
516 QList<QByteArray> parameterNames;
517 QVector<int> parameterTypes;
518 auto formal = function->formalsBegin();
519 auto end = function->formalsEnd();
520 for ( ; formal != end; ++formal) {
521 flags.setHasArguments(true);
522 parameterNames << stringAt(index: formal->nameIndex).toUtf8();
523 int type = metaTypeForParameter(param: formal->type);
524 if (type == QMetaType::UnknownType)
525 type = QMetaType::QVariant;
526 parameterTypes << type;
527 }
528
529 int returnType = metaTypeForParameter(param: function->returnType);
530 if (returnType == QMetaType::UnknownType)
531 returnType = QMetaType::QVariant;
532
533 cache->appendMethod(slotName, flags, coreIndex: effectiveMethodIndex++, returnType, names: parameterNames, parameterTypes);
534 }
535
536
537 // Dynamic properties
538 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
539 int propertyIdx = 0;
540 p = obj->propertiesBegin();
541 pend = obj->propertiesEnd();
542 for ( ; p != pend; ++p, ++propertyIdx) {
543 int propertyType = 0;
544 int propertTypeMinorVersion = 0;
545 QQmlPropertyData::Flags propertyFlags;
546
547 const QV4::CompiledData::BuiltinType type = p->builtinType();
548
549 if (type == QV4::CompiledData::BuiltinType::Var)
550 propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
551
552
553 if (type != QV4::CompiledData::BuiltinType::InvalidBuiltin) {
554 propertyType = metaTypeForPropertyType(type);
555
556 if (type == QV4::CompiledData::BuiltinType::Variant)
557 propertyFlags.type = QQmlPropertyData::Flags::QVariantType;
558 } else {
559 Q_ASSERT(!p->isBuiltinType);
560
561 QQmlType qmltype;
562 bool selfReference = false;
563 if (!imports->resolveType(stringAt(index: p->builtinTypeOrTypeNameIndex), &qmltype, nullptr, nullptr, nullptr,
564 nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
565 return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid property type"));
566 }
567
568 // inline components are not necessarily valid yet
569 Q_ASSERT(qmltype.isValid() || qmltype.isInlineComponentType());
570 if (qmltype.isComposite() || qmltype.isInlineComponentType()) {
571 CompositeMetaTypeIds typeIds;
572 if (qmltype.isInlineComponentType()) {
573 auto objectId = qmltype.inlineComponendId();
574 auto containingType = qmltype.containingType();
575 if (containingType.isValid()) {
576 auto icType = containingType.lookupInlineComponentById(objectid: objectId);
577 typeIds = {icType.typeId(), icType.qListTypeId()};
578 } else {
579 typeIds = {};
580 }
581 if (!typeIds.isValid()) // type has not been registered yet, we must be in containing type
582 typeIds = objectContainer->typeIdsForComponent(objectId);
583 Q_ASSERT(typeIds.isValid());
584 } else if (selfReference) {
585 typeIds = objectContainer->typeIdsForComponent();
586 } else {
587 QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(unNormalizedUrl: qmltype.sourceUrl());
588 Q_ASSERT(tdata);
589 Q_ASSERT(tdata->isComplete());
590
591 auto compilationUnit = tdata->compilationUnit();
592 typeIds = compilationUnit->typeIdsForComponent();
593 }
594
595 if (p->isList) {
596 propertyType = typeIds.listId;
597 } else {
598 propertyType = typeIds.id;
599 }
600 } else {
601 if (p->isList) {
602 propertyType = qmltype.qListTypeId();
603 } else {
604 propertyType = qmltype.typeId();
605 propertTypeMinorVersion = qmltype.minorVersion();
606 }
607 }
608
609 if (p->isList)
610 propertyFlags.type = QQmlPropertyData::Flags::QListType;
611 else
612 propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
613 }
614
615 if (!p->isReadOnly && !p->isList)
616 propertyFlags.setIsWritable(true);
617
618
619 QString propertyName = stringAt(index: p->nameIndex);
620 if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
621 cache->_defaultPropertyName = propertyName;
622 cache->appendProperty(propertyName, flags: propertyFlags, coreIndex: effectivePropertyIndex++,
623 propType: propertyType, revision: propertTypeMinorVersion, notifyIndex: effectiveSignalIndex);
624
625 effectiveSignalIndex++;
626 }
627
628 QQmlError noError;
629 return noError;
630}
631
632template <typename ObjectContainer>
633inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType &param,
634 QString *customTypeName)
635{
636 if (param.indexIsBuiltinType) {
637 // built-in type
638 return metaTypeForPropertyType(type: static_cast<QV4::CompiledData::BuiltinType>(int(param.typeNameIndexOrBuiltinType)));
639 }
640
641 // lazily resolved type
642 const QString typeName = stringAt(index: param.typeNameIndexOrBuiltinType);
643 if (customTypeName)
644 *customTypeName = typeName;
645 QQmlType qmltype;
646 bool selfReference = false;
647 if (!imports->resolveType(type: typeName, type_return: &qmltype, version_major: nullptr, version_minor: nullptr, ns_return: nullptr, errors: nullptr, registrationType: QQmlType::AnyRegistrationType,
648 typeRecursionDetected: &selfReference))
649 return QMetaType::UnknownType;
650
651 if (!qmltype.isComposite())
652 return qmltype.typeId();
653
654 if (selfReference)
655 return objectContainer->typeIdsForComponent().id;
656
657 QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(unNormalizedUrl: qmltype.sourceUrl());
658 Q_ASSERT(tdata);
659 Q_ASSERT(tdata->isComplete());
660
661 auto compilationUnit = tdata->compilationUnit();
662
663 return compilationUnit->metaTypeId;
664}
665
666template <typename ObjectContainer>
667class QQmlPropertyCacheAliasCreator
668{
669public:
670 typedef typename ObjectContainer::CompiledObject CompiledObject;
671
672 QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
673
674 void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv);
675
676 QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
677
678private:
679 void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv);
680 QQmlError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
681
682 void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
683
684 int objectForId(const CompiledObject &component, int id) const;
685
686 QQmlPropertyCacheVector *propertyCaches;
687 const ObjectContainer *objectContainer;
688};
689
690template <typename ObjectContainer>
691inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
692 : propertyCaches(propertyCaches)
693 , objectContainer(objectContainer)
694{
695
696}
697
698template <typename ObjectContainer>
699inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv)
700{
701 // skip the root object (index 0) as that one does not have a first object index originating
702 // from a binding.
703 for (int i = 1; i < objectContainer->objectCount(); ++i) {
704 const CompiledObject &component = *objectContainer->objectAt(i);
705 if (!(component.flags & QV4::CompiledData::Object::IsComponent))
706 continue;
707
708 const auto rootBinding = component.bindingsBegin();
709 appendAliasPropertiesInMetaObjectsWithinComponent(component, firstObjectIndex: rootBinding->value.objectIndex, enginePriv);
710 }
711
712 const int rootObjectIndex = 0;
713 appendAliasPropertiesInMetaObjectsWithinComponent(component: *objectContainer->objectAt(rootObjectIndex), firstObjectIndex: rootObjectIndex, enginePriv);
714}
715
716template <typename ObjectContainer>
717inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv)
718{
719 QVector<int> objectsWithAliases;
720 collectObjectsWithAliasesRecursively(objectIndex: firstObjectIndex, objectsWithAliases: &objectsWithAliases);
721 if (objectsWithAliases.isEmpty())
722 return;
723
724 const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) {
725 auto alias = object.aliasesBegin();
726 auto end = object.aliasesEnd();
727 for ( ; alias != end; ++alias) {
728 Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
729
730 const int targetObjectIndex = objectForId(component, id: alias->targetObjectId);
731 Q_ASSERT(targetObjectIndex >= 0);
732
733 if (alias->aliasToLocalAlias)
734 continue;
735
736 if (alias->encodedMetaPropertyIndex == -1)
737 continue;
738
739 const QQmlPropertyCache *targetCache = propertyCaches->at(index: targetObjectIndex);
740 Q_ASSERT(targetCache);
741
742 int coreIndex = QQmlPropertyIndex::fromEncoded(encodedIndex: alias->encodedMetaPropertyIndex).coreIndex();
743 QQmlPropertyData *targetProperty = targetCache->property(index: coreIndex);
744 if (!targetProperty)
745 return false;
746 }
747 return true;
748 };
749
750 do {
751 QVector<int> pendingObjects;
752
753 for (int objectIndex: qAsConst(t&: objectsWithAliases)) {
754 const CompiledObject &object = *objectContainer->objectAt(objectIndex);
755
756 if (allAliasTargetsExist(object)) {
757 appendAliasesToPropertyCache(component, objectIndex, enginePriv);
758 } else {
759 pendingObjects.append(t: objectIndex);
760 }
761
762 }
763 qSwap(value1&: objectsWithAliases, value2&: pendingObjects);
764 } while (!objectsWithAliases.isEmpty());
765}
766
767template <typename ObjectContainer>
768inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const
769{
770 const CompiledObject &object = *objectContainer->objectAt(objectIndex);
771 if (object.aliasCount() > 0)
772 objectsWithAliases->append(t: objectIndex);
773
774 // Stop at Component boundary
775 if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
776 return;
777
778 auto binding = object.bindingsBegin();
779 auto end = object.bindingsEnd();
780 for (; binding != end; ++binding) {
781 if (binding->type != QV4::CompiledData::Binding::Type_Object
782 && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
783 && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
784 continue;
785
786 collectObjectsWithAliasesRecursively(objectIndex: binding->value.objectIndex, objectsWithAliases);
787 }
788}
789
790template <typename ObjectContainer>
791inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
792 QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv)
793{
794 *type = 0;
795 bool writable = false;
796 bool resettable = false;
797
798 propertyFlags->setIsAlias(true);
799
800 if (alias.aliasToLocalAlias) {
801 const QV4::CompiledData::Alias *lastAlias = &alias;
802 QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
803
804 do {
805 const int targetObjectIndex = objectForId(component, id: lastAlias->targetObjectId);
806 Q_ASSERT(targetObjectIndex >= 0);
807 const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
808 Q_ASSERT(targetObject);
809
810 auto nextAlias = targetObject->aliasesBegin();
811 for (uint i = 0; i < lastAlias->localAliasIndex; ++i)
812 ++nextAlias;
813
814 const QV4::CompiledData::Alias *targetAlias = &(*nextAlias);
815 if (seenAliases.contains(t: targetAlias)) {
816 return qQmlCompileError(location: targetAlias->location,
817 description: QQmlPropertyCacheCreatorBase::tr(sourceText: "Cyclic alias"));
818 }
819
820 seenAliases.append(t: targetAlias);
821 lastAlias = targetAlias;
822 } while (lastAlias->aliasToLocalAlias);
823
824 return propertyDataForAlias(component, alias: *lastAlias, type, minorVersion, propertyFlags, enginePriv);
825 }
826
827 const int targetObjectIndex = objectForId(component, id: alias.targetObjectId);
828 Q_ASSERT(targetObjectIndex >= 0);
829 const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
830
831 if (alias.encodedMetaPropertyIndex == -1) {
832 Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
833 auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
834 if (!typeRef) {
835 // Can be caused by the alias target not being a valid id or property. E.g.:
836 // property alias dataValue: dataVal
837 // invalidAliasComponent { id: dataVal }
838 return qQmlCompileError(targetObject.location,
839 QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid alias target"));
840 }
841
842 if (typeRef->type.isValid())
843 *type = typeRef->type.typeId();
844 else
845 *type = typeRef->compilationUnit()->metaTypeId;
846
847 *minorVersion = typeRef->minorVersion;
848
849 propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
850 } else {
851 int coreIndex = QQmlPropertyIndex::fromEncoded(encodedIndex: alias.encodedMetaPropertyIndex).coreIndex();
852 int valueTypeIndex = QQmlPropertyIndex::fromEncoded(encodedIndex: alias.encodedMetaPropertyIndex).valueTypeIndex();
853
854 QQmlPropertyCache *targetCache = propertyCaches->at(index: targetObjectIndex);
855 Q_ASSERT(targetCache);
856
857 QQmlPropertyData *targetProperty = targetCache->property(index: coreIndex);
858 Q_ASSERT(targetProperty);
859
860 // for deep aliases, valueTypeIndex is always set
861 if (!QQmlValueTypeFactory::isValueType(idx: targetProperty->propType()) && valueTypeIndex != -1) {
862 // deep alias property
863 *type = targetProperty->propType();
864 targetCache = enginePriv->propertyCacheForType(*type);
865 Q_ASSERT(targetCache);
866 targetProperty = targetCache->property(index: valueTypeIndex);
867
868 if (targetProperty == nullptr) {
869 return qQmlCompileError(location: alias.referenceLocation,
870 description: QQmlPropertyCacheCreatorBase::tr(sourceText: "Invalid alias target"));
871 }
872
873 *type = targetProperty->propType();
874 writable = targetProperty->isWritable();
875 resettable = targetProperty->isResettable();
876
877 } else {
878 // value type or primitive type or enum
879 *type = targetProperty->propType();
880
881 writable = targetProperty->isWritable();
882 resettable = targetProperty->isResettable();
883
884 if (valueTypeIndex != -1) {
885 const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type: *type);
886 if (valueTypeMetaObject->property(index: valueTypeIndex).isEnumType())
887 *type = QMetaType::Int;
888 else
889 *type = valueTypeMetaObject->property(index: valueTypeIndex).userType();
890 } else {
891 if (targetProperty->isEnum()) {
892 *type = QMetaType::Int;
893 } else {
894 // Copy type flags
895 propertyFlags->copyPropertyTypeFlags(from: targetProperty->flags());
896
897 if (targetProperty->isVarProperty())
898 propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
899 }
900 }
901 }
902 }
903
904 propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable);
905 propertyFlags->setIsResettable(resettable);
906 return QQmlError();
907}
908
909template <typename ObjectContainer>
910inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
911 const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv)
912{
913 const CompiledObject &object = *objectContainer->objectAt(objectIndex);
914 if (!object.aliasCount())
915 return QQmlError();
916
917 QQmlPropertyCache *propertyCache = propertyCaches->at(index: objectIndex);
918 Q_ASSERT(propertyCache);
919
920 int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
921 int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
922
923 int aliasIndex = 0;
924 auto alias = object.aliasesBegin();
925 auto end = object.aliasesEnd();
926 for ( ; alias != end; ++alias, ++aliasIndex) {
927 Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
928
929 int type = 0;
930 int minorVersion = 0;
931 QQmlPropertyData::Flags propertyFlags;
932 QQmlError error = propertyDataForAlias(component, alias: *alias, type: &type, minorVersion: &minorVersion, propertyFlags: &propertyFlags, enginePriv);
933 if (error.isValid())
934 return error;
935
936 const QString propertyName = objectContainer->stringAt(alias->nameIndex);
937
938 if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias)
939 propertyCache->_defaultPropertyName = propertyName;
940
941 propertyCache->appendProperty(propertyName, flags: propertyFlags, coreIndex: effectivePropertyIndex++,
942 propType: type, revision: minorVersion, notifyIndex: effectiveSignalIndex++);
943 }
944
945 return QQmlError();
946}
947
948template <typename ObjectContainer>
949inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
950{
951 for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
952 const int candidateIndex = component.namedObjectsInComponentTable()[i];
953 const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
954 if (candidate.id == id)
955 return candidateIndex;
956 }
957 return -1;
958}
959
960QT_END_NAMESPACE
961
962#endif // QQMLPROPERTYCACHECREATOR_P_H
963

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