Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmlpropertycachecreator_p.h
Go to the documentation of this file.
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#include <private/qqmlsignalnames_p.h>
25
26#include <QScopedValueRollback>
27
28#if QT_CONFIG(regularexpression)
29#include <QtCore/qregularexpression.h>
30#endif
31
32#include <vector>
33
35
37 const QString &description)
38{
40 error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
41 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
42 error.setDescription(description);
43 return error;
44}
45
62
63struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext>
64{
66 QQmlPropertyCacheVector *propertyCaches) const;
67};
68
70{
72public:
74
76 {
77 switch (type) {
79 case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QVariant>();
80 case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<int>();
81 case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<bool>();
82 case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<qreal>();
83 case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QString>();
84 case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QUrl>();
85 case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QTime>();
86 case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QDate>();
87 case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QDateTime>();
88#if QT_CONFIG(regularexpression)
89 case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QRegularExpression>();
90#else
92#endif
93 case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QRectF>();
94 case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QPointF>();
95 case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QSizeF>();
97 };
98 return QMetaType {};
99 }
100
102 {
103 switch (type) {
105 case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QList<QVariant>>();
106 case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<QList<int>>();
107 case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<QList<bool>>();
108 case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<QList<qreal>>();
109 case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QList<QString>>();
110 case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QList<QUrl>>();
111 case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QList<QTime>>();
112 case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QList<QDate>>();
113 case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
114#if QT_CONFIG(regularexpression)
115 case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QList<QRegularExpression>>();
116#else
118#endif
119 case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QList<QRectF>>();
120 case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QList<QPointF>>();
121 case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QList<QSizeF>>();
123 };
124 return QMetaType {};
125 }
126
127 static bool canCreateClassNameTypeByUrl(const QUrl &url);
129
131
133 // valid if and only if an error occurred
135 // true if there was no error and there are still components left to process
136 bool canResume = false;
137 // the object index of the last processed (inline) component root.
139 };
140};
141
142template <typename ObjectContainer>
144{
145public:
146 using CompiledObject = typename ObjectContainer::CompiledObject;
147 using InlineComponent = typename std::remove_reference<decltype (*(std::declval<CompiledObject>().inlineComponentsBegin()))>::type;
148
152 const ObjectContainer *objectContainer, const QQmlImports *imports,
155
156
165 IncrementalResult buildMetaObjectsIncrementally();
166
173
175 Maybe,
176 Always
177 };
178protected:
181 QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &baseTypeCache);
182
184
185 QString stringAt(int index) const { return objectContainer->stringAt(index); }
186
188 const ObjectContainer * const objectContainer;
189 const QQmlImports * const imports;
192 QByteArray typeClassName; // not const as we temporarily chang it for inline components
193 unsigned int currentRoot; // set to objectID of inline component root when handling inline components
194
196 std::vector<InlineComponent> allICs;
197 std::vector<icutils::Node> nodesSorted;
198 std::vector<icutils::Node>::reverse_iterator nodeIt = nodesSorted.rbegin();
199 bool hasCycle = false;
200};
201
202template <typename ObjectContainer>
204 QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
205 QQmlEnginePrivate *enginePrivate,
206 const ObjectContainer *objectContainer, const QQmlImports *imports,
207 const QByteArray &typeClassName)
208 : enginePrivate(enginePrivate)
209 , objectContainer(objectContainer)
210 , imports(imports)
211 , propertyCaches(propertyCaches)
212 , pendingGroupPropertyBindings(pendingGroupPropertyBindings)
213 , typeClassName(typeClassName)
214 , currentRoot(-1)
215{
217
218 using namespace icutils;
219
220 // get a list of all inline components
221
222 for (int i=0; i != objectContainer->objectCount(); ++i) {
223 const CompiledObject *obj = objectContainer->objectAt(i);
224 for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
225 allICs.push_back(*it);
226 }
227 }
228
229 // create a graph on inline components referencing inline components
230 std::vector<icutils::Node> nodes;
231 nodes.resize(allICs.size());
232 std::iota(nodes.begin(), nodes.end(), 0);
233 AdjacencyList adjacencyList;
234 adjacencyList.resize(nodes.size());
235 fillAdjacencyListForInlineComponents(objectContainer, adjacencyList, nodes, allICs);
236
237 nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
238 nodeIt = nodesSorted.rbegin();
239}
240
241template <typename ObjectContainer>
243{
244 if (hasCycle) {
245 QQmlError diag;
246 diag.setDescription(QLatin1String("Inline components form a cycle!"));
247 return diag;
248 }
249 return {};
250}
251
252template <typename ObjectContainer>
255{
256 // needs to be checked with verifyNoICCycle before this function is called
257 Q_ASSERT(!hasCycle);
258
259 // create meta objects for inline components before compiling actual root component
260 if (nodeIt != nodesSorted.rend()) {
261 const auto &ic = allICs[nodeIt->index()];
262 QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex);
263 Q_ASSERT(propertyCaches->at(ic.objectIndex).isNull());
264 Q_ASSERT(typeRef->typePropertyCache().isNull()); // not set yet
265
266 QByteArray icTypeName { objectContainer->stringAt(ic.nameIndex).toUtf8() };
267 QScopedValueRollback<QByteArray> nameChange {typeClassName, icTypeName};
268 QScopedValueRollback<unsigned int> rootChange {currentRoot, ic.objectIndex};
269 ++nodeIt;
270 QQmlError diag = buildMetaObjectRecursively(ic.objectIndex, m_context, VMEMetaObjectIsRequired::Always);
271 if (diag.isValid()) {
272 return {diag, false, 0};
273 }
274 typeRef->setTypePropertyCache(propertyCaches->at(ic.objectIndex));
275 Q_ASSERT(!typeRef->typePropertyCache().isNull());
276 return { QQmlError(), true, int(ic.objectIndex) };
277 }
278
279 auto diag = buildMetaObjectRecursively(/*root object*/0, m_context, VMEMetaObjectIsRequired::Maybe);
280 return {diag, false, 0};
281}
282
283template <typename ObjectContainer>
285{
286 auto isAddressable = [](const QUrl &url) {
287 const QString fileName = url.fileName();
288 return !fileName.isEmpty() && fileName.front().isUpper();
289 };
290
291 const CompiledObject *obj = objectContainer->objectAt(objectIndex);
292 bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0
293 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
295 || (objectIndex == 0 && isAddressable(objectContainer->url())))
296 && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType());
297
298 if (!needVMEMetaObject) {
299 auto binding = obj->bindingsBegin();
300 auto end = obj->bindingsEnd();
301 for ( ; binding != end; ++binding) {
302 if (binding->type() == QV4::CompiledData::Binding::Type_Object
303 && (binding->flags() & QV4::CompiledData::Binding::IsOnAssignment)) {
304 // If the on assignment is inside a group property, we need to distinguish between QObject based
305 // group properties and value type group properties. For the former the base type is derived from
306 // the property that references us, for the latter we only need a meta-object on the referencing object
307 // because interceptors can't go to the shared value type instances.
308 if (context.instantiatingProperty && QQmlMetaType::isValueType(context.instantiatingProperty->propType())) {
309 if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) {
310 const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
311 auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
312 Q_ASSERT(typeRef);
313 QQmlPropertyCache::ConstPtr baseTypeCache = typeRef->createPropertyCache();
314 QQmlError error = baseTypeCache
315 ? createMetaObject(context.referencingObjectIndex, obj, baseTypeCache)
316 : qQmlCompileError(binding->location, QQmlPropertyCacheCreatorBase::tr(
317 "Type cannot be used for 'on' assignment"));
318 if (error.isValid())
319 return error;
320 }
321 } else {
322 // On assignments are implemented using value interceptors, which require a VME meta object.
323 needVMEMetaObject = true;
324 }
325 break;
326 }
327 }
328 }
329
330 QQmlPropertyCache::ConstPtr baseTypeCache;
331 {
333 baseTypeCache = propertyCacheForObject(obj, context, &error);
334 if (error.isValid())
335 return error;
336 }
337
338 if (baseTypeCache) {
339 if (needVMEMetaObject) {
340 QQmlError error = createMetaObject(objectIndex, obj, baseTypeCache);
341 if (error.isValid())
342 return error;
343 } else {
344 propertyCaches->set(objectIndex, baseTypeCache);
345 }
346 }
347
348 QQmlPropertyCache::ConstPtr thisCache = propertyCaches->at(objectIndex);
349 auto binding = obj->bindingsBegin();
350 auto end = obj->bindingsEnd();
351 for (; binding != end; ++binding) {
352 switch (binding->type()) {
356 // We can always resolve object, group, and attached properties.
357 break;
358 default:
359 // Everything else is of no interest here.
360 continue;
361 }
362
364 objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
365
366 // Binding to group property where we failed to look up the type of the
367 // property? Possibly a group property that is an alias that's not resolved yet.
368 // Let's attempt to resolve it after we're done with the aliases and fill in the
369 // propertyCaches entry then.
370 if (!thisCache || !context.resolveInstantiatingProperty())
371 pendingGroupPropertyBindings->append(context);
372
373 QQmlError error = buildMetaObjectRecursively(
374 binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
375 if (error.isValid())
376 return error;
377 }
378
379 QQmlError noError;
380 return noError;
381}
382
383template <typename ObjectContainer>
385{
386 if (context.instantiatingProperty) {
387 return context.instantiatingPropertyCache();
388 } else if (obj->inheritedTypeNameIndex != 0) {
389 auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
390 Q_ASSERT(typeRef);
391
392 if (typeRef->isFullyDynamicType()) {
393 if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
394 *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
395 return nullptr;
396 }
397 if (obj->signalCount() > 0) {
398 *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
399 return nullptr;
400 }
401 if (obj->functionCount() > 0) {
402 *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
403 return nullptr;
404 }
405 }
406
407 if (QQmlPropertyCache::ConstPtr propertyCache = typeRef->createPropertyCache())
408 return propertyCache;
410 obj->location,
411 QQmlPropertyCacheCreatorBase::tr("Type '%1' cannot declare new members.")
412 .arg(stringAt(obj->inheritedTypeNameIndex)));
413 return nullptr;
414 } else if (const QV4::CompiledData::Binding *binding = context.instantiatingBinding) {
415 if (binding->isAttachedProperty()) {
416 auto *typeRef = objectContainer->resolvedType(
417 binding->propertyNameIndex);
418 Q_ASSERT(typeRef);
419 QQmlType qmltype = typeRef->type();
420 if (!qmltype.isValid()) {
421 imports->resolveType(
422 QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex),
423 &qmltype, nullptr, nullptr);
424 }
425
426 const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
427 if (!attachedMo) {
428 *error = qQmlCompileError(binding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
429 return nullptr;
430 }
431 return QQmlMetaType::propertyCache(attachedMo);
432 }
433 }
434 return nullptr;
435}
436
437template <typename ObjectContainer>
439 int objectIndex, const CompiledObject *obj,
440 const QQmlPropertyCache::ConstPtr &baseTypeCache)
441{
442 QQmlPropertyCache::Ptr cache = baseTypeCache->copyAndReserve(
443 obj->propertyCount() + obj->aliasCount(),
444 obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(),
445 obj->signalCount() + obj->propertyCount() + obj->aliasCount(),
446 obj->enumCount());
447
448 propertyCaches->setOwn(objectIndex, cache);
449 propertyCaches->setNeedsVMEMetaObject(objectIndex);
450
451 QByteArray newClassName;
452
453 if (objectIndex == /*root object*/0 || int(currentRoot) == objectIndex) {
454 newClassName = typeClassName;
455 }
456 if (newClassName.isEmpty()) {
457 newClassName = QQmlMetaObject(baseTypeCache).className();
458 newClassName.append("_QML_");
459 newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
460 }
461
462 cache->_dynamicClassName = newClassName;
463
464 using ListPropertyAssignBehavior = typename ObjectContainer::ListPropertyAssignBehavior;
465 switch (objectContainer->listPropertyAssignBehavior()) {
466 case ListPropertyAssignBehavior::ReplaceIfNotDefault:
467 cache->_listPropertyAssignBehavior = "ReplaceIfNotDefault";
468 break;
469 case ListPropertyAssignBehavior::Replace:
470 cache->_listPropertyAssignBehavior = "Replace";
471 break;
472 case ListPropertyAssignBehavior::Append:
473 break;
474 }
475
476 QQmlPropertyResolver resolver(baseTypeCache);
477
478 auto p = obj->propertiesBegin();
479 auto pend = obj->propertiesEnd();
480 for ( ; p != pend; ++p) {
481 bool notInRevision = false;
482 const QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
483 if (d && d->isFinal())
484 return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
485 }
486
487 auto a = obj->aliasesBegin();
488 auto aend = obj->aliasesEnd();
489 for ( ; a != aend; ++a) {
490 bool notInRevision = false;
491 const QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex()), &notInRevision);
492 if (d && d->isFinal())
493 return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
494 }
495
496 int effectivePropertyIndex = cache->propertyIndexCacheStart;
497 int effectiveMethodIndex = cache->methodIndexCacheStart;
498
499 // For property change signal override detection.
500 // We prepopulate a set of signal names which already exist in the object,
501 // and throw an error if there is a signal/method defined as an override.
502 // TODO: Remove AllowOverride once we can. No override should be allowed.
503 enum class AllowOverride { No, Yes };
504 QHash<QString, AllowOverride> seenSignals {
505 { QStringLiteral("destroyed"), AllowOverride::No },
506 { QStringLiteral("parentChanged"), AllowOverride::No },
507 { QStringLiteral("objectNameChanged"), AllowOverride::No }
508 };
509 const QQmlPropertyCache *parentCache = cache.data();
510 while ((parentCache = parentCache->parent().data())) {
511 if (int pSigCount = parentCache->signalCount()) {
512 int pSigOffset = parentCache->signalOffset();
513 for (int i = pSigOffset; i < pSigCount; ++i) {
514 const QQmlPropertyData *currPSig = parentCache->signal(i);
515 // XXX TODO: find a better way to get signal name from the property data :-/
516 for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
517 iter != parentCache->stringCache.end(); ++iter) {
518 if (currPSig == (*iter).second) {
519 if (currPSig->isOverridableSignal()) {
520 const qsizetype oldSize = seenSignals.size();
521 AllowOverride &entry = seenSignals[iter.key()];
522 if (seenSignals.size() != oldSize)
523 entry = AllowOverride::Yes;
524 } else {
525 seenSignals[iter.key()] = AllowOverride::No;
526 }
527
528 break;
529 }
530 }
531 }
532 }
533 }
534
535 // Set up notify signals for properties - first normal, then alias
536 p = obj->propertiesBegin();
537 pend = obj->propertiesEnd();
538 for ( ; p != pend; ++p) {
540
541 const QString changedSigName =
543 seenSignals[changedSigName] = AllowOverride::No;
544
545 cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
546 }
547
548 a = obj->aliasesBegin();
549 aend = obj->aliasesEnd();
550 for ( ; a != aend; ++a) {
552
553 const QString changedSigName =
555 seenSignals[changedSigName] = AllowOverride::No;
556
557 cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
558 }
559
560 auto e = obj->enumsBegin();
561 auto eend = obj->enumsEnd();
562 for ( ; e != eend; ++e) {
563 const int enumValueCount = e->enumValueCount();
564 QVector<QQmlEnumValue> values;
565 values.reserve(enumValueCount);
566
567 auto enumValue = e->enumValuesBegin();
568 auto end = e->enumValuesEnd();
569 for ( ; enumValue != end; ++enumValue)
570 values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value));
571
572 cache->appendEnum(stringAt(e->nameIndex), values);
573 }
574
575 // Dynamic signals
576 auto s = obj->signalsBegin();
577 auto send = obj->signalsEnd();
578 for ( ; s != send; ++s) {
579 const int paramCount = s->parameterCount();
580
581 QList<QByteArray> names;
582 names.reserve(paramCount);
583 QVarLengthArray<QMetaType, 10> paramTypes(paramCount);
584
585 if (paramCount) {
586
587 int i = 0;
588 auto param = s->parametersBegin();
589 auto end = s->parametersEnd();
590 for ( ; param != end; ++param, ++i) {
591 names.append(stringAt(param->nameIndex).toUtf8());
592
593 QString customTypeName;
594 QMetaType type = metaTypeForParameter(param->type, &customTypeName);
595 if (!type.isValid())
596 return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
597
598 paramTypes[i] = type;
599 }
600 }
601
603 if (paramCount)
604 flags.setHasArguments(true);
605
606 QString signalName = stringAt(s->nameIndex);
607 const auto it = seenSignals.find(signalName);
608 if (it == seenSignals.end()) {
609 seenSignals[signalName] = AllowOverride::No;
610 } else {
611 // TODO: Remove the AllowOverride::Yes branch once we can.
613 s->location,
614 QQmlPropertyCacheCreatorBase::tr(
615 "Duplicate signal name: "
616 "invalid override of property change signal or superclass signal"));
617 switch (*it) {
618 case AllowOverride::No:
619 return message;
620 case AllowOverride::Yes:
621 message.setUrl(objectContainer->url());
622 enginePrivate->warning(message);
623 *it = AllowOverride::No; // No further overriding allowed.
624 break;
625 }
626 }
627 cache->appendSignal(signalName, flags, effectiveMethodIndex++,
628 paramCount?paramTypes.constData():nullptr, names);
629 }
630
631
632 // Dynamic slots
633 auto function = objectContainer->objectFunctionsBegin(obj);
634 auto fend = objectContainer->objectFunctionsEnd(obj);
635 for ( ; function != fend; ++function) {
637
638 const QString slotName = stringAt(function->nameIndex);
639 const auto it = seenSignals.constFind(slotName);
640 if (it != seenSignals.constEnd()) {
641 // TODO: Remove the AllowOverride::Yes branch once we can.
643 function->location,
644 QQmlPropertyCacheCreatorBase::tr(
645 "Duplicate method name: "
646 "invalid override of property change signal or superclass signal"));
647 switch (*it) {
648 case AllowOverride::No:
649 return message;
650 case AllowOverride::Yes:
651 message.setUrl(objectContainer->url());
652 enginePrivate->warning(message);
653 break;
654 }
655 }
656 // Note: we don't append slotName to the seenSignals list, since we don't
657 // protect against overriding change signals or methods with properties.
658
659 QList<QByteArray> parameterNames;
660 QVector<QMetaType> parameterTypes;
661 auto formal = function->formalsBegin();
662 auto end = function->formalsEnd();
663 for ( ; formal != end; ++formal) {
664 flags.setHasArguments(true);
665 parameterNames << stringAt(formal->nameIndex).toUtf8();
666 QMetaType type = metaTypeForParameter(formal->type);
667 if (!type.isValid())
668 type = QMetaType::fromType<QVariant>();
669 parameterTypes << type;
670 }
671
672 QMetaType returnType = metaTypeForParameter(function->returnType);
673 if (!returnType.isValid())
674 returnType = QMetaType::fromType<QVariant>();
675
676 cache->appendMethod(slotName, flags, effectiveMethodIndex++, returnType, parameterNames, parameterTypes);
677 }
678
679
680 // Dynamic properties
681 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
682 int propertyIdx = 0;
683 p = obj->propertiesBegin();
684 pend = obj->propertiesEnd();
685 for ( ; p != pend; ++p, ++propertyIdx) {
686 QMetaType propertyType;
687 QTypeRevision propertyTypeVersion = QTypeRevision::zero();
688 QQmlPropertyData::Flags propertyFlags;
689
690 const QV4::CompiledData::CommonType type = p->commonType();
691
692 if (p->isList())
693 propertyFlags.setType(QQmlPropertyData::Flags::QListType);
695 propertyFlags.setType(QQmlPropertyData::Flags::VarPropertyType);
696
698 propertyType = p->isList()
699 ? listTypeForPropertyType(type)
700 : metaTypeForPropertyType(type);
701 } else {
702 Q_ASSERT(!p->isCommonType());
703
704 QQmlType qmltype;
705 bool selfReference = false;
706 if (!imports->resolveType(
707 QQmlTypeLoader::get(enginePrivate),
708 stringAt(p->commonTypeOrTypeNameIndex()), &qmltype, nullptr, nullptr,
709 nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
710 return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
711 }
712
713 // inline components are not necessarily valid yet
714 Q_ASSERT(qmltype.isValid());
715 if (qmltype.isComposite() || qmltype.isInlineComponentType()) {
716 QQmlType compositeType;
717 if (qmltype.isInlineComponentType()) {
718 compositeType = qmltype;
719 Q_ASSERT(compositeType.isValid());
720 } else if (selfReference) {
721 compositeType = objectContainer->qmlTypeForComponent();
722 } else {
723 // compositeType may not be the same type as qmlType because multiple engines
724 // may load different types for the same document. Therefore we have to ask
725 // our engine's type loader here.
726 QQmlRefPointer<QQmlTypeData> tdata
727 = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
728 Q_ASSERT(tdata);
729 Q_ASSERT(tdata->isComplete());
730 compositeType = tdata->compilationUnit()->qmlTypeForComponent();
731 }
732
733 if (p->isList()) {
734 propertyType = compositeType.qListTypeId();
735 } else {
736 propertyType = compositeType.typeId();
737 }
738 } else {
739 if (p->isList())
740 propertyType = qmltype.qListTypeId();
741 else
742 propertyType = qmltype.typeId();
743 propertyTypeVersion = qmltype.version();
744 }
745
746 if (p->isList())
747 propertyFlags.setType(QQmlPropertyData::Flags::QListType);
748 else if (propertyType.flags().testFlag(QMetaType::PointerToQObject))
749 propertyFlags.setType(QQmlPropertyData::Flags::QObjectDerivedType);
750 }
751
752 if (!p->isReadOnly() && !propertyType.flags().testFlag(QMetaType::IsQmlList))
753 propertyFlags.setIsWritable(true);
754
755
756 QString propertyName = stringAt(p->nameIndex);
757 if (!obj->hasAliasAsDefaultProperty() && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
758 cache->_defaultPropertyName = propertyName;
759 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
760 propertyType, propertyTypeVersion, effectiveSignalIndex);
761
762 effectiveSignalIndex++;
763 }
764
765 QQmlError noError;
766 return noError;
767}
768
769template <typename ObjectContainer>
771 const QV4::CompiledData::ParameterType &param, QString *customTypeName)
772{
773 const quint32 typeId = param.typeNameIndexOrCommonType();
774 if (param.indexIsCommonType()) {
775 // built-in type
776 if (param.isList())
777 return listTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
778 return metaTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
779 }
780
781 // lazily resolved type
782 const QString typeName = stringAt(param.typeNameIndexOrCommonType());
783 if (customTypeName)
784 *customTypeName = typeName;
785 QQmlType qmltype;
786 bool selfReference = false;
787 if (!imports->resolveType(
788 &enginePrivate->typeLoader, typeName, &qmltype, nullptr, nullptr, nullptr,
789 QQmlType::AnyRegistrationType, &selfReference))
790 return QMetaType();
791
792 if (!qmltype.isComposite()) {
793 const QMetaType typeId = param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
794 if (!typeId.isValid() && qmltype.isInlineComponentType()) {
795 const QQmlType qmlType = objectContainer->qmlTypeForComponent(qmltype.elementName());
796 return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
797 } else {
798 return typeId;
799 }
800 }
801
802 if (selfReference) {
803 const QQmlType qmlType = objectContainer->qmlTypeForComponent();
804 return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
805 }
806
807 return param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
808}
809
810template <typename ObjectContainer, typename CompiledObject>
811int objectForId(const ObjectContainer *objectContainer, const CompiledObject &component, int id)
812{
813 for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
814 const int candidateIndex = component.namedObjectsInComponentTable()[i];
815 const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
816 if (candidate.objectId() == id)
817 return candidateIndex;
818 }
819 return -1;
820}
821
822template <typename ObjectContainer>
824{
825public:
826 typedef typename ObjectContainer::CompiledObject CompiledObject;
827
829 QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
830 QQmlError appendAliasesToPropertyCache(
831 const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
832
833private:
834 QQmlError propertyDataForAlias(
836 QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
837 QQmlEnginePrivate *enginePriv);
838
839 QQmlPropertyCacheVector *propertyCaches;
840 const ObjectContainer *objectContainer;
841};
842
843template <typename ObjectContainer>
845 QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
846 : propertyCaches(propertyCaches)
847 , objectContainer(objectContainer)
848{
849}
850
851template <typename ObjectContainer>
853 const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
854 QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
855 QQmlEnginePrivate *enginePriv)
856{
857 *type = QMetaType();
858 bool writable = false;
859 bool resettable = false;
860 bool bindable = false;
861
862 propertyFlags->setIsAlias(true);
863
864 if (alias.isAliasToLocalAlias()) {
865 const QV4::CompiledData::Alias *lastAlias = &alias;
866 QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
867
868 do {
869 const int targetObjectIndex = objectForId(
870 objectContainer, component, lastAlias->targetObjectId());
871 Q_ASSERT(targetObjectIndex >= 0);
872 const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
873 Q_ASSERT(targetObject);
874
875 auto nextAlias = targetObject->aliasesBegin();
876 for (uint i = 0; i < lastAlias->localAliasIndex; ++i)
877 ++nextAlias;
878
879 const QV4::CompiledData::Alias *targetAlias = &(*nextAlias);
880 if (seenAliases.contains(targetAlias)) {
881 return qQmlCompileError(targetAlias->location,
882 QQmlPropertyCacheCreatorBase::tr("Cyclic alias"));
883 }
884
885 seenAliases.append(targetAlias);
886 lastAlias = targetAlias;
887 } while (lastAlias->isAliasToLocalAlias());
888
889 return propertyDataForAlias(
890 component, *lastAlias, type, version, propertyFlags, enginePriv);
891 }
892
893 const int targetObjectIndex = objectForId(objectContainer, component, alias.targetObjectId());
894 Q_ASSERT(targetObjectIndex >= 0);
895 const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
896
897 if (alias.encodedMetaPropertyIndex == -1) {
899 auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
900 if (!typeRef) {
901 // Can be caused by the alias target not being a valid id or property. E.g.:
902 // property alias dataValue: dataVal
903 // invalidAliasComponent { id: dataVal }
904 return qQmlCompileError(targetObject.location,
905 QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
906 }
907
908 const auto referencedType = typeRef->type();
909 if (referencedType.isValid()) {
910 *type = referencedType.typeId();
911 if (!type->isValid() && referencedType.isInlineComponentType()) {
912 *type = objectContainer->qmlTypeForComponent(referencedType.elementName()).typeId();
913 Q_ASSERT(type->isValid());
914 }
915 } else {
916 *type = typeRef->compilationUnit()->metaType();
917 }
918
919 *version = typeRef->version();
920
921 propertyFlags->setType(QQmlPropertyData::Flags::QObjectDerivedType);
922 } else {
923 int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
924 int valueTypeIndex = QQmlPropertyIndex::fromEncoded(
925 alias.encodedMetaPropertyIndex).valueTypeIndex();
926
927 QQmlPropertyCache::ConstPtr targetCache = propertyCaches->at(targetObjectIndex);
928 Q_ASSERT(targetCache);
929
930 const QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
931 Q_ASSERT(targetProperty);
932
933 const QMetaType targetPropType = targetProperty->propType();
934
935 const auto populateWithPropertyData = [&](const QQmlPropertyData *property) {
936 *type = property->propType();
937 writable = property->isWritable();
938 resettable = property->isResettable();
939 bindable = property->isBindable();
940
941 if (property->isVarProperty())
942 propertyFlags->setType(QQmlPropertyData::Flags::QVariantType);
943 else
944 propertyFlags->copyPropertyTypeFlags(property->flags());
945 };
946
947 // for deep aliases, valueTypeIndex is always set
948 if (!QQmlMetaType::isValueType(targetPropType) && valueTypeIndex != -1) {
949 // deep alias property
950
952 = QQmlMetaType::propertyCacheForType(targetPropType);
953
954 if (!typeCache) {
955 // See if it's a half-resolved composite type
956 if (const QV4::ResolvedTypeReference *typeRef
957 = objectContainer->resolvedType(targetPropType)) {
958 typeCache = typeRef->typePropertyCache();
959 }
960 }
961
962 const QQmlPropertyData *typeProperty = typeCache
963 ? typeCache->property(valueTypeIndex)
964 : nullptr;
965 if (typeProperty == nullptr) {
966 return qQmlCompileError(
967 alias.referenceLocation,
968 QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
969 }
970 populateWithPropertyData(typeProperty);
971 } else {
972 // value type or primitive type or enum
973 populateWithPropertyData(targetProperty);
974
975 if (valueTypeIndex != -1) {
976 const QMetaObject *valueTypeMetaObject
978 const QMetaProperty valueTypeMetaProperty
979 = valueTypeMetaObject->property(valueTypeIndex);
980 *type = valueTypeMetaProperty.metaType();
981
982 // We can only write or reset the value type property if we can write
983 // the value type itself.
984 resettable = writable && valueTypeMetaProperty.isResettable();
985 writable = writable && valueTypeMetaProperty.isWritable();
986
987 bindable = valueTypeMetaProperty.isBindable();
988 }
989 }
990 }
991
992 propertyFlags->setIsWritable(
994 propertyFlags->setIsResettable(resettable);
995 propertyFlags->setIsBindable(bindable);
996 return QQmlError();
997}
998
999template <typename ObjectContainer>
1001 const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv)
1002{
1003 const CompiledObject &object = *objectContainer->objectAt(objectIndex);
1004 if (!object.aliasCount())
1005 return QQmlError();
1006
1007 QQmlPropertyCache::Ptr propertyCache = propertyCaches->ownAt(objectIndex);
1008 Q_ASSERT(propertyCache);
1009
1010 int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.size();
1011 int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.size();
1012
1013 int aliasIndex = 0;
1014 auto alias = object.aliasesBegin();
1015 auto end = object.aliasesEnd();
1016 for ( ; alias != end; ++alias, ++aliasIndex) {
1018
1021 QQmlPropertyData::Flags propertyFlags;
1022 QQmlError error = propertyDataForAlias(component, *alias, &type, &version,
1023 &propertyFlags, enginePriv);
1024 if (error.isValid())
1025 return error;
1026
1027 const QString propertyName = objectContainer->stringAt(alias->nameIndex());
1028
1029 if (object.hasAliasAsDefaultProperty() && aliasIndex == object.indexOfDefaultPropertyOrAlias)
1030 propertyCache->_defaultPropertyName = propertyName;
1031
1032 propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
1033 type, version, effectiveSignalIndex++);
1034 }
1035
1036 return QQmlError();
1037}
1038
1040
1041#endif // QQMLPROPERTYCACHECREATOR_P_H
\inmodule QtCore
Definition qatomic.h:112
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
typename QLinkedStringHash< QPair< int, QQmlPropertyData * > >::ConstIterator ConstIterator
Definition qlist.h:75
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
QMetaType metaType() const
\inmodule QtCore
Definition qmetatype.h:341
constexpr TypeFlags flags() const
Definition qmetatype.h:2658
bool isValid() const
@ PointerToQObject
Definition qmetatype.h:406
void warning(const QQmlError &)
QQmlTypeLoader typeLoader
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:18
void setDescription(const QString &)
Sets the error description.
The QQmlImports class encapsulates one QML document's import statements.
bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return, QTypeRevision *version_return, QQmlImportNamespace **ns_return, QList< QQmlError > *errors=nullptr, QQmlType::RegistrationType registrationType=QQmlType::AnyRegistrationType, bool *typeRecursionDetected=nullptr) const
const char * className() const
static const QMetaObject * metaObjectForValueType(QMetaType type)
static QQmlPropertyCache::ConstPtr propertyCache(QObject *object, QTypeRevision version=QTypeRevision())
Returns a QQmlPropertyCache for obj if one is available.
static bool isValueType(QMetaType type)
static QQmlPropertyCache::ConstPtr propertyCacheForType(QMetaType metaType)
QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
QQmlError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv)
ObjectContainer::CompiledObject CompiledObject
QMetaType metaTypeForParameter(const QV4::CompiledData::ParameterType &param, QString *customTypeName=nullptr)
IncrementalResult buildMetaObjectsIncrementally()
typename ObjectContainer::CompiledObject CompiledObject
QString stringAt(int index) const
QQmlEnginePrivate *const enginePrivate
QQmlBindingInstantiationContext m_context
QQmlPendingGroupPropertyBindings * pendingGroupPropertyBindings
QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &baseTypeCache)
typename std::remove_reference< decltype(*(std::declval< CompiledObject >().inlineComponentsBegin()))>::type InlineComponent
const ObjectContainer *const objectContainer
std::vector< icutils::Node >::reverse_iterator nodeIt
QQmlError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports, const QByteArray &typeClassName)
std::vector< InlineComponent > allICs
QQmlPropertyCacheVector * propertyCaches
std::vector< icutils::Node > nodesSorted
QQmlPropertyCache::ConstPtr propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const
bool needsVMEMetaObject(int index) const
QQmlPropertyCache::ConstPtr at(int index) const
void set(int index, const QQmlPropertyCache::ConstPtr &replacement)
void setOwn(int index, const QQmlPropertyCache::Ptr &replacement)
QQmlPropertyCache::Ptr ownAt(int index) const
void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex, QMetaType propType, QTypeRevision revision, int notifyIndex)
QMetaType propType() const
static Flags defaultSlotFlags()
static Flags defaultSignalFlags()
static QQmlPropertyIndex fromEncoded(qint32 encodedIndex)
bool isNull() const
static QString propertyNameToChangedSignalName(QStringView property)
QQmlRefPointer< QQmlTypeData > getType(const QUrl &unNormalizedUrl, Mode mode=PreferSynchronous)
Returns a QQmlTypeData for the specified url.
static QQmlTypeLoader * get(Engine *engine)
@ AnyRegistrationType
Definition qqmltype_p.h:167
QMetaType typeId() const
Definition qqmltype.cpp:668
QMetaType qListTypeId() const
Definition qqmltype.cpp:673
iterator end()
Definition qset.h:140
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
iterator find(const T &value)
Definition qset.h:159
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
QString & append(QChar c)
Definition qstring.cpp:3252
\inmodule QtCore
static constexpr QTypeRevision zero()
Produces a QTypeRevision with major and minor version {0}.
\inmodule QtCore
Definition qurl.h:94
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2497
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
std::vector< std::vector< Node * > > AdjacencyList
static void * context
#define Q_DECLARE_TR_FUNCTIONS(context)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
const char * typeName
GLint location
GLenum GLsizei GLsizei GLint * values
[15]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLuint GLuint end
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLenum type
GLbitfield flags
GLuint GLsizei const GLchar * message
GLenum const GLint * param
GLuint name
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLuint * names
GLuint entry
GLfloat GLfloat p
[1]
static qreal component(const QPointF &point, unsigned int i)
QT_BEGIN_NAMESPACE QQmlError qQmlCompileError(const QV4::CompiledData::Location &location, const QString &description)
int objectForId(const ObjectContainer *objectContainer, const CompiledObject &component, int id)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_AUTOTEST_EXPORT
unsigned int quint32
Definition qtypes.h:50
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
const char property[13]
Definition qwizard.cpp:101
QUrl url("example.com")
[constructor-url-reference]
QUrl baseUrl
\inmodule QtCore
QQmlPropertyCache::ConstPtr referencingObjectPropertyCache
const QV4::CompiledData::Binding * instantiatingBinding
const QQmlPropertyData * instantiatingProperty
QQmlPropertyCache::ConstPtr instantiatingPropertyCache() const
void resolveMissingPropertyCaches(QQmlPropertyCacheVector *propertyCaches) const
static bool canCreateClassNameTypeByUrl(const QUrl &url)
static QMetaType listTypeForPropertyType(QV4::CompiledData::CommonType type)
static QByteArray createClassNameTypeByUrl(const QUrl &url)
static QMetaType metaTypeForPropertyType(QV4::CompiledData::CommonType type)
static QAtomicInt Q_AUTOTEST_EXPORT classIndexCounter
static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, const QString &name)
const QQmlPropertyData * property(int index) const
bool hasFlag(Flag flag) const