aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2024-02-14 17:44:58 +0100
committerUlf Hermann <ulf.hermann@qt.io>2024-02-15 22:34:49 +0100
commit4d13ed6a598c142f9ed9928fa0bdcb395e4b94cc (patch)
treef7b3c2fd6e2407446494c072426a7033dcb1de2d
parent6b609c796b59e548854888e2c82d46eab9fcc083 (diff)
QtQml: Consider value types when looking for metaobjects
We have quite a few types with only extension metaobjects now. Move the functionality to retrieve the metaobject into QQmlType where it belongs. Pick-to: 6.7 6.6 Fixes: QTBUG-119829 Change-Id: I2b99b1a305d8726547ae0512d3c832799a4e4b04 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h2
-rw-r--r--src/qml/qml/qqmlglobal.cpp108
-rw-r--r--src/qml/qml/qqmlmetatype.cpp19
-rw-r--r--src/qml/qml/qqmlmetatype_p.h20
-rw-r--r--src/qml/qml/qqmltype.cpp18
-rw-r--r--src/qml/qml/qqmltype_p.h5
-rw-r--r--src/qml/qml/qqmltype_p_p.h39
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp6
-rw-r--r--tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp14
10 files changed, 152 insertions, 85 deletions
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index e8896ce19d..ef4b742590 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -384,7 +384,7 @@ inline ReturnedValue coerce(
if (void *target = QQmlValueTypeProvider::heapCreateValueType(qmlType, value)) {
Heap::QQmlValueTypeWrapper *wrapper = engine->memoryManager->allocate<QQmlValueTypeWrapper>(
- nullptr, metaType, QQmlMetaType::metaObjectForValueType(qmlType),
+ nullptr, metaType, qmlType.metaObjectForValueType(),
nullptr, -1, Heap::ReferenceObject::NoFlag);
Q_ASSERT(!wrapper->gadgetPtr());
wrapper->setGadgetPtr(target);
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 061a55a9e6..1273067187 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -445,29 +445,30 @@ bool createOrConstructValueType(
const QQmlType &targetType, const QV4::Value &source,
Allocate &&allocate, DefaultConstruct &&defaultConstruct)
{
- if (const QMetaObject *targetMetaObject = QQmlMetaType::metaObjectForValueType(targetType)) {
- const auto warn = [&]() {
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << targetMetaObject->className() << "to call with value"
- << source.toQStringNoThrow();
- };
-
- if (targetType.canPopulateValueType()) {
- if (source.isObject() && targetMetaObject) {
+ const auto warn = [&](const QMetaObject *targetMetaObject) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << targetMetaObject->className() << "to call with value"
+ << source.toQStringNoThrow();
+ };
+
+ if (targetType.canPopulateValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
+ if (source.isObject()) {
doWriteProperties(targetMetaObject, defaultConstruct(), source);
return true;
}
if (targetType.canConstructValueType()) {
if (fromMatchingType(targetMetaObject, source, allocate))
return true;
- warn();
-
+ warn(targetMetaObject);
}
- } else if (targetType.canConstructValueType()) {
+ }
+ } else if (targetType.canConstructValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
if (fromMatchingType(targetMetaObject, source, allocate))
return true;
- warn();
+ warn(targetMetaObject);
}
}
@@ -489,15 +490,15 @@ bool createOrConstructValueType(
const QQmlType &targetType, QMetaType sourceMetaType, void *source,
Allocate &&allocate, DefaultConstruct &&defaultConstruct)
{
- if (const QMetaObject *targetMetaObject = QQmlMetaType::metaObjectForValueType(targetType)) {
- const auto warn = [&]() {
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << targetMetaObject->className() << "to call with value" << source;
- };
- if (targetType.canPopulateValueType()) {
+ const auto warn = [&](const QMetaObject *targetMetaObject) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << targetMetaObject->className() << "to call with value" << source;
+ };
+ if (targetType.canPopulateValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
if (const QMetaObject *sourceMetaObject
= QQmlMetaType::metaObjectForValueType(sourceMetaType)) {
doWriteProperties(
@@ -529,15 +530,17 @@ bool createOrConstructValueType(
return true;
}
}
+ }
- if (targetType.canConstructValueType()) {
+ if (targetType.canConstructValueType()) {
+ if (const QMetaObject *targetMetaObject = targetType.metaObjectForValueType()) {
if (fromMatchingType(targetMetaObject, std::forward<Allocate>(allocate),
[&](QMetaType, auto callback) {
return callback(sourceMetaType, source);
})) {
return true;
}
- warn();
+ warn(targetMetaObject);
}
}
@@ -656,11 +659,12 @@ QVariant QQmlValueTypeProvider::createValueType(const QString &s, QMetaType meta
if (!isConstructibleMetaType(metaType))
return QVariant();
const QQmlType type = QQmlMetaType::qmlType(metaType);
- const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type);
- if (mo && type.canConstructValueType()) {
- QVariant result;
- if (fromString(mo, s, [&]() { return createVariantData(metaType, &result); }))
- return result;
+ if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
+ QVariant result;
+ if (fromString(mo, s, [&]() { return createVariantData(metaType, &result); }))
+ return result;
+ }
}
return fromJSValue(type, s, metaType);
@@ -671,27 +675,29 @@ QVariant QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType m
if (!isConstructibleMetaType(metaType))
return QVariant();
const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- const auto warn = [&]() {
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s.toQStringNoThrow();
- };
-
- if (type.canPopulateValueType()) {
+ const auto warn = [&](const QMetaObject *mo) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << mo->className() << "to call with value" << s.toQStringNoThrow();
+ };
+
+ if (type.canPopulateValueType()) {
+ if (const QMetaObject *mo = type.metaObject()) {
QVariant result = byProperties(mo, metaType, s);
if (result.isValid())
return result;
if (type.canConstructValueType()) {
if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
return result;
- warn();
+ warn(mo);
}
- } else if (type.canConstructValueType()) {
+ }
+ } else if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObject()) {
QVariant result;
if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
return result;
- warn();
+ warn(mo);
}
}
@@ -708,27 +714,29 @@ QVariant QQmlValueTypeProvider::createValueType(const QVariant &s, QMetaType met
if (!isConstructibleMetaType(metaType))
return QVariant();
const QQmlType type = QQmlMetaType::qmlType(metaType);
- if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) {
- const auto warn = [&]() {
- qWarning().noquote()
- << "Could not find any constructor for value type"
- << mo->className() << "to call with value" << s;
- };
-
- if (type.canPopulateValueType()) {
+ const auto warn = [&](const QMetaObject *mo) {
+ qWarning().noquote()
+ << "Could not find any constructor for value type"
+ << mo->className() << "to call with value" << s;
+ };
+
+ if (type.canPopulateValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
QVariant result = byProperties(mo, metaType, s);
if (result.isValid())
return result;
if (type.canConstructValueType()) {
if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
return result;
- warn();
+ warn(mo);
}
- } else if (type.canConstructValueType()) {
+ }
+ } else if (type.canConstructValueType()) {
+ if (const QMetaObject *mo = type.metaObjectForValueType()) {
QVariant result;
if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); }))
return result;
- warn();
+ warn(mo);
}
}
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 10bb54fca8..a21c225eba 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -1463,8 +1463,15 @@ QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType meta
return composite;
const QQmlTypePrivate *type = data->idToType.value(metaType.id());
- return (type && type->typeId == metaType)
- ? data->propertyCache(type->baseMetaObject, QTypeRevision())
+ if (!type || type->typeId != metaType)
+ return QQmlPropertyCache::ConstPtr();
+
+ const QMetaObject *metaObject = type->isValueType()
+ ? type->metaObjectForValueType()
+ : type->baseMetaObject;
+
+ return metaObject
+ ? data->propertyCache(metaObject, QTypeRevision())
: QQmlPropertyCache::ConstPtr();
}
@@ -1898,8 +1905,12 @@ const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
// call QObject pointers value types. Explicitly registered types also override
// the implicit use of gadgets.
if (!(metaType.flags() & QMetaType::PointerToQObject)) {
- if (const QMetaObject *mo = metaObjectForValueType(QQmlMetaType::qmlType(metaType)))
- return mo;
+ const QQmlMetaTypeDataPtr data;
+ const QQmlTypePrivate *type = data->idToType.value(metaType.id());
+ if (type && type->regType == QQmlType::CppType && type->typeId == metaType) {
+ if (const QMetaObject *mo = type->metaObjectForValueType())
+ return mo;
+ }
}
// If it _is_ a gadget, we can just use it.
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 3c2b893499..f4870d9db1 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -257,26 +257,6 @@ public:
static bool isValueType(QMetaType type);
static QQmlValueType *valueType(QMetaType metaType);
static const QMetaObject *metaObjectForValueType(QMetaType type);
- static const QMetaObject *metaObjectForValueType(const QQmlType &qmlType)
- {
- // Prefer the extension meta object, if any.
- // Extensions allow registration of non-gadget value types.
- if (const QMetaObject *extensionMetaObject = qmlType.extensionMetaObject()) {
- // This may be a namespace even if the original metaType isn't.
- // You can do such things with QML_FOREIGN declarations.
- if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
- return extensionMetaObject;
- }
-
- if (const QMetaObject *qmlTypeMetaObject = qmlType.metaObject()) {
- // This may be a namespace even if the original metaType isn't.
- // You can do such things with QML_FOREIGN declarations.
- if (qmlTypeMetaObject->metaType().flags() & QMetaType::IsGadget)
- return qmlTypeMetaObject;
- }
-
- return nullptr;
- }
static QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t);
static void registerInternalCompositeType(
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index b3df3a862b..ee5ae374e5 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -644,6 +644,11 @@ bool QQmlType::isSequentialContainer() const
return d && d->regType == SequentialContainerType;
}
+bool QQmlType::isValueType() const
+{
+ return d && d->isValueType();
+}
+
QMetaType QQmlType::typeId() const
{
return d ? d->typeId : QMetaType{};
@@ -661,14 +666,13 @@ QMetaSequence QQmlType::listMetaSequence() const
const QMetaObject *QQmlType::metaObject() const
{
- if (!d)
- return nullptr;
- const QQmlTypePrivate::ProxyMetaObjects *proxies = d->init();
+ return d ? d->metaObject() : nullptr;
+}
- if (proxies->data.isEmpty())
- return d->baseMetaObject;
- else
- return proxies->data.constFirst().metaObject;
+const QMetaObject *QQmlType::metaObjectForValueType() const
+{
+ Q_ASSERT(d);
+ return d->metaObjectForValueType();
}
const QMetaObject *QQmlType::baseMetaObject() const
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index c4959cae06..ffffd0fdd9 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -96,12 +96,17 @@ public:
bool isQObjectSingleton() const;
bool isQJSValueSingleton() const;
bool isSequentialContainer() const;
+ bool isValueType() const;
QMetaType typeId() const;
QMetaType qListTypeId() const;
QMetaSequence listMetaSequence() const;
const QMetaObject *metaObject() const;
+
+ // Precondition: The type is actually a value type!
+ const QMetaObject *metaObjectForValueType() const;
+
const QMetaObject *baseMetaObject() const;
QTypeRevision metaObjectRevision() const;
bool containsRevisionedAttributes() const;
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index bd77eb5509..8f614eeac2 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -86,6 +86,11 @@ public:
return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType;
}
+ bool isValueType() const
+ {
+ return regType == QQmlType::CppType && !(typeId.flags() & QMetaType::PointerToQObject);
+ }
+
QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
QQmlPropertyCache::ConstPtr compositePropertyCache(QQmlEnginePrivate *engine) const;
@@ -192,6 +197,40 @@ public:
}, ok);
}
+ const QMetaObject *metaObject() const
+ {
+ if (isValueType())
+ return metaObjectForValueType();
+
+ const QQmlTypePrivate::ProxyMetaObjects *proxies = init();
+ return proxies->data.isEmpty()
+ ? baseMetaObject
+ : proxies->data.constFirst().metaObject;
+ }
+
+ const QMetaObject *metaObjectForValueType() const
+ {
+ Q_ASSERT(isValueType());
+
+ // Prefer the extension meta object, if any.
+ // Extensions allow registration of non-gadget value types.
+ if (const QMetaObject *extensionMetaObject = extraData.cppTypeData->extMetaObject) {
+ // This may be a namespace even if the original metaType isn't.
+ // You can do such things with QML_FOREIGN declarations.
+ if (extensionMetaObject->metaType().flags() & QMetaType::IsGadget)
+ return extensionMetaObject;
+ }
+
+ if (baseMetaObject) {
+ // This may be a namespace even if the original metaType isn't.
+ // You can do such things with QML_FOREIGN declarations.
+ if (baseMetaObject->metaType().flags() & QMetaType::IsGadget)
+ return baseMetaObject;
+ }
+
+ return nullptr;
+ }
+
private:
mutable QAtomicPointer<const ProxyMetaObjects> proxyMetaObjects;
mutable QAtomicPointer<const Enums> enums;
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 412c1d6419..0a707d4739 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -447,11 +447,11 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
if (const QObjectWrapper *objectWrapper = var.as<QObjectWrapper>())
return instanceOfQObject(typeWrapper, objectWrapper);
- if (const QMetaObject *valueTypeMetaObject
- = QQmlMetaType::metaObjectForValueType(typeWrapper->d()->type())) {
+ const QQmlType type = typeWrapper->d()->type();
+ if (type.isValueType()) {
if (const QQmlValueTypeWrapper *valueWrapper = var.as<QQmlValueTypeWrapper>()) {
return QV4::Encode(QQmlMetaObject::canConvert(valueWrapper->metaObject(),
- valueTypeMetaObject));
+ type.metaObjectForValueType()));
}
// We want "foo as valuetype" to return undefined if it doesn't match.
diff --git a/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml b/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml
new file mode 100644
index 0000000000..69f9316c51
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property list<var> data: []
+ Item {}
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index a69cf1a25a..b0f49c94fe 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -445,6 +445,7 @@ private slots:
void writeNumberToEnumAlias();
void badInlineComponentAnnotation();
void manuallyCallSignalHandler();
+ void overrideDefaultProperty();
private:
QQmlEngine engine;
@@ -8567,6 +8568,19 @@ void tst_qqmllanguage::manuallyCallSignalHandler()
}
}
+void tst_qqmllanguage::overrideDefaultProperty()
+{
+ QQmlEngine e;
+ const QUrl url = testFileUrl("overrideDefaultProperty.qml");
+
+ // Should not crash here!
+
+ QQmlComponent c(&e, url);
+ QVERIFY(c.isError());
+ QCOMPARE(c.errorString(),
+ url.toString() + QLatin1String(":5 Cannot assign object to list property \"data\"\n"));
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"