diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2024-02-14 17:44:58 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-02-15 22:34:49 +0100 |
commit | 4d13ed6a598c142f9ed9928fa0bdcb395e4b94cc (patch) | |
tree | f7b3c2fd6e2407446494c072426a7033dcb1de2d | |
parent | 6b609c796b59e548854888e2c82d46eab9fcc083 (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.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlglobal.cpp | 108 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 19 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype_p.h | 20 | ||||
-rw-r--r-- | src/qml/qml/qqmltype.cpp | 18 | ||||
-rw-r--r-- | src/qml/qml/qqmltype_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmltype_p_p.h | 39 | ||||
-rw-r--r-- | src/qml/qml/qqmltypewrapper.cpp | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/overrideDefaultProperty.qml | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 14 |
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" |