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