diff options
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject.cpp | 8 | ||||
-rw-r--r-- | src/qml/qml/qqmlbuiltinfunctions.cpp | 22 | ||||
-rw-r--r-- | src/qml/qml/qqmlglobal.cpp | 280 | ||||
-rw-r--r-- | src/qml/qml/qqmlglobal_p.h | 10 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 19 | ||||
-rw-r--r-- | src/qml/qml/qqmlproperty.cpp | 15 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertyvalidator.cpp | 6 | ||||
-rw-r--r-- | src/qml/qml/qqmlstringconverters.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/qqmlvaluetypeproviders/testtypes.h | 130 | ||||
-rw-r--r-- | tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp | 67 |
12 files changed, 416 insertions, 159 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 56cc4efb48..41c7a90906 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1569,9 +1569,9 @@ static QVariant toVariant( QV4::ScopedValue arrayValue(scope); for (qint64 i = 0; i < length; ++i) { arrayValue = a->get(i); - QVariant asVariant(valueMetaType); - if (QQmlValueTypeProvider::createValueType( - arrayValue, valueMetaType, asVariant.data())) { + QVariant asVariant = QQmlValueTypeProvider::createValueType( + arrayValue, valueMetaType); + if (asVariant.isValid()) { retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData()); continue; } @@ -1662,8 +1662,8 @@ static QVariant toVariant( #endif if (metaType.isValid() && !(metaType.flags() & QMetaType::PointerToQObject)) { - QVariant result(metaType); - if (QQmlValueTypeProvider::createValueType(value, metaType, result.data())) + const QVariant result = QQmlValueTypeProvider::createValueType(value, metaType); + if (result.isValid()) return result; } diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 132084705c..d33466a7fa 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -714,9 +714,9 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin } else { const QMetaType originalType = variant.metaType(); if (originalType != valueMetaType) { - QVariant converted(valueMetaType); - if (QQmlValueTypeProvider::createValueType( - variant, valueMetaType, converted.data())) { + const QVariant converted = QQmlValueTypeProvider::createValueType( + variant, valueMetaType); + if (converted.isValid()) { variant = converted; } else if (!variant.convert(valueMetaType)) { qWarning().noquote() @@ -725,7 +725,7 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin .arg(QString::number(i), QString::fromUtf8(originalType.name()), QString::fromUtf8(valueMetaType.name())); - variant = converted; + variant = QVariant(valueMetaType); } } meta->addValueAtEnd(result.data(), variant.constData()); diff --git a/src/qml/qml/qqmlbuiltinfunctions.cpp b/src/qml/qml/qqmlbuiltinfunctions.cpp index 36111f32a6..9d75073e84 100644 --- a/src/qml/qml/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/qqmlbuiltinfunctions.cpp @@ -475,8 +475,9 @@ QVariant QtObject::font(const QJSValue &fontSpecifier) const } { - QVariant v((QMetaType(QMetaType::QFont))); - if (QQmlValueTypeProvider::constructFromJSValue(fontSpecifier, v.metaType(), v.data())) + const QVariant v = QQmlValueTypeProvider::createValueType( + fontSpecifier, QMetaType(QMetaType::QFont)); + if (v.isValid()) return v; } @@ -511,9 +512,8 @@ static QVariant constructFromJSValue(QJSEngine *e, QMetaType type, T... paramete return QVariant(); QJSValue params = e->newArray(sizeof...(parameters)); addParameters(e, params, 0, parameters...); - QVariant variant(type); - QQmlValueTypeProvider::constructFromJSValue(params, type, variant.data()); - return variant; + const QVariant variant = QQmlValueTypeProvider::createValueType(params, type); + return variant.isValid() ? variant : QVariant(type); } /*! @@ -563,10 +563,9 @@ QVariant QtObject::quaternion(double scalar, double x, double y, double z) const */ QVariant QtObject::matrix4x4() const { - QVariant variant((QMetaType(QMetaType::QMatrix4x4))); - QQmlValueTypeProvider::constructFromJSValue( - QJSValue(), variant.metaType(), variant.data()); - return variant; + const QMetaType metaType(QMetaType::QMatrix4x4); + const QVariant variant = QQmlValueTypeProvider::createValueType(QJSValue(), metaType); + return variant.isValid() ? variant : QVariant(metaType); } /*! @@ -587,8 +586,9 @@ QVariant QtObject::matrix4x4() const QVariant QtObject::matrix4x4(const QJSValue &value) const { if (value.isObject()) { - QVariant v((QMetaType(QMetaType::QMatrix4x4))); - if (QQmlValueTypeProvider::constructFromJSValue(value, v.metaType(), v.data())) + QVariant v = QQmlValueTypeProvider::createValueType( + value, QMetaType(QMetaType::QMatrix4x4)); + if (v.isValid()) return v; } diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index 265b26b1e0..90889c2ea4 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -1,15 +1,15 @@ // 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 -#include <private/qqmlglobal_p.h> -#include <QtQml/private/qqmlmetatype_p.h> #include <QtQml/private/qjsvalue_p.h> - +#include <QtQml/private/qqmlglobal_p.h> +#include <QtQml/private/qqmlmetatype_p.h> #include <QtQml/qqmlengine.h> -#include <QtCore/qvariant.h> -#include <QtCore/qstringlist.h> + +#include <QtCore/private/qvariant_p.h> +#include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> -#include <QtCore/QCoreApplication> +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE @@ -70,21 +70,32 @@ static bool isConstructibleMetaType(const QMetaType metaType) return true; } +static void *createVariantData(QMetaType type, QVariant *variant) +{ + const QtPrivate::QMetaTypeInterface *iface = type.iface(); + QVariant::Private *d = &variant->data_ptr(); + Q_ASSERT(d->is_null && !d->is_shared); + *d = QVariant::Private(iface); + if (QVariant::Private::canUseInternalSpace(iface)) + return d->data.data; + + // This is not exception safe. + // If your value type throws an exception from its ctor bad things will happen anyway. + d->data.shared = QVariant::PrivateShared::create(iface->size, iface->alignment); + d->is_shared = true; + return d->data.shared->data(); +} + static void callConstructor( - const QMetaObject *mo, int i, void *parameter, QMetaType metaType, void *data) + const QMetaObject *mo, int i, void *parameter, void *data) { - // Unfortunately CreateInstance unconditionally creates the instance on the heap. - void *gadget = nullptr; - void *p[] = { &gadget, parameter }; - mo->static_metacall(QMetaObject::CreateInstance, i, p); - Q_ASSERT(gadget); - metaType.destruct(data); - metaType.construct(data, gadget); - metaType.destroy(gadget); + void *p[] = { data, parameter }; + mo->static_metacall(QMetaObject::ConstructInPlace, i, p); } +template<typename Allocate> static bool fromMatchingType( - const QMetaObject *mo, const QV4::Value &s, const QMetaType metaType, void *data) + const QMetaObject *mo, const QV4::Value &s, Allocate &&allocate) { for (int i = 0, end = mo->constructorCount(); i < end; ++i) { const QMetaMethod ctor = mo->constructor(i); @@ -94,19 +105,19 @@ static bool fromMatchingType( const QMetaType parameterType = ctor.parameterMetaType(0); QVariant parameter = QV4::ExecutionEngine::toVariant(s, parameterType); if (parameter.metaType() == parameterType) { - callConstructor(mo, i, parameter.data(), metaType, data); + callConstructor(mo, i, parameter.data(), allocate()); return true; } - QVariant converted(parameterType); - if (QQmlValueTypeProvider::createValueType(s, parameterType, converted.data())) { - callConstructor(mo, i, converted.data(), metaType, data); + QVariant converted = QQmlValueTypeProvider::createValueType(s, parameterType); + if (converted.isValid()) { + callConstructor(mo, i, converted.data(), allocate()); return true; } if (QMetaType::convert(parameter.metaType(), parameter.constData(), parameterType, converted.data())) { - callConstructor(mo, i, converted.data(), metaType, data); + callConstructor(mo, i, converted.data(), allocate()); return true; } } @@ -114,14 +125,14 @@ static bool fromMatchingType( return false; } -static bool fromMatchingType( - const QMetaObject *mo, QVariant s, const QMetaType metaType, void *data) +template<typename Allocate> +static bool fromMatchingType(const QMetaObject *mo, QVariant s, Allocate &&allocate) { const QMetaType sourceMetaType = s.metaType(); if (sourceMetaType == QMetaType::fromType<QJSValue>()) { QJSValue val = s.value<QJSValue>(); - return fromMatchingType( - mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), metaType, data); + return fromMatchingType(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), + std::forward<Allocate>(allocate)); } for (int i = 0, end = mo->constructorCount(); i < end; ++i) { @@ -131,20 +142,20 @@ static bool fromMatchingType( const QMetaType parameterType = ctor.parameterMetaType(0); if (sourceMetaType == parameterType) { - callConstructor(mo, i, s.data(), metaType, data); + callConstructor(mo, i, s.data(), allocate()); return true; } - QVariant parameter(parameterType); - if (QQmlValueTypeProvider::createValueType(s, parameterType, parameter.data())) { - callConstructor(mo, i, parameter.data(), metaType, data); + QVariant parameter = QQmlValueTypeProvider::createValueType(s, parameterType); + if (parameter.isValid()) { + callConstructor(mo, i, parameter.data(), allocate()); return true; } // At this point, s should be a builtin type. For builtin types // the QMetaType converters are good enough. if (QMetaType::convert(sourceMetaType, s.constData(), parameterType, parameter.data())) { - callConstructor(mo, i, parameter.data(), metaType, data); + callConstructor(mo, i, parameter.data(), allocate()); return true; } } @@ -152,8 +163,8 @@ static bool fromMatchingType( return false; } -static bool fromString( - const QMetaObject *mo, QString s, const QMetaType metaType, void *data) +template<typename Allocate> +static bool fromString(const QMetaObject *mo, QString s, Allocate &&allocate) { for (int i = 0, end = mo->constructorCount(); i < end; ++i) { const QMetaMethod ctor = mo->constructor(i); @@ -161,24 +172,16 @@ static bool fromString( continue; if (ctor.parameterMetaType(0) == QMetaType::fromType<QString>()) { - callConstructor(mo, i, &s, metaType, data); + callConstructor(mo, i, &s, allocate()); return true; } } - return false; } -static bool byProperties( - const QMetaObject *mo, const QV4::Value &s, void *data) +static void doWriteProperties(const QMetaObject *mo, const QV4::Value &s, void *target) { - if (!s.isObject()) - return false; - - if (!mo) - return false; - const QV4::Object *o = static_cast<const QV4::Object *>(&s); QV4::Scope scope(o->engine()); QV4::ScopedObject object(scope, o); @@ -198,19 +201,20 @@ static bool byProperties( const QMetaType propertyType = metaProperty.metaType(); QVariant property = QV4::ExecutionEngine::toVariant(v4PropValue, propertyType); if (property.metaType() == propertyType) { - metaProperty.writeOnGadget(data, property); + metaProperty.writeOnGadget(target, property); continue; } - QVariant converted(propertyType); - if (QQmlValueTypeProvider::createValueType(v4PropValue, propertyType, converted.data())) { - metaProperty.writeOnGadget(data, converted); + QVariant converted = QQmlValueTypeProvider::createValueType(v4PropValue, propertyType); + if (converted.isValid()) { + metaProperty.writeOnGadget(target, converted); continue; } + converted = QVariant(propertyType); if (QMetaType::convert(property.metaType(), property.constData(), propertyType, converted.data())) { - metaProperty.writeOnGadget(data, converted); + metaProperty.writeOnGadget(target, converted); continue; } @@ -219,31 +223,75 @@ static bool byProperties( .arg(v4PropValue->toQStringNoThrow(), QString::fromUtf8(propertyType.name()), propertyName); } - return true; } -static bool byProperties( - const QMetaObject *mo, const QVariant &s, void *data) +static QVariant byProperties(const QMetaObject *mo, QMetaType metaType, const QV4::Value &s) { - if (!mo) - return false; + if (!s.isObject() || !mo) + return QVariant(); - if (s.metaType() == QMetaType::fromType<QJSValue>()) { - QJSValue val = s.value<QJSValue>(); - return byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&val)), data); - } + QVariant result(metaType); + doWriteProperties(mo, s, result.data()); + return result; +} - return false; +static QVariant byProperties(const QMetaObject *mo, QMetaType metaType, const QVariant &s) +{ + if (!mo || s.metaType() != QMetaType::fromType<QJSValue>()) + return QVariant(); + + QJSValue val = s.value<QJSValue>(); + return byProperties(mo, metaType, QV4::Value(QJSValuePrivate::asReturnedValue(&val))); } -static bool fromJSValue( - const QQmlType &type, const QJSValue &s, QMetaType metaType, void *data) +/*! + * \internal + * Specialization that creates the value type in place at \a target, which is expected to be + * already initialized. This is more efficient if we can do byProperties() since it can use a + * pre-constructed object. It also avoids the creation of a QVariant in most cases. It is less + * efficient if you're going to create a QVariant anyway. + */ +bool QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType metaType, void *target) { + if (!isConstructibleMetaType(metaType)) + return false; + + auto destruct = [metaType, target]() { + metaType.destruct(target); + return target; + }; + + 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()) { + if (s.isObject() && mo) { + doWriteProperties(mo, s, target); + return true; + } + if (type.canConstructValueType()) { + if (fromMatchingType(mo, s, destruct)) + return true; + warn(); + + } + } else if (type.canConstructValueType()) { + if (fromMatchingType(mo, s, destruct)) + return true; + warn(); + } + } + if (const auto valueTypeFunction = type.createValueTypeFunction()) { - QVariant result = valueTypeFunction(s); + const QVariant result + = valueTypeFunction(QJSValuePrivate::fromReturnedValue(s.asReturnedValue())); if (result.metaType() == metaType) { - metaType.destruct(data); - metaType.construct(data, result.constData()); + metaType.construct(destruct(), result.constData()); return true; } } @@ -251,70 +299,69 @@ static bool fromJSValue( return false; } -bool QQmlValueTypeProvider::constructFromJSValue( - const QJSValue &s, QMetaType metaType, void *data) +static QVariant fromJSValue(const QQmlType &type, const QJSValue &s, QMetaType metaType) { - return isConstructibleMetaType(metaType) - && fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType, data); + if (const auto valueTypeFunction = type.createValueTypeFunction()) { + const QVariant result = valueTypeFunction(s); + if (result.metaType() == metaType) + return result; + } + + return QVariant(); } -bool QQmlValueTypeProvider::createValueType( - const QString &s, QMetaType metaType, void *data) +QVariant QQmlValueTypeProvider::createValueType(const QJSValue &s, QMetaType metaType) { if (!isConstructibleMetaType(metaType)) - return false; - const QQmlType type = QQmlMetaType::qmlType(metaType); - const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type); - if (mo && type.canConstructValueType()) { - if (fromString(mo, s, metaType, data)) - return true; - } - - return fromJSValue(type, s, metaType, data); + return QVariant(); + return fromJSValue(QQmlMetaType::qmlType(metaType), s, metaType); } -bool QQmlValueTypeProvider::createValueType( - const QJSValue &s, QMetaType metaType, void *data) +QVariant QQmlValueTypeProvider::createValueType(const QString &s, QMetaType metaType) { if (!isConstructibleMetaType(metaType)) - return false; + return QVariant(); const QQmlType type = QQmlMetaType::qmlType(metaType); - if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) { - if (type.canPopulateValueType() - && byProperties(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)), data)) { - return true; - } - - if (type.canConstructValueType() - && fromMatchingType(mo, QV4::Value(QJSValuePrivate::asReturnedValue(&s)), - metaType, data)) { - return true; - } + const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type); + if (mo && type.canConstructValueType()) { + QVariant result; + if (fromString(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; } - return fromJSValue(type, s, metaType, data); + return fromJSValue(type, s, metaType); } -bool QQmlValueTypeProvider::createValueType( - const QV4::Value &s, QMetaType metaType, void *data) +QVariant QQmlValueTypeProvider::createValueType(const QV4::Value &s, QMetaType metaType) { if (!isConstructibleMetaType(metaType)) - return false; + return QVariant(); const QQmlType type = QQmlMetaType::qmlType(metaType); if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) { - if (type.canPopulateValueType() && byProperties(mo, s, data)) - return true; - if (type.canConstructValueType()) { - if (fromMatchingType(mo, s, metaType, data)) - return true; + const auto warn = [&]() { qWarning().noquote() << "Could not find any constructor for value type" << mo->className() << "to call with value" << s.toQStringNoThrow(); + }; + + if (type.canPopulateValueType()) { + 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(); + } + } else if (type.canConstructValueType()) { + QVariant result; + if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; + warn(); } } - return fromJSValue( - type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType, data); + return fromJSValue(type, QJSValuePrivate::fromReturnedValue(s.asReturnedValue()), metaType); } @@ -322,25 +369,36 @@ bool QQmlValueTypeProvider::createValueType( * \internal * This should only be called with either builtin types or wrapped QJSValues as source. */ -bool QQmlValueTypeProvider::createValueType( - const QVariant &s, QMetaType metaType, void *data) +QVariant QQmlValueTypeProvider::createValueType(const QVariant &s, QMetaType metaType) { if (!isConstructibleMetaType(metaType)) - return false; + return QVariant(); const QQmlType type = QQmlMetaType::qmlType(metaType); if (const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(type)) { - if (type.canPopulateValueType() && byProperties(mo, s, data)) - return true; - if (type.canConstructValueType()) { - if (fromMatchingType(mo, s, metaType, data)) - return true; + const auto warn = [&]() { qWarning().noquote() << "Could not find any constructor for value type" << mo->className() << "to call with value" << s; + }; + + if (type.canPopulateValueType()) { + 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(); + } + } else if (type.canConstructValueType()) { + QVariant result; + if (fromMatchingType(mo, s, [&]() { return createVariantData(metaType, &result); })) + return result; + warn(); } } - return false; + return QVariant(); } QQmlColorProvider::~QQmlColorProvider() {} diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 918f95993f..e56ae73edc 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -189,12 +189,12 @@ inline void QQml_setParent_noEvent(QObject *object, QObject *parent) class QQmlValueTypeProvider { public: - static bool constructFromJSValue(const QJSValue &, QMetaType, void *); - - static bool createValueType(const QString &, QMetaType, void *); - static bool createValueType(const QJSValue &, QMetaType, void *); static bool createValueType(const QV4::Value &, QMetaType, void *); - static bool createValueType(const QVariant &, QMetaType, void *); + + static QVariant createValueType(const QJSValue &, QMetaType); + static QVariant createValueType(const QString &, QMetaType); + static QVariant createValueType(const QV4::Value &, QMetaType); + static QVariant createValueType(const QVariant &, QMetaType); }; class Q_QML_PRIVATE_EXPORT QQmlColorProvider diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index c014c24d43..46fe25b1ba 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -426,9 +426,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } break; case QMetaType::QColor: { - QVariant data(propertyType); - if (QQmlValueTypeProvider::createValueType( - compilationUnit->bindingValueAsString(binding), propertyType, data.data())) { + QVariant data = QQmlValueTypeProvider::createValueType( + compilationUnit->bindingValueAsString(binding), propertyType); + if (data.isValid()) { property->writeProperty(_qobject, data.data(), propertyWriteFlags); } } @@ -509,12 +509,9 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QMetaType::QVector3D: case QMetaType::QVector4D: case QMetaType::QQuaternion: { - QVariant result(propertyType); - bool ok = QQmlValueTypeProvider::createValueType( - compilationUnit->bindingValueAsString(binding), - result.metaType(), result.data()); - assertOrNull(ok); - Q_UNUSED(ok); + QVariant result = QQmlValueTypeProvider::createValueType( + compilationUnit->bindingValueAsString(binding), propertyType); + assertOrNull(result.isValid()); property->writeProperty(_qobject, result.data(), propertyWriteFlags); break; } @@ -602,8 +599,8 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const break; } - QVariant target(propertyType); - if (QQmlValueTypeProvider::createValueType(source, propertyType, target.data())) { + QVariant target = QQmlValueTypeProvider::createValueType(source, propertyType); + if (target.isValid()) { property->writeProperty(_qobject, target.data(), propertyWriteFlags); break; } diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 306f733e21..3c039ba55d 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1423,14 +1423,15 @@ static ConvertAndAssignResult tryConvertAndAssign( } } - QVariant converted(propertyMetaType); - if (QQmlValueTypeProvider::createValueType(value, propertyMetaType, converted.data()) - || QMetaType::convert(value.metaType(), value.constData(), - propertyMetaType, converted.data())) { - return {true, property.writeProperty(object, converted.data(), flags)}; + QVariant converted = QQmlValueTypeProvider::createValueType(value, propertyMetaType); + if (!converted.isValid()) { + converted = QVariant(propertyMetaType); + if (!QMetaType::convert(value.metaType(), value.constData(), + propertyMetaType, converted.data())) { + return {false, false}; + } } - - return {false, false}; + return {true, property.writeProperty(object, converted.data(), flags)}; }; template<typename Op> diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index c6783842dc..308b66e2d9 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -568,10 +568,10 @@ QQmlError QQmlPropertyValidator::validateLiteralBinding( default: return QString(); } }; - QVariant result(property->propType()); - if (!QQmlValueTypeProvider::createValueType( + const QVariant result = QQmlValueTypeProvider::createValueType( compilationUnit->bindingValueAsString(binding), - result.metaType(), result.data())) { + property->propType()); + if (!result.isValid()) { return warnOrError(tr("Invalid property assignment: %1 expected") .arg(typeName())); } diff --git a/src/qml/qml/qqmlstringconverters.cpp b/src/qml/qml/qqmlstringconverters.cpp index c1625b6667..d2222dc338 100644 --- a/src/qml/qml/qqmlstringconverters.cpp +++ b/src/qml/qml/qqmlstringconverters.cpp @@ -40,8 +40,8 @@ QVariant QQmlStringConverters::variantFromString(const QString &s, QMetaType pre case QMetaType::QRect: return QVariant::fromValue(rectFFromString(s, ok).toRect()); default: { - QVariant ret(preferredType); - if (QQmlValueTypeProvider::createValueType(s, preferredType, ret.data())) { + const QVariant ret = QQmlValueTypeProvider::createValueType(s, preferredType); + if (ret.isValid()) { if (ok) *ok = true; return ret; diff --git a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp index 1746957872..7569556121 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp +++ b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.cpp @@ -2,10 +2,14 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "testtypes.h" +QList<Padding::LogEntry> Padding::log; + void registerTypes() { qmlRegisterType<MyTypeObject>("Test", 1, 0, "MyTypeObject"); qmlRegisterTypesAndRevisions<ConstructibleValueType>("Test", 1); qmlRegisterTypesAndRevisions<ConstructibleFromQReal>("Test", 1); qmlRegisterTypesAndRevisions<StructuredValueType>("Test", 1); + qmlRegisterTypesAndRevisions<Padding>("Test", 1); + qmlRegisterTypesAndRevisions<MyItem>("Test", 1); } diff --git a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h index ca1d1b2b7a..fa8eaaf202 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h +++ b/tests/auto/qml/qqmlvaluetypeproviders/testtypes.h @@ -331,6 +331,136 @@ private: QVariant m_aVariant; }; +class Padding +{ + Q_GADGET + + Q_PROPERTY(int left READ left WRITE setLeft) + Q_PROPERTY(int right READ right WRITE setRight) + + QML_VALUE_TYPE(padding) + QML_STRUCTURED_VALUE + +public: + enum LogType { + DefaultCtor, + CopyCtor, + MoveCtor, + CopyAssign, + MoveAssign, + InvokableCtor, + CustomCtor, + Invalid, + }; + + Q_ENUM(LogType); + + struct LogEntry { + LogType type = Invalid; + int left = 0; + int right = 0; + + friend QDebug operator<<(QDebug &debug, const LogEntry &self) + { + return debug << self.type << " " << self.left << " " << self.right; + } + }; + + static QList<LogEntry> log; + + void doLog(LogType type) { + log.append({ + type, m_left, m_right + }); + } + + Padding() + { + doLog(DefaultCtor); + } + + Padding(const Padding &other) + : m_left(other.m_left) + , m_right(other.m_right) + { + doLog(CopyCtor); + } + + Padding(Padding &&other) + : m_left(other.m_left) + , m_right(other.m_right) + { + doLog(MoveCtor); + } + + Padding(int left, int right) + : m_left( left ) + , m_right( right ) + { + doLog(CustomCtor); + } + + Padding &operator=(const Padding &other) { + if (this != &other) { + m_left = other.m_left; + m_right = other.m_right; + } + doLog(CopyAssign); + return *this; + } + + Padding &operator=(Padding &&other) { + if (this != &other) { + m_left = other.m_left; + m_right = other.m_right; + } + doLog(MoveAssign); + return *this; + } + + Q_INVOKABLE Padding(int padding ) + : m_left( padding ) + , m_right( padding ) + { + doLog(InvokableCtor); + } + + void setLeft(int padding) { m_left = padding; } + int left() const { return m_left; } + + void setRight(int padding) { m_right = padding; } + int right() const { return m_right; } + +private: + int m_left = 0; + int m_right = 0; +}; + +class MyItem : public QObject +{ + Q_OBJECT + Q_PROPERTY(Padding padding READ padding WRITE setPadding NOTIFY paddingChanged) + QML_ELEMENT + +public: + void setPadding(const Padding &padding) + { + if (padding.left() == m_padding.left() && padding.right() == m_padding.right()) + return; + + m_padding = padding; + emit paddingChanged(); + } + + const Padding &padding() const{ return m_padding; } + +signals: + void paddingChanged(); + +private: + Padding m_padding{ 17, 17 }; +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp index 25e24e5f40..5dd7759509 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp +++ b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp @@ -41,6 +41,7 @@ private slots: void structured(); void recursive(); void date(); + void constructors(); }; void tst_qqmlvaluetypeproviders::initTestCase() @@ -386,6 +387,72 @@ void tst_qqmlvaluetypeproviders::date() QCOMPARE(o->property("aVariant").value<QDateTime>().time().minute(), 44); } +void tst_qqmlvaluetypeproviders::constructors() +{ + + QQmlEngine e; + + { + const auto guard = qScopeGuard([]() { Padding::log.clear(); }); + QQmlComponent component(&e); + component.setData("import Test\nMyItem { padding : 50 }", QUrl()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + MyItem *item = qobject_cast<MyItem *>(o.data()); + QVERIFY(item); + QCOMPARE(item->padding().left(), 50); + QCOMPARE(item->padding().right(), 50); + + QCOMPARE(Padding::log.length(), 3); + + // Created by default ctor of MyItem + QCOMPARE(Padding::log[0].type, Padding::CustomCtor); + QCOMPARE(Padding::log[0].left, 17); + QCOMPARE(Padding::log[0].right, 17); + + // Created by assignment of integer + QCOMPARE(Padding::log[1].type, Padding::InvokableCtor); + QCOMPARE(Padding::log[1].left, 50); + QCOMPARE(Padding::log[1].right, 50); + + // In MyItem::setPadding() + QCOMPARE(Padding::log[2].type, Padding::CopyAssign); + QCOMPARE(Padding::log[2].left, 50); + QCOMPARE(Padding::log[2].right, 50); + } + + { + const auto guard = qScopeGuard([]() { Padding::log.clear(); }); + QQmlComponent component(&e); + component.setData("import Test\nMyItem { padding: ({ left: 10, right: 20 }) }", QUrl()); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + MyItem *item = qobject_cast<MyItem *>(o.data()); + QVERIFY(item); + QCOMPARE(item->padding().left(), 10); + QCOMPARE(item->padding().right(), 20); + + QCOMPARE(Padding::log.length(), 3); + + // Created by default ctor of MyItem + QCOMPARE(Padding::log[0].type, Padding::CustomCtor); + QCOMPARE(Padding::log[0].left, 17); + QCOMPARE(Padding::log[0].right, 17); + + // Preparing for setting properties of structured value + QCOMPARE(Padding::log[1].type, Padding::DefaultCtor); + QCOMPARE(Padding::log[1].left, 0); + QCOMPARE(Padding::log[1].right, 0); + + // In MyItem::setPadding() + QCOMPARE(Padding::log[2].type, Padding::CopyAssign); + QCOMPARE(Padding::log[2].left, 10); + QCOMPARE(Padding::log[2].right, 20); + } +} + QTEST_MAIN(tst_qqmlvaluetypeproviders) #include "tst_qqmlvaluetypeproviders.moc" |