aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlpropertycachecreator_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlpropertycachecreator_p.h')
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h1041
1 files changed, 1041 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
new file mode 100644
index 0000000000..4d49ca6ed4
--- /dev/null
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -0,0 +1,1041 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QQMLPROPERTYCACHECREATOR_P_H
+#define QQMLPROPERTYCACHECREATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmlvaluetype_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlmetaobject_p.h>
+#include <private/qqmlpropertyresolver_p.h>
+#include <private/qqmltypedata_p.h>
+#include <private/inlinecomponentutils_p.h>
+#include <private/qqmlsourcecoordinate_p.h>
+#include <private/qqmlsignalnames_p.h>
+
+#include <QScopedValueRollback>
+
+#if QT_CONFIG(regularexpression)
+#include <QtCore/qregularexpression.h>
+#endif
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+inline QQmlError qQmlCompileError(const QV4::CompiledData::Location &location,
+ const QString &description)
+{
+ QQmlError error;
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
+ error.setDescription(description);
+ return error;
+}
+
+struct QQmlBindingInstantiationContext {
+ QQmlBindingInstantiationContext() {}
+ QQmlBindingInstantiationContext(
+ int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding,
+ const QString &instantiatingPropertyName,
+ const QQmlPropertyCache::ConstPtr &referencingObjectPropertyCache);
+
+ bool resolveInstantiatingProperty();
+ QQmlPropertyCache::ConstPtr instantiatingPropertyCache() const;
+
+ int referencingObjectIndex = -1;
+ const QV4::CompiledData::Binding *instantiatingBinding = nullptr;
+ QString instantiatingPropertyName;
+ QQmlPropertyCache::ConstPtr referencingObjectPropertyCache;
+ const QQmlPropertyData *instantiatingProperty = nullptr;
+};
+
+struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext>
+{
+ void resolveMissingPropertyCaches(
+ QQmlPropertyCacheVector *propertyCaches) const;
+};
+
+struct QQmlPropertyCacheCreatorBase
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase)
+public:
+ static QAtomicInt Q_AUTOTEST_EXPORT classIndexCounter;
+
+ static QMetaType metaTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QVariant>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<int>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<bool>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<qreal>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QString>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QUrl>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QTime>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QDate>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QDateTime>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QRegularExpression>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QRectF>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QPointF>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QSizeF>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
+
+ static QMetaType listTypeForPropertyType(QV4::CompiledData::CommonType type)
+ {
+ switch (type) {
+ case QV4::CompiledData::CommonType::Void: return QMetaType();
+ case QV4::CompiledData::CommonType::Var: return QMetaType::fromType<QList<QVariant>>();
+ case QV4::CompiledData::CommonType::Int: return QMetaType::fromType<QList<int>>();
+ case QV4::CompiledData::CommonType::Bool: return QMetaType::fromType<QList<bool>>();
+ case QV4::CompiledData::CommonType::Real: return QMetaType::fromType<QList<qreal>>();
+ case QV4::CompiledData::CommonType::String: return QMetaType::fromType<QList<QString>>();
+ case QV4::CompiledData::CommonType::Url: return QMetaType::fromType<QList<QUrl>>();
+ case QV4::CompiledData::CommonType::Time: return QMetaType::fromType<QList<QTime>>();
+ case QV4::CompiledData::CommonType::Date: return QMetaType::fromType<QList<QDate>>();
+ case QV4::CompiledData::CommonType::DateTime: return QMetaType::fromType<QList<QDateTime>>();
+#if QT_CONFIG(regularexpression)
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType::fromType<QList<QRegularExpression>>();
+#else
+ case QV4::CompiledData::CommonType::RegExp: return QMetaType();
+#endif
+ case QV4::CompiledData::CommonType::Rect: return QMetaType::fromType<QList<QRectF>>();
+ case QV4::CompiledData::CommonType::Point: return QMetaType::fromType<QList<QPointF>>();
+ case QV4::CompiledData::CommonType::Size: return QMetaType::fromType<QList<QSizeF>>();
+ case QV4::CompiledData::CommonType::Invalid: break;
+ };
+ return QMetaType {};
+ }
+
+ static bool canCreateClassNameTypeByUrl(const QUrl &url);
+ static QByteArray createClassNameTypeByUrl(const QUrl &url);
+
+ static QByteArray createClassNameForInlineComponent(const QUrl &baseUrl, const QString &name);
+
+ struct IncrementalResult {
+ // valid if and only if an error occurred
+ QQmlError error;
+ // true if there was no error and there are still components left to process
+ bool canResume = false;
+ // the object index of the last processed (inline) component root.
+ int processedRoot = 0;
+ };
+};
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase
+{
+public:
+ using CompiledObject = typename ObjectContainer::CompiledObject;
+ using InlineComponent = typename std::remove_reference<decltype (*(std::declval<CompiledObject>().inlineComponentsBegin()))>::type;
+
+ QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
+ QQmlEnginePrivate *enginePrivate,
+ const ObjectContainer *objectContainer, const QQmlImports *imports,
+ const QByteArray &typeClassName);
+ ~QQmlPropertyCacheCreator() { propertyCaches->seal(); }
+
+
+ /*!
+ \internal
+ Creates the property cache for the CompiledObjects of objectContainer,
+ one (inline) root component at a time.
+
+ \note Later compiler passes might modify those property caches. Therefore,
+ the actual metaobjects are not created yet.
+ */
+ IncrementalResult buildMetaObjectsIncrementally();
+
+ /*!
+ \internal
+ Returns a valid error if the inline components of the objectContainer
+ form a cycle. Otherwise an invalid error is returned
+ */
+ QQmlError verifyNoICCycle();
+
+ enum class VMEMetaObjectIsRequired {
+ Maybe,
+ Always
+ };
+protected:
+ QQmlError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired);
+ QQmlPropertyCache::ConstPtr propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const;
+ QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &baseTypeCache);
+
+ QMetaType metaTypeForParameter(const QV4::CompiledData::ParameterType &param, QString *customTypeName = nullptr);
+
+ QString stringAt(int index) const { return objectContainer->stringAt(index); }
+
+ QQmlEnginePrivate * const enginePrivate;
+ const ObjectContainer * const objectContainer;
+ const QQmlImports * const imports;
+ QQmlPropertyCacheVector *propertyCaches;
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings;
+ QByteArray typeClassName; // not const as we temporarily chang it for inline components
+ unsigned int currentRoot; // set to objectID of inline component root when handling inline components
+
+ QQmlBindingInstantiationContext m_context;
+ std::vector<InlineComponent> allICs;
+ std::vector<icutils::Node> nodesSorted;
+ std::vector<icutils::Node>::reverse_iterator nodeIt = nodesSorted.rbegin();
+ bool hasCycle = false;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
+ QQmlEnginePrivate *enginePrivate,
+ const ObjectContainer *objectContainer, const QQmlImports *imports,
+ const QByteArray &typeClassName)
+ : enginePrivate(enginePrivate)
+ , objectContainer(objectContainer)
+ , imports(imports)
+ , propertyCaches(propertyCaches)
+ , pendingGroupPropertyBindings(pendingGroupPropertyBindings)
+ , typeClassName(typeClassName)
+ , currentRoot(-1)
+{
+ propertyCaches->resetAndResize(objectContainer->objectCount());
+
+ using namespace icutils;
+
+ // get a list of all inline components
+
+ for (int i=0; i != objectContainer->objectCount(); ++i) {
+ const CompiledObject *obj = objectContainer->objectAt(i);
+ for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
+ allICs.push_back(*it);
+ }
+ }
+
+ // create a graph on inline components referencing inline components
+ std::vector<icutils::Node> nodes;
+ nodes.resize(allICs.size());
+ std::iota(nodes.begin(), nodes.end(), 0);
+ AdjacencyList adjacencyList;
+ adjacencyList.resize(nodes.size());
+ fillAdjacencyListForInlineComponents(objectContainer, adjacencyList, nodes, allICs);
+
+ nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
+ nodeIt = nodesSorted.rbegin();
+}
+
+template <typename ObjectContainer>
+inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::verifyNoICCycle()
+{
+ if (hasCycle) {
+ QQmlError diag;
+ diag.setDescription(QLatin1String("Inline components form a cycle!"));
+ return diag;
+ }
+ return {};
+}
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheCreatorBase::IncrementalResult
+QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectsIncrementally()
+{
+ // needs to be checked with verifyNoICCycle before this function is called
+ Q_ASSERT(!hasCycle);
+
+ // create meta objects for inline components before compiling actual root component
+ if (nodeIt != nodesSorted.rend()) {
+ const auto &ic = allICs[nodeIt->index()];
+ QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex);
+ Q_ASSERT(propertyCaches->at(ic.objectIndex).isNull());
+ Q_ASSERT(typeRef->typePropertyCache().isNull()); // not set yet
+
+ QByteArray icTypeName { objectContainer->stringAt(ic.nameIndex).toUtf8() };
+ QScopedValueRollback<QByteArray> nameChange {typeClassName, icTypeName};
+ QScopedValueRollback<unsigned int> rootChange {currentRoot, ic.objectIndex};
+ ++nodeIt;
+ QQmlError diag = buildMetaObjectRecursively(ic.objectIndex, m_context, VMEMetaObjectIsRequired::Always);
+ if (diag.isValid()) {
+ return {diag, false, 0};
+ }
+ typeRef->setTypePropertyCache(propertyCaches->at(ic.objectIndex));
+ Q_ASSERT(!typeRef->typePropertyCache().isNull());
+ return { QQmlError(), true, int(ic.objectIndex) };
+ }
+
+ auto diag = buildMetaObjectRecursively(/*root object*/0, m_context, VMEMetaObjectIsRequired::Maybe);
+ return {diag, false, 0};
+}
+
+template <typename ObjectContainer>
+inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
+{
+ auto isAddressable = [](const QUrl &url) {
+ const QString fileName = url.fileName();
+ return !fileName.isEmpty() && fileName.front().isUpper();
+ };
+
+ const CompiledObject *obj = objectContainer->objectAt(objectIndex);
+ bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0
+ || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
+ || ((obj->hasFlag(QV4::CompiledData::Object::IsComponent)
+ || (objectIndex == 0 && isAddressable(objectContainer->url())))
+ && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType());
+
+ if (!needVMEMetaObject) {
+ auto binding = obj->bindingsBegin();
+ auto end = obj->bindingsEnd();
+ for ( ; binding != end; ++binding) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Object
+ && (binding->flags() & QV4::CompiledData::Binding::IsOnAssignment)) {
+ // If the on assignment is inside a group property, we need to distinguish between QObject based
+ // group properties and value type group properties. For the former the base type is derived from
+ // the property that references us, for the latter we only need a meta-object on the referencing object
+ // because interceptors can't go to the shared value type instances.
+ if (context.instantiatingProperty && QQmlMetaType::isValueType(context.instantiatingProperty->propType())) {
+ if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) {
+ const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
+ auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+ QQmlPropertyCache::ConstPtr baseTypeCache = typeRef->createPropertyCache();
+ QQmlError error = baseTypeCache
+ ? createMetaObject(context.referencingObjectIndex, obj, baseTypeCache)
+ : qQmlCompileError(binding->location, QQmlPropertyCacheCreatorBase::tr(
+ "Type cannot be used for 'on' assignment"));
+ if (error.isValid())
+ return error;
+ }
+ } else {
+ // On assignments are implemented using value interceptors, which require a VME meta object.
+ needVMEMetaObject = true;
+ }
+ break;
+ }
+ }
+ }
+
+ QQmlPropertyCache::ConstPtr baseTypeCache;
+ {
+ QQmlError error;
+ baseTypeCache = propertyCacheForObject(obj, context, &error);
+ if (error.isValid())
+ return error;
+ }
+
+ if (baseTypeCache) {
+ if (needVMEMetaObject) {
+ QQmlError error = createMetaObject(objectIndex, obj, baseTypeCache);
+ if (error.isValid())
+ return error;
+ } else {
+ propertyCaches->set(objectIndex, baseTypeCache);
+ }
+ }
+
+ QQmlPropertyCache::ConstPtr thisCache = propertyCaches->at(objectIndex);
+ auto binding = obj->bindingsBegin();
+ auto end = obj->bindingsEnd();
+ for (; binding != end; ++binding) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Object:
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ case QV4::CompiledData::Binding::Type_AttachedProperty:
+ // We can always resolve object, group, and attached properties.
+ break;
+ default:
+ // Everything else is of no interest here.
+ continue;
+ }
+
+ QQmlBindingInstantiationContext context(
+ objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
+
+ // Binding to group property where we failed to look up the type of the
+ // property? Possibly a group property that is an alias that's not resolved yet.
+ // Let's attempt to resolve it after we're done with the aliases and fill in the
+ // propertyCaches entry then.
+ if (!thisCache || !context.resolveInstantiatingProperty())
+ pendingGroupPropertyBindings->append(context);
+
+ QQmlError error = buildMetaObjectRecursively(
+ binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
+ if (error.isValid())
+ return error;
+ }
+
+ QQmlError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const
+{
+ if (context.instantiatingProperty) {
+ return context.instantiatingPropertyCache();
+ } else if (obj->inheritedTypeNameIndex != 0) {
+ auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+
+ if (typeRef->isFullyDynamicType()) {
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
+ *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
+ return nullptr;
+ }
+ if (obj->signalCount() > 0) {
+ *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
+ return nullptr;
+ }
+ if (obj->functionCount() > 0) {
+ *error = qQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
+ return nullptr;
+ }
+ }
+
+ if (QQmlPropertyCache::ConstPtr propertyCache = typeRef->createPropertyCache())
+ return propertyCache;
+ *error = qQmlCompileError(
+ obj->location,
+ QQmlPropertyCacheCreatorBase::tr("Type '%1' cannot declare new members.")
+ .arg(stringAt(obj->inheritedTypeNameIndex)));
+ return nullptr;
+ } else if (const QV4::CompiledData::Binding *binding = context.instantiatingBinding) {
+ if (binding->isAttachedProperty()) {
+ auto *typeRef = objectContainer->resolvedType(
+ binding->propertyNameIndex);
+ Q_ASSERT(typeRef);
+ QQmlType qmltype = typeRef->type();
+ if (!qmltype.isValid()) {
+ imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex),
+ &qmltype, nullptr, nullptr);
+ }
+
+ const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
+ if (!attachedMo) {
+ *error = qQmlCompileError(binding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
+ return nullptr;
+ }
+ return QQmlMetaType::propertyCache(attachedMo);
+ }
+ }
+ return nullptr;
+}
+
+template <typename ObjectContainer>
+inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
+ int objectIndex, const CompiledObject *obj,
+ const QQmlPropertyCache::ConstPtr &baseTypeCache)
+{
+ QQmlPropertyCache::Ptr cache = baseTypeCache->copyAndReserve(
+ obj->propertyCount() + obj->aliasCount(),
+ obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(),
+ obj->signalCount() + obj->propertyCount() + obj->aliasCount(),
+ obj->enumCount());
+
+ propertyCaches->setOwn(objectIndex, cache);
+ propertyCaches->setNeedsVMEMetaObject(objectIndex);
+
+ QByteArray newClassName;
+
+ if (objectIndex == /*root object*/0 || int(currentRoot) == objectIndex) {
+ newClassName = typeClassName;
+ }
+ if (newClassName.isEmpty()) {
+ newClassName = QQmlMetaObject(baseTypeCache).className();
+ newClassName.append("_QML_");
+ newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
+ }
+
+ cache->_dynamicClassName = newClassName;
+
+ using ListPropertyAssignBehavior = typename ObjectContainer::ListPropertyAssignBehavior;
+ switch (objectContainer->listPropertyAssignBehavior()) {
+ case ListPropertyAssignBehavior::ReplaceIfNotDefault:
+ cache->_listPropertyAssignBehavior = "ReplaceIfNotDefault";
+ break;
+ case ListPropertyAssignBehavior::Replace:
+ cache->_listPropertyAssignBehavior = "Replace";
+ break;
+ case ListPropertyAssignBehavior::Append:
+ break;
+ }
+
+ QQmlPropertyResolver resolver(baseTypeCache);
+
+ auto p = obj->propertiesBegin();
+ auto pend = obj->propertiesEnd();
+ for ( ; p != pend; ++p) {
+ bool notInRevision = false;
+ const QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
+ if (d && d->isFinal())
+ return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ auto a = obj->aliasesBegin();
+ auto aend = obj->aliasesEnd();
+ for ( ; a != aend; ++a) {
+ bool notInRevision = false;
+ const QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex()), &notInRevision);
+ if (d && d->isFinal())
+ return qQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ int effectivePropertyIndex = cache->propertyIndexCacheStart;
+ int effectiveMethodIndex = cache->methodIndexCacheStart;
+
+ // For property change signal override detection.
+ // We prepopulate a set of signal names which already exist in the object,
+ // and throw an error if there is a signal/method defined as an override.
+ // TODO: Remove AllowOverride once we can. No override should be allowed.
+ enum class AllowOverride { No, Yes };
+ QHash<QString, AllowOverride> seenSignals {
+ { QStringLiteral("destroyed"), AllowOverride::No },
+ { QStringLiteral("parentChanged"), AllowOverride::No },
+ { QStringLiteral("objectNameChanged"), AllowOverride::No }
+ };
+ const QQmlPropertyCache *parentCache = cache.data();
+ while ((parentCache = parentCache->parent().data())) {
+ if (int pSigCount = parentCache->signalCount()) {
+ int pSigOffset = parentCache->signalOffset();
+ for (int i = pSigOffset; i < pSigCount; ++i) {
+ const QQmlPropertyData *currPSig = parentCache->signal(i);
+ // XXX TODO: find a better way to get signal name from the property data :-/
+ for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
+ iter != parentCache->stringCache.end(); ++iter) {
+ if (currPSig == (*iter).second) {
+ if (currPSig->isOverridableSignal()) {
+ const qsizetype oldSize = seenSignals.size();
+ AllowOverride &entry = seenSignals[iter.key()];
+ if (seenSignals.size() != oldSize)
+ entry = AllowOverride::Yes;
+ } else {
+ seenSignals[iter.key()] = AllowOverride::No;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Set up notify signals for properties - first normal, then alias
+ p = obj->propertiesBegin();
+ pend = obj->propertiesEnd();
+ for ( ; p != pend; ++p) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ const QString changedSigName =
+ QQmlSignalNames::propertyNameToChangedSignalName(stringAt(p->nameIndex));
+ seenSignals[changedSigName] = AllowOverride::No;
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ a = obj->aliasesBegin();
+ aend = obj->aliasesEnd();
+ for ( ; a != aend; ++a) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ const QString changedSigName =
+ QQmlSignalNames::propertyNameToChangedSignalName(stringAt(a->nameIndex()));
+ seenSignals[changedSigName] = AllowOverride::No;
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ auto e = obj->enumsBegin();
+ auto eend = obj->enumsEnd();
+ for ( ; e != eend; ++e) {
+ const int enumValueCount = e->enumValueCount();
+ QVector<QQmlEnumValue> values;
+ values.reserve(enumValueCount);
+
+ auto enumValue = e->enumValuesBegin();
+ auto end = e->enumValuesEnd();
+ for ( ; enumValue != end; ++enumValue)
+ values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value));
+
+ cache->appendEnum(stringAt(e->nameIndex), values);
+ }
+
+ // Dynamic signals
+ auto s = obj->signalsBegin();
+ auto send = obj->signalsEnd();
+ for ( ; s != send; ++s) {
+ const int paramCount = s->parameterCount();
+
+ QList<QByteArray> names;
+ names.reserve(paramCount);
+ QVarLengthArray<QMetaType, 10> paramTypes(paramCount);
+
+ if (paramCount) {
+
+ int i = 0;
+ auto param = s->parametersBegin();
+ auto end = s->parametersEnd();
+ for ( ; param != end; ++param, ++i) {
+ names.append(stringAt(param->nameIndex).toUtf8());
+
+ QString customTypeName;
+ QMetaType type = metaTypeForParameter(param->type, &customTypeName);
+ if (!type.isValid())
+ return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
+
+ paramTypes[i] = type;
+ }
+ }
+
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+ if (paramCount)
+ flags.setHasArguments(true);
+
+ QString signalName = stringAt(s->nameIndex);
+ const auto it = seenSignals.find(signalName);
+ if (it == seenSignals.end()) {
+ seenSignals[signalName] = AllowOverride::No;
+ } else {
+ // TODO: Remove the AllowOverride::Yes branch once we can.
+ QQmlError message = qQmlCompileError(
+ s->location,
+ QQmlPropertyCacheCreatorBase::tr(
+ "Duplicate signal name: "
+ "invalid override of property change signal or superclass signal"));
+ switch (*it) {
+ case AllowOverride::No:
+ return message;
+ case AllowOverride::Yes:
+ message.setUrl(objectContainer->url());
+ enginePrivate->warning(message);
+ *it = AllowOverride::No; // No further overriding allowed.
+ break;
+ }
+ }
+ cache->appendSignal(signalName, flags, effectiveMethodIndex++,
+ paramCount?paramTypes.constData():nullptr, names);
+ }
+
+
+ // Dynamic slots
+ auto function = objectContainer->objectFunctionsBegin(obj);
+ auto fend = objectContainer->objectFunctionsEnd(obj);
+ for ( ; function != fend; ++function) {
+ auto flags = QQmlPropertyData::defaultSlotFlags();
+
+ const QString slotName = stringAt(function->nameIndex);
+ const auto it = seenSignals.constFind(slotName);
+ if (it != seenSignals.constEnd()) {
+ // TODO: Remove the AllowOverride::Yes branch once we can.
+ QQmlError message = qQmlCompileError(
+ function->location,
+ QQmlPropertyCacheCreatorBase::tr(
+ "Duplicate method name: "
+ "invalid override of property change signal or superclass signal"));
+ switch (*it) {
+ case AllowOverride::No:
+ return message;
+ case AllowOverride::Yes:
+ message.setUrl(objectContainer->url());
+ enginePrivate->warning(message);
+ break;
+ }
+ }
+ // Note: we don't append slotName to the seenSignals list, since we don't
+ // protect against overriding change signals or methods with properties.
+
+ QList<QByteArray> parameterNames;
+ QVector<QMetaType> parameterTypes;
+ auto formal = function->formalsBegin();
+ auto end = function->formalsEnd();
+ for ( ; formal != end; ++formal) {
+ flags.setHasArguments(true);
+ parameterNames << stringAt(formal->nameIndex).toUtf8();
+ QMetaType type = metaTypeForParameter(formal->type);
+ if (!type.isValid())
+ type = QMetaType::fromType<QVariant>();
+ parameterTypes << type;
+ }
+
+ QMetaType returnType = metaTypeForParameter(function->returnType);
+ if (!returnType.isValid())
+ returnType = QMetaType::fromType<QVariant>();
+
+ cache->appendMethod(slotName, flags, effectiveMethodIndex++, returnType, parameterNames, parameterTypes);
+ }
+
+
+ // Dynamic properties
+ int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
+ int propertyIdx = 0;
+ p = obj->propertiesBegin();
+ pend = obj->propertiesEnd();
+ for ( ; p != pend; ++p, ++propertyIdx) {
+ QMetaType propertyType;
+ QTypeRevision propertyTypeVersion = QTypeRevision::zero();
+ QQmlPropertyData::Flags propertyFlags;
+
+ const QV4::CompiledData::CommonType type = p->commonType();
+
+ if (p->isList())
+ propertyFlags.setType(QQmlPropertyData::Flags::QListType);
+ else if (type == QV4::CompiledData::CommonType::Var)
+ propertyFlags.setType(QQmlPropertyData::Flags::VarPropertyType);
+
+ if (type != QV4::CompiledData::CommonType::Invalid) {
+ propertyType = p->isList()
+ ? listTypeForPropertyType(type)
+ : metaTypeForPropertyType(type);
+ } else {
+ Q_ASSERT(!p->isCommonType());
+
+ QQmlType qmltype;
+ bool selfReference = false;
+ if (!imports->resolveType(
+ QQmlTypeLoader::get(enginePrivate),
+ stringAt(p->commonTypeOrTypeNameIndex()), &qmltype, nullptr, nullptr,
+ nullptr, QQmlType::AnyRegistrationType, &selfReference)) {
+ return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
+ }
+
+ // inline components are not necessarily valid yet
+ Q_ASSERT(qmltype.isValid());
+ if (qmltype.isComposite() || qmltype.isInlineComponentType()) {
+ QQmlType compositeType;
+ if (qmltype.isInlineComponentType()) {
+ compositeType = qmltype;
+ Q_ASSERT(compositeType.isValid());
+ } else if (selfReference) {
+ compositeType = objectContainer->qmlTypeForComponent();
+ } else {
+ // compositeType may not be the same type as qmlType because multiple engines
+ // may load different types for the same document. Therefore we have to ask
+ // our engine's type loader here.
+ QQmlRefPointer<QQmlTypeData> tdata
+ = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+ compositeType = tdata->compilationUnit()->qmlTypeForComponent();
+ }
+
+ if (p->isList()) {
+ propertyType = compositeType.qListTypeId();
+ } else {
+ propertyType = compositeType.typeId();
+ }
+ } else {
+ if (p->isList())
+ propertyType = qmltype.qListTypeId();
+ else
+ propertyType = qmltype.typeId();
+ propertyTypeVersion = qmltype.version();
+ }
+
+ if (p->isList())
+ propertyFlags.setType(QQmlPropertyData::Flags::QListType);
+ else if (propertyType.flags().testFlag(QMetaType::PointerToQObject))
+ propertyFlags.setType(QQmlPropertyData::Flags::QObjectDerivedType);
+ }
+
+ if (!p->isReadOnly() && !propertyType.flags().testFlag(QMetaType::IsQmlList))
+ propertyFlags.setIsWritable(true);
+
+
+ QString propertyName = stringAt(p->nameIndex);
+ if (!obj->hasAliasAsDefaultProperty() && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
+ cache->_defaultPropertyName = propertyName;
+ cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ propertyType, propertyTypeVersion, effectiveSignalIndex);
+
+ effectiveSignalIndex++;
+ }
+
+ QQmlError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(
+ const QV4::CompiledData::ParameterType &param, QString *customTypeName)
+{
+ const quint32 typeId = param.typeNameIndexOrCommonType();
+ if (param.indexIsCommonType()) {
+ // built-in type
+ if (param.isList())
+ return listTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
+ return metaTypeForPropertyType(QV4::CompiledData::CommonType(typeId));
+ }
+
+ // lazily resolved type
+ const QString typeName = stringAt(param.typeNameIndexOrCommonType());
+ if (customTypeName)
+ *customTypeName = typeName;
+ QQmlType qmltype;
+ bool selfReference = false;
+ if (!imports->resolveType(
+ &enginePrivate->typeLoader, typeName, &qmltype, nullptr, nullptr, nullptr,
+ QQmlType::AnyRegistrationType, &selfReference))
+ return QMetaType();
+
+ if (!qmltype.isComposite()) {
+ const QMetaType typeId = param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
+ if (!typeId.isValid() && qmltype.isInlineComponentType()) {
+ const QQmlType qmlType = objectContainer->qmlTypeForComponent(qmltype.elementName());
+ return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
+ } else {
+ return typeId;
+ }
+ }
+
+ if (selfReference) {
+ const QQmlType qmlType = objectContainer->qmlTypeForComponent();
+ return param.isList() ? qmlType.qListTypeId() : qmlType.typeId();
+ }
+
+ return param.isList() ? qmltype.qListTypeId() : qmltype.typeId();
+}
+
+template <typename ObjectContainer, typename CompiledObject>
+int objectForId(const ObjectContainer *objectContainer, const CompiledObject &component, int id)
+{
+ for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
+ if (candidate.objectId() == id)
+ return candidateIndex;
+ }
+ return -1;
+}
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheAliasCreator
+{
+public:
+ typedef typename ObjectContainer::CompiledObject CompiledObject;
+
+ QQmlPropertyCacheAliasCreator(
+ QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
+ QQmlError appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
+
+private:
+ QQmlError propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
+ QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
+ QQmlEnginePrivate *enginePriv);
+
+ QQmlPropertyCacheVector *propertyCaches;
+ const ObjectContainer *objectContainer;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(
+ QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
+ : propertyCaches(propertyCaches)
+ , objectContainer(objectContainer)
+{
+}
+
+template <typename ObjectContainer>
+inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
+ QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
+ QQmlEnginePrivate *enginePriv)
+{
+ *type = QMetaType();
+ bool writable = false;
+ bool resettable = false;
+ bool bindable = false;
+
+ propertyFlags->setIsAlias(true);
+
+ if (alias.isAliasToLocalAlias()) {
+ const QV4::CompiledData::Alias *lastAlias = &alias;
+ QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
+
+ do {
+ const int targetObjectIndex = objectForId(
+ objectContainer, component, lastAlias->targetObjectId());
+ Q_ASSERT(targetObjectIndex >= 0);
+ const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
+ Q_ASSERT(targetObject);
+
+ auto nextAlias = targetObject->aliasesBegin();
+ for (uint i = 0; i < lastAlias->localAliasIndex; ++i)
+ ++nextAlias;
+
+ const QV4::CompiledData::Alias *targetAlias = &(*nextAlias);
+ if (seenAliases.contains(targetAlias)) {
+ return qQmlCompileError(targetAlias->location,
+ QQmlPropertyCacheCreatorBase::tr("Cyclic alias"));
+ }
+
+ seenAliases.append(targetAlias);
+ lastAlias = targetAlias;
+ } while (lastAlias->isAliasToLocalAlias());
+
+ return propertyDataForAlias(
+ component, *lastAlias, type, version, propertyFlags, enginePriv);
+ }
+
+ const int targetObjectIndex = objectForId(objectContainer, component, alias.targetObjectId());
+ Q_ASSERT(targetObjectIndex >= 0);
+ const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
+
+ if (alias.encodedMetaPropertyIndex == -1) {
+ Q_ASSERT(alias.hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject));
+ auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
+ if (!typeRef) {
+ // Can be caused by the alias target not being a valid id or property. E.g.:
+ // property alias dataValue: dataVal
+ // invalidAliasComponent { id: dataVal }
+ return qQmlCompileError(targetObject.location,
+ QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
+
+ const auto referencedType = typeRef->type();
+ if (referencedType.isValid()) {
+ *type = referencedType.typeId();
+ if (!type->isValid() && referencedType.isInlineComponentType()) {
+ *type = objectContainer->qmlTypeForComponent(referencedType.elementName()).typeId();
+ Q_ASSERT(type->isValid());
+ }
+ } else {
+ *type = typeRef->compilationUnit()->metaType();
+ }
+
+ *version = typeRef->version();
+
+ propertyFlags->setType(QQmlPropertyData::Flags::QObjectDerivedType);
+ } else {
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
+ int valueTypeIndex = QQmlPropertyIndex::fromEncoded(
+ alias.encodedMetaPropertyIndex).valueTypeIndex();
+
+ QQmlPropertyCache::ConstPtr targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ const QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ Q_ASSERT(targetProperty);
+
+ const QMetaType targetPropType = targetProperty->propType();
+
+ const auto populateWithPropertyData = [&](const QQmlPropertyData *property) {
+ *type = property->propType();
+ writable = property->isWritable();
+ resettable = property->isResettable();
+ bindable = property->isBindable();
+
+ if (property->isVarProperty())
+ propertyFlags->setType(QQmlPropertyData::Flags::QVariantType);
+ else
+ propertyFlags->copyPropertyTypeFlags(property->flags());
+ };
+
+ // for deep aliases, valueTypeIndex is always set
+ if (!QQmlMetaType::isValueType(targetPropType) && valueTypeIndex != -1) {
+ // deep alias property
+
+ QQmlPropertyCache::ConstPtr typeCache
+ = QQmlMetaType::propertyCacheForType(targetPropType);
+
+ if (!typeCache) {
+ // See if it's a half-resolved composite type
+ if (const QV4::ResolvedTypeReference *typeRef
+ = objectContainer->resolvedType(targetPropType)) {
+ typeCache = typeRef->typePropertyCache();
+ }
+ }
+
+ const QQmlPropertyData *typeProperty = typeCache
+ ? typeCache->property(valueTypeIndex)
+ : nullptr;
+ if (typeProperty == nullptr) {
+ return qQmlCompileError(
+ alias.referenceLocation,
+ QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
+ populateWithPropertyData(typeProperty);
+ } else {
+ // value type or primitive type or enum
+ populateWithPropertyData(targetProperty);
+
+ if (valueTypeIndex != -1) {
+ const QMetaObject *valueTypeMetaObject
+ = QQmlMetaType::metaObjectForValueType(*type);
+ const QMetaProperty valueTypeMetaProperty
+ = valueTypeMetaObject->property(valueTypeIndex);
+ *type = valueTypeMetaProperty.metaType();
+
+ // We can only write or reset the value type property if we can write
+ // the value type itself.
+ resettable = writable && valueTypeMetaProperty.isResettable();
+ writable = writable && valueTypeMetaProperty.isWritable();
+
+ bindable = valueTypeMetaProperty.isBindable();
+ }
+ }
+ }
+
+ propertyFlags->setIsWritable(
+ writable && !alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly));
+ propertyFlags->setIsResettable(resettable);
+ propertyFlags->setIsBindable(bindable);
+ return QQmlError();
+}
+
+template <typename ObjectContainer>
+inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv)
+{
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+ if (!object.aliasCount())
+ return QQmlError();
+
+ QQmlPropertyCache::Ptr propertyCache = propertyCaches->ownAt(objectIndex);
+ Q_ASSERT(propertyCache);
+
+ int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.size();
+ int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.size();
+
+ int aliasIndex = 0;
+ auto alias = object.aliasesBegin();
+ auto end = object.aliasesEnd();
+ for ( ; alias != end; ++alias, ++aliasIndex) {
+ Q_ASSERT(alias->hasFlag(QV4::CompiledData::Alias::Resolved));
+
+ QMetaType type;
+ QTypeRevision version = QTypeRevision::zero();
+ QQmlPropertyData::Flags propertyFlags;
+ QQmlError error = propertyDataForAlias(component, *alias, &type, &version,
+ &propertyFlags, enginePriv);
+ if (error.isValid())
+ return error;
+
+ const QString propertyName = objectContainer->stringAt(alias->nameIndex());
+
+ if (object.hasAliasAsDefaultProperty() && aliasIndex == object.indexOfDefaultPropertyOrAlias)
+ propertyCache->_defaultPropertyName = propertyName;
+
+ propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ type, version, effectiveSignalIndex++);
+ }
+
+ return QQmlError();
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYCACHECREATOR_P_H