diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2023-05-16 13:28:26 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-05-22 14:42:46 +0200 |
commit | d5511fdea6caa4dee7ea97314e64163d309bd884 (patch) | |
tree | 168bf7c8ab57d5e42518511905e67420215677c5 | |
parent | f69bee0b7e7fea528cd60d8a6e23c75ccfb3aca6 (diff) |
QML: Register all builtins
We need run time access to the QQmlType instances of all the builtins in
order to properly coerce types without special casing everything all the
time.
Since we can now have QML types without metaobjects, we need to check
for the metaobject in a few places where we didn't need to check before.
Change-Id: Ib22cbb12c60ebdce4897c3f3338851e8b925926f
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/qml/jsruntime/qv4resolvedtypereference.cpp | 9 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 106 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 24 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatypedata.cpp | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatypedata_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycachecreator_p.h | 13 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertyvalidator.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmltypedata.cpp | 12 | ||||
-rw-r--r-- | tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp | 90 |
10 files changed, 238 insertions, 24 deletions
diff --git a/src/qml/jsruntime/qv4resolvedtypereference.cpp b/src/qml/jsruntime/qv4resolvedtypereference.cpp index 6eab05fe51..4615a72eb1 100644 --- a/src/qml/jsruntime/qv4resolvedtypereference.cpp +++ b/src/qml/jsruntime/qv4resolvedtypereference.cpp @@ -55,8 +55,8 @@ QQmlPropertyCache::ConstPtr ResolvedTypeReference::createPropertyCache() const QMetaObject *metaObject = m_type.metaObject(); if (!metaObject) // value type of non-Q_GADGET base with extension metaObject = m_type.extensionMetaObject(); - Q_ASSERT(metaObject); - m_typePropertyCache = QQmlMetaType::propertyCache(metaObject, m_version); + if (metaObject) + m_typePropertyCache = QQmlMetaType::propertyCache(metaObject, m_version); return m_typePropertyCache; } else { Q_ASSERT(m_compilationUnit); @@ -80,7 +80,10 @@ bool ResolvedTypeReference::addToHash( } else if (m_type.isValid()) { bool ok = false; - hash->addData(createPropertyCache()->checksum(checksums, &ok)); + if (QQmlPropertyCache::ConstPtr propertyCache = createPropertyCache()) + hash->addData(propertyCache->checksum(checksums, &ok)); + else + Q_ASSERT(m_type.module() == QLatin1String("QML")); // a builtin without metaobject return ok; } if (!m_compilationUnit) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index d7542c09fc..64a0e48663 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -441,34 +441,126 @@ void QQmlData::flushPendingBinding(int coreIndex) QQmlData::DeferredData::DeferredData() = default; QQmlData::DeferredData::~DeferredData() = default; +template<> +int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + QQmlPrivate::RegisterType type = { + QQmlPrivate::RegisterType::CurrentVersion, + QMetaType(), + QMetaType(), + 0, nullptr, nullptr, + QString(), + nullptr, + uri, + QTypeRevision::fromVersion(versionMajor, versionMinor), + qmlName, + nullptr, + nullptr, + nullptr, + -1, + -1, + -1, + nullptr, + nullptr, + nullptr, + QTypeRevision::zero(), + -1, + QQmlPrivate::ValueTypeCreationMethod::None, + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} + bool QQmlEnginePrivate::baseModulesUninitialized = true; void QQmlEnginePrivate::init() { Q_Q(QQmlEngine); if (baseModulesUninitialized) { + // Named builtins + qmlRegisterType<void>("QML", 1, 0, "void"); + + const int varId = qmlRegisterType<QVariant>("QML", 1, 0, "var"); + QQmlMetaType::registerTypeAlias(varId, QLatin1String("variant")); + qmlRegisterAnonymousSequentialContainer<QList<QVariant>>("QML", 1); - // required for the Compiler. qmlRegisterType<QObject>("QML", 1, 0, "QtObject"); qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); - qmlRegisterAnonymousSequentialContainer<QList<QVariant>>("QML", 1); - qmlRegisterAnonymousSequentialContainer<QList<bool>>("QML", 1); + + qmlRegisterType<int>("QML", 1, 0, "int"); qmlRegisterAnonymousSequentialContainer<QList<int>>("QML", 1); - qmlRegisterAnonymousSequentialContainer<QList<float>>("QML", 1); + + const int realId = qmlRegisterType<double>("QML", 1, 0, "real"); + QQmlMetaType::registerTypeAlias(realId, QLatin1String("double")); qmlRegisterAnonymousSequentialContainer<QList<double>>("QML", 1); + + qmlRegisterType<QString>("QML", 1, 0, "string"); qmlRegisterAnonymousSequentialContainer<QList<QString>>("QML", 1); - qmlRegisterAnonymousSequentialContainer<QList<QUrl>>("QML", 1); + + qmlRegisterType<bool>("QML", 1, 0, "bool"); + qmlRegisterAnonymousSequentialContainer<QList<bool>>("QML", 1); + + qmlRegisterType<QDateTime>("QML", 1, 0, "date"); qmlRegisterAnonymousSequentialContainer<QList<QDateTime>>("QML", 1); + + qmlRegisterType<QUrl>("QML", 1, 0, "url"); + qmlRegisterAnonymousSequentialContainer<QList<QUrl>>("QML", 1); + +#if QT_CONFIG(regularexpression) + qmlRegisterType<QRegularExpression>("QML", 1, 0, "regexp"); qmlRegisterAnonymousSequentialContainer<QList<QRegularExpression>>("QML", 1); +#else + qmlRegisterType<void>("QML", 1, 0, "regexp"); +#endif + + // Anonymous builtins + qmlRegisterAnonymousType<std::nullptr_t>("QML", 1); + qmlRegisterAnonymousType<QVariantMap>("QML", 1); + + qmlRegisterAnonymousType<QJSValue>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<QJSValue>>("QML", 1); + + qmlRegisterAnonymousType<qint8>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<qint8>>("QML", 1); + + qmlRegisterAnonymousType<quint8>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<quint8>>("QML", 1); + + qmlRegisterAnonymousType<short>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<short>>("QML", 1); + + qmlRegisterAnonymousType<ushort>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<ushort>>("QML", 1); + + qmlRegisterAnonymousType<uint>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<uint>>("QML", 1); + + qmlRegisterAnonymousType<qlonglong>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<qlonglong>>("QML", 1); + + qmlRegisterAnonymousType<qulonglong>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<qulonglong>>("QML", 1); + + qmlRegisterAnonymousType<float>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<float>>("QML", 1); + + qmlRegisterAnonymousType<QChar>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<QChar>>("QML", 1); + + qmlRegisterAnonymousType<QDate>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<QDate>>("QML", 1); + + qmlRegisterAnonymousType<QTime>("QML", 1); + qmlRegisterAnonymousSequentialContainer<QList<QTime>>("QML", 1); + + qmlRegisterAnonymousType<QByteArray>("QML", 1); qmlRegisterAnonymousSequentialContainer<QList<QByteArray>>("QML", 1); // No need to specifically register those. static_assert(std::is_same_v<QStringList, QList<QString>>); static_assert(std::is_same_v<QVariantList, QList<QVariant>>); - qRegisterMetaType<QVariant>(); qRegisterMetaType<QQmlScriptString>(); - qRegisterMetaType<QJSValue>(); qRegisterMetaType<QQmlComponent::Status>(); qRegisterMetaType<QList<QObject*> >(); qRegisterMetaType<QQmlBinding*>(); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index dbc31a1cd1..26ab192c31 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -320,6 +320,14 @@ void QQmlMetaType::clearTypeRegistrations() data->propertyCaches.clear(); } +void QQmlMetaType::registerTypeAlias(int typeIndex, const QString &name) +{ + QQmlMetaTypeDataPtr data; + const QQmlType type = data->types.value(typeIndex); + const QQmlTypePrivate *priv = type.priv(); + data->nameToType.insert(name, priv); +} + int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function) { if (function.structVersion > 1) @@ -1347,9 +1355,12 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaTyp return composite; const QQmlTypePrivate *type = data->idToType.value(metaType.id()); - return (type && type->typeId == metaType) - ? data->propertyCache(QQmlType(type).metaObject(), type->version) - : QQmlPropertyCache::ConstPtr(); + if (type && type->typeId == metaType) { + if (const QMetaObject *mo = QQmlType(type).metaObject()) + return data->propertyCache(mo, type->version); + } + + return QQmlPropertyCache::ConstPtr(); } /*! @@ -1389,8 +1400,11 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType( return QQmlPropertyCache::ConstPtr(); const QQmlType type(typePriv); - if (type.containsRevisionedAttributes()) + if (type.containsRevisionedAttributes()) { + // It can only have (revisioned) properties or methods if it has a metaobject + Q_ASSERT(type.metaObject()); return data->propertyCache(type, version); + } if (const QMetaObject *metaObject = type.metaObject()) return data->propertyCache(metaObject, version); @@ -1510,7 +1524,7 @@ QList<QQmlType> QQmlMetaType::qmlTypes() const QQmlMetaTypeDataPtr data; QList<QQmlType> types; - for (QQmlTypePrivate *t : data->nameToType) + for (const QQmlTypePrivate *t : data->nameToType) types.append(QQmlType(t)); return types; diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 0604bbc4db..3a00d7f201 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -213,6 +213,8 @@ public: } } + static void registerTypeAlias(int typeId, const QString &name); + static int registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent); static void unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function); diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp index ab6054349a..a7d546fb1a 100644 --- a/src/qml/qml/qqmlmetatypedata.cpp +++ b/src/qml/qml/qqmlmetatypedata.cpp @@ -137,6 +137,7 @@ QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache( quint8 maxMinorVersion = 0; const QMetaObject *metaObject = type.metaObject(); + Q_ASSERT(metaObject); const QTypeRevision combinedVersion = version.hasMajorVersion() ? version diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h index 60b76137d1..94eb801926 100644 --- a/src/qml/qml/qqmlmetatypedata_p.h +++ b/src/qml/qml/qqmlmetatypedata_p.h @@ -36,7 +36,7 @@ struct QQmlMetaTypeData typedef QHash<int, QQmlTypePrivate *> Ids; Ids idToType; - using Names = QMultiHash<QHashedString, QQmlTypePrivate *>; + using Names = QMultiHash<QHashedString, const QQmlTypePrivate *>; Names nameToType; typedef QHash<QUrl, QQmlTypePrivate *> Files; //For file imported composite types only diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 1dcf169ecc..7c6c54c611 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -255,7 +255,10 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); QQmlPropertyCache::ConstPtr baseTypeCache = typeRef->createPropertyCache(); - QQmlError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); + 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; } @@ -345,7 +348,13 @@ inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::pr } } - return typeRef->createPropertyCache(); + 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( diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index 308b66e2d9..4f7663b34d 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -685,13 +685,12 @@ QQmlError QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *p const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex); if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) { QQmlPropertyCache::ConstPtr cache = typeRef->createPropertyCache(); - const QMetaObject *mo = cache->firstCppMetaObject(); + const QMetaObject *mo = cache ? cache->firstCppMetaObject() : nullptr; QQmlType qmlType; while (mo && !qmlType.isValid()) { qmlType = QQmlMetaType::qmlType(mo); mo = mo->superClass(); } - Q_ASSERT(qmlType.isValid()); isValueSource = qmlType.propertyValueSourceCast() != -1; isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1; diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index 0e94de34cd..b607315725 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -273,8 +273,8 @@ static bool addTypeReferenceChecksumsToHash( if (typeRef.typeData) { const auto unit = typeRef.typeData->compilationUnit()->unitData(); hash->addData({unit->md5Checksum, sizeof(unit->md5Checksum)}); - } else if (typeRef.type.isValid()) { - const auto propertyCache = QQmlMetaType::propertyCache(typeRef.type.metaObject()); + } else if (const QMetaObject *mo = typeRef.type.metaObject()) { + const auto propertyCache = QQmlMetaType::propertyCache(mo); bool ok = false; hash->addData(propertyCache->checksum(checksums, &ok)); if (!ok) @@ -990,8 +990,12 @@ QQmlError QQmlTypeData::buildTypeResolutionCaches( return qQmlCompileError(resolvedType->location, reason); } - if (qmlType.containsRevisionedAttributes()) - ref->setTypePropertyCache(QQmlMetaType::propertyCache(qmlType, resolvedType->version)); + if (qmlType.containsRevisionedAttributes()) { + // It can only have (revisioned) properties or methods if it has a metaobject + Q_ASSERT(qmlType.metaObject()); + ref->setTypePropertyCache( + QQmlMetaType::propertyCache(qmlType, resolvedType->version)); + } } ref->setVersion(resolvedType->version); ref->doDynamicTypeCheck(); diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index a185f1776f..dc5060f7be 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -53,6 +53,7 @@ private slots: void revertValueTypeAnimation(); void clearPropertyCaches(); + void builtins(); }; class TestType : public QObject @@ -738,6 +739,95 @@ void tst_qqmlmetatype::clearPropertyCaches() QVERIFY(oldCache.data() != newCache.data()); } +template<typename T> +void checkBuiltinBaseType() +{ + const QQmlType type = QQmlMetaType::qmlType(QMetaType::fromType<T>()); + QVERIFY(type.isValid()); + QCOMPARE(type.typeId(), QMetaType::fromType<T>()); + QCOMPARE(type.qListTypeId(), QMetaType::fromType<QList<T>>()); +} + +template<typename T> +void checkBuiltinListType() +{ + const QQmlType listType = QQmlMetaType::qmlListType(QMetaType::fromType<QList<T>>()); + QVERIFY(listType.isValid()); + QVERIFY(listType.isSequentialContainer()); + QCOMPARE(listType.typeId(), QMetaType::fromType<T>()); + QCOMPARE(listType.qListTypeId(), QMetaType::fromType<QList<T>>()); + QCOMPARE(listType.listMetaSequence().valueMetaType(), QMetaType::fromType<T>()); +} + +template<typename... T> +void checkBuiltinTypes() +{ + (checkBuiltinBaseType<T>(), ...); + (checkBuiltinListType<T>(), ...); +} + +template<typename T> +void checkNamedBuiltin(const QString &name) +{ + QCOMPARE(QQmlMetaType::qmlType("QML/" + name, QTypeRevision::fromVersion(1, 0)), + QQmlMetaType::qmlType(QMetaType::fromType<T>())); +} + +template<typename T> +void checkObjectBuiltin(const QString &name) +{ + const QQmlType objectType = QQmlMetaType::qmlType(QMetaType::fromType<T *>()); + QVERIFY(objectType.isValid()); + QCOMPARE(objectType.typeId(), QMetaType::fromType<T *>()); + QCOMPARE(objectType.qListTypeId(), QMetaType::fromType<QQmlListProperty<T>>()); + + const QQmlType listType = QQmlMetaType::qmlListType(QMetaType::fromType<QQmlListProperty<T>>()); + QVERIFY(listType.isValid()); + QCOMPARE(listType.typeId(), QMetaType::fromType<T *>()); + QCOMPARE(listType.qListTypeId(), QMetaType::fromType<QQmlListProperty<T>>()); + + checkNamedBuiltin<T *>(name); +} + +void tst_qqmlmetatype::builtins() +{ + qmlClearTypeRegistrations(); + QQmlEngine engine; // registers the builtins + + checkBuiltinTypes< + QVariant, QJSValue, qint8, quint8, short, ushort, int, uint, qlonglong, qulonglong, float, + double, QChar, QString, bool, QDateTime, QDate, QTime, QUrl, QByteArray>(); + + checkNamedBuiltin<QVariant>("var"); + checkNamedBuiltin<QVariant>("variant"); + checkNamedBuiltin<int>("int"); + checkNamedBuiltin<double>("double"); + checkNamedBuiltin<double>("real"); + checkNamedBuiltin<QString>("string"); + checkNamedBuiltin<bool>("bool"); + checkNamedBuiltin<QDateTime>("date"); + checkNamedBuiltin<QUrl>("url"); + +#if QT_CONFIG(regularexpression) + checkBuiltinBaseType<QRegularExpression>(); + checkBuiltinListType<QRegularExpression>(); + checkNamedBuiltin<QRegularExpression>("regexp"); +#endif + + // Can't retrieve this one by metatype + const QQmlType voidType = QQmlMetaType::qmlType("QML/void", QTypeRevision::fromVersion(1, 0)); + QVERIFY(voidType.isValid()); + QCOMPARE(voidType.typeId(), QMetaType()); + QCOMPARE(voidType.qListTypeId(), QMetaType()); + + // No separate list types + checkBuiltinBaseType<std::nullptr_t>(); + checkBuiltinBaseType<QVariantMap>(); + + checkObjectBuiltin<QObject>("QtObject"); + checkObjectBuiltin<QQmlComponent>("Component"); +} + QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.moc" |