From 4d3ccce54a5e58bab878170b1552acd0d4a20cec Mon Sep 17 00:00:00 2001 From: Matthew Vogt Date: Tue, 27 Mar 2012 14:58:19 +1000 Subject: Warn on incompatible v4 binding object type For QObject-derived properties, verify that the type of the source object is convertible to the type of the target object, and report an error for incompatible assignment attempts. Task-number: QTBUG-24986 Change-Id: Ieece29a017222e48351cd433c1b2f9e2d93a5fd7 Reviewed-by: Chris Adams Reviewed-by: Roberto Raggi Reviewed-by: Michael Brasser --- src/qml/qml/qqmlproperty.cpp | 21 ++++++++++++++++++--- src/qml/qml/v4/qv4bindings.cpp | 19 +++++++++++++++++++ tests/auto/qml/qqmlproperty/data/invalidBinding.qml | 14 ++++++++++++++ tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 20 ++++++++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qqmlproperty/data/invalidBinding.qml diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 2d459421d9..b798215fa5 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1521,13 +1521,28 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object, return true; const char *valueType = 0; - if (value.userType() == QVariant::Invalid) valueType = "null"; - else valueType = QMetaType::typeName(value.userType()); + const char *propertyType = 0; + + if (value.userType() == QMetaType::QObjectStar) { + if (QObject *o = *(QObject **)value.constData()) { + valueType = o->metaObject()->className(); + + const QMetaObject *propertyMetaObject = rawMetaObjectForType(QQmlEnginePrivate::get(engine), type); + propertyType = propertyMetaObject->className(); + } + } else if (value.userType() != QVariant::Invalid) { + valueType = QMetaType::typeName(value.userType()); + } + + if (!valueType) + valueType = "null"; + if (!propertyType) + propertyType = QMetaType::typeName(type); expression->delayedError()->error.setDescription(QLatin1String("Unable to assign ") + QLatin1String(valueType) + QLatin1String(" to ") + - QLatin1String(QMetaType::typeName(type))); + QLatin1String(propertyType)); return false; } diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp index 5bace305f1..25fbbd4861 100644 --- a/src/qml/qml/v4/qv4bindings.cpp +++ b/src/qml/qml/v4/qv4bindings.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -1656,6 +1657,24 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, if (data.isUndefined()) THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign undefined value")); + if (data.gettype() == QObjectStarType) { + if (QObject *dataObject = data.getQObject()) { + const QMetaObject *dataMo = dataObject->metaObject(); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + QMetaProperty receiver = output->metaObject()->property(instr->store.index); + const QMetaObject *receiverMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, receiver.userType()); + + // Verify that these types are compatible + if (!QQmlPropertyPrivate::canConvert(dataMo, receiverMo)) { + THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign ") + + QLatin1String(dataMo->className()) + + QLatin1String(" to ") + + QLatin1String(receiverMo->className())); + } + } + } + int status = -1; void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; QMetaObject::metacall(output, QMetaObject::WriteProperty, diff --git a/tests/auto/qml/qqmlproperty/data/invalidBinding.qml b/tests/auto/qml/qqmlproperty/data/invalidBinding.qml new file mode 100644 index 0000000000..58e83070ee --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/invalidBinding.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Item { + property Text text: myText + + property Rectangle rectangle1: myText + property Rectangle rectangle2: getMyText() + + function getMyText() { return myText; } + + Text { + id: myText + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 5ef8937dc1..2dde5f003d 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -133,6 +133,7 @@ private slots: void aliasPropertyBindings(); void noContext(); void assignEmptyVariantMap(); + void warnOnInvalidBinding(); void copy(); private: @@ -1708,6 +1709,25 @@ void tst_qqmlproperty::assignEmptyVariantMap() delete obj; } +void tst_qqmlproperty::warnOnInvalidBinding() +{ + QUrl testUrl(testFileUrl("invalidBinding.qml")); + QString expectedWarning; + + // V4 error message for property-to-property binding + expectedWarning = testUrl.toString() + QString::fromLatin1(":6:36: Unable to assign QQuickText to QQuickRectangle"); + QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); + + // V8 error message for function-to-property binding + expectedWarning = testUrl.toString() + QString::fromLatin1(":7:36: Unable to assign QQuickText to QQuickRectangle"); + QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); + + QQmlComponent component(&engine, testUrl); + QObject *obj = component.create(); + QVERIFY(obj); + delete obj; +} + void tst_qqmlproperty::initTestCase() { QQmlDataTest::initTestCase(); -- cgit v1.2.3