aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-05-16 13:28:26 +0200
committerUlf Hermann <ulf.hermann@qt.io>2023-05-22 14:42:46 +0200
commitd5511fdea6caa4dee7ea97314e64163d309bd884 (patch)
tree168bf7c8ab57d5e42518511905e67420215677c5
parentf69bee0b7e7fea528cd60d8a6e23c75ccfb3aca6 (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.cpp9
-rw-r--r--src/qml/qml/qqmlengine.cpp106
-rw-r--r--src/qml/qml/qqmlmetatype.cpp24
-rw-r--r--src/qml/qml/qqmlmetatype_p.h2
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp1
-rw-r--r--src/qml/qml/qqmlmetatypedata_p.h2
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h13
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp3
-rw-r--r--src/qml/qml/qqmltypedata.cpp12
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp90
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"