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

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