From fd68b868aee89f40138492468745f0e5edb3286e Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Tue, 12 Jul 2011 14:05:24 +1000 Subject: [PATCH] Honour the resettable flag of aliased properties Previously, alias properties were not considered isResettable even if the property they alias is resettable. This commit ensures that the IsResettable flag is set for alias properties iff the aliased property is resettable, and that it is honoured during property reset operations. Task-number: QTBUG-18182 Change-Id: I9cab11923a952df72e976a48489a78b24a34314f Reviewed-on: http://codereview.qt.nokia.com/1471 Reviewed-by: Qt Sanity Bot Reviewed-by: Aaron Kennedy --- src/declarative/qml/qdeclarativecompiler.cpp | 3 + src/declarative/qml/qdeclarativevmemetaobject.cpp | 2 +- .../data/aliasreset/AliasPropertyComponent.qml | 17 ++++ .../data/aliasreset/aliasPropertyReset.1.qml | 23 ++++++ .../data/aliasreset/aliasPropertyReset.2.qml | 23 ++++++ .../data/aliasreset/aliasPropertyReset.3.qml | 31 ++++++++ .../data/aliasreset/aliasPropertyReset.4.qml | 26 ++++++ .../data/aliasreset/aliasPropertyReset.5.qml | 14 ++++ .../data/aliasreset/aliasPropertyReset.error.1.qml | 18 ++++ .../tst_qdeclarativeecmascript.cpp | 82 ++++++++++++++++++++ 10 files changed, 238 insertions(+), 1 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/AliasPropertyComponent.qml create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.1.qml create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.2.qml create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.3.qml create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.4.qml create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.5.qml create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.error.1.qml diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index 5b61421..02c517c 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -2750,6 +2750,7 @@ bool QDeclarativeCompiler::compileAlias(QMetaObjectBuilder &builder, int propIdx = -1; int flags = 0; bool writable = false; + bool resettable = false; if (alias.count() == 2 || alias.count() == 3) { propIdx = indexOfProperty(idObject, alias.at(1).toUtf8()); @@ -2764,6 +2765,7 @@ bool QDeclarativeCompiler::compileAlias(QMetaObjectBuilder &builder, COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); writable = aliasProperty.isWritable(); + resettable = aliasProperty.isResettable(); if (alias.count() == 3) { QDeclarativeValueType *valueType = enginePrivate->valueTypes[aliasProperty.type()]; @@ -2808,6 +2810,7 @@ bool QDeclarativeCompiler::compileAlias(QMetaObjectBuilder &builder, QMetaPropertyBuilder propBuilder = builder.addProperty(prop.name, typeName.constData(), builder.methodCount() - 1); propBuilder.setWritable(writable); + propBuilder.setResettable(resettable); return true; } diff --git a/src/declarative/qml/qdeclarativevmemetaobject.cpp b/src/declarative/qml/qdeclarativevmemetaobject.cpp index 746c9f6..bcd46f2 100644 --- a/src/declarative/qml/qdeclarativevmemetaobject.cpp +++ b/src/declarative/qml/qdeclarativevmemetaobject.cpp @@ -458,7 +458,7 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) } } } - if(c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) { + if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) { if (id >= propOffset) { id -= propOffset; diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/AliasPropertyComponent.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/AliasPropertyComponent.qml new file mode 100644 index 0000000..9135e79 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/AliasPropertyComponent.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 + +Item { + id: apc + property alias sourceComponent: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + objectName: "loader" + sourceComponent: redSquare + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.1.qml new file mode 100644 index 0000000..a27c16d --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.1.qml @@ -0,0 +1,23 @@ +import QtQuick 1.0 +import Qt.test 1.0 + +Item { + id: first + property bool aliasIsUndefined: false + property alias sourceComponentAlias: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + sourceComponent: redSquare + } + + function resetAliased() { + loader.sourceComponent = undefined; + aliasIsUndefined = (sourceComponentAlias == undefined); + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.2.qml new file mode 100644 index 0000000..fa2bb5e --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.2.qml @@ -0,0 +1,23 @@ +import QtQuick 1.0 +import Qt.test 1.0 + +Item { + id: first + property bool loaderSourceComponentIsUndefined: false + property alias sourceComponentAlias: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + sourceComponent: redSquare + } + + function resetAlias() { + sourceComponentAlias = undefined; + loaderSourceComponentIsUndefined = (loader.sourceComponent == undefined); + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.3.qml new file mode 100644 index 0000000..4fa6861 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.3.qml @@ -0,0 +1,31 @@ +import QtQuick 1.0 +import Qt.test 1.0 + +Item { + id: first + property bool loaderTwoSourceComponentIsUndefined: false + property bool loaderOneSourceComponentIsUndefined: false + property alias sourceComponentAlias: loaderOne.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loaderOne + sourceComponent: loaderTwo.sourceComponent + } + + Loader { + id: loaderTwo + sourceComponent: redSquare + x: 15 + } + + function resetAlias() { + sourceComponentAlias = undefined; // loaderOne.sourceComponent should be set to undefined instead of l2.sc + loaderOneSourceComponentIsUndefined = (loaderOne.sourceComponent == undefined); // should be true + loaderTwoSourceComponentIsUndefined = (loaderTwo.sourceComponent == undefined); // should be false + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.4.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.4.qml new file mode 100644 index 0000000..56fcc2e --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.4.qml @@ -0,0 +1,26 @@ +import QtQuick 1.0 +import Qt.test 1.0 + +Item { + id: first + property alias sourceComponentAlias: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + objectName: "loader" + sourceComponent: redSquare + } + + function resetAlias() { + sourceComponentAlias = undefined; // ensure we don't crash after deletion of loader. + } + + function setAlias() { + sourceComponentAlias = redSquare; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.5.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.5.qml new file mode 100644 index 0000000..a046bbf --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.5.qml @@ -0,0 +1,14 @@ +import QtQuick 1.0 +import Qt.test 1.0 + +Item { + id: root + + AliasPropertyComponent { + sourceComponent: returnsUndefined() + } + + function returnsUndefined() { + return undefined; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.error.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.error.1.qml new file mode 100644 index 0000000..22311d6 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasreset/aliasPropertyReset.error.1.qml @@ -0,0 +1,18 @@ +import QtQuick 1.0 +import Qt.test 1.0 + +MyQmlObject { + id: first + property bool aliasedIntIsUndefined: false + property alias intAlias: objprop.intp + + objectProperty: QtObject { + id: objprop + property int intp: 12 + } + + function resetAlias() { + intAlias = undefined; // should error + aliasedIntIsUndefined = (objprop.intp == undefined); + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index c5f7ed1..3efe189 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -104,6 +104,7 @@ private slots: void constantsOverrideBindings(); void outerBindingOverridesInnerBinding(); void aliasPropertyAndBinding(); + void aliasPropertyReset(); void nonExistentAttachedObject(); void scope(); void importScope(); @@ -1038,6 +1039,87 @@ void tst_qdeclarativeecmascript::aliasPropertyAndBinding() delete object; } +/* +Ensure that we can write undefined value to an alias property, +and that the aliased property is reset correctly if possible. +*/ +void tst_qdeclarativeecmascript::aliasPropertyReset() +{ + QObject *object = 0; + + // test that a manual write (of undefined) to a resettable aliased property succeeds + QDeclarativeComponent c1(&engine, TEST_FILE("aliasreset/aliasPropertyReset.1.qml")); + object = c1.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value() != 0); + QCOMPARE(object->property("aliasIsUndefined"), QVariant(false)); + QMetaObject::invokeMethod(object, "resetAliased"); + QVERIFY(object->property("sourceComponentAlias").value() == 0); + QCOMPARE(object->property("aliasIsUndefined"), QVariant(true)); + delete object; + + // test that a manual write (of undefined) to a resettable alias property succeeds + QDeclarativeComponent c2(&engine, TEST_FILE("aliasreset/aliasPropertyReset.2.qml")); + object = c2.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value() != 0); + QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false)); + QMetaObject::invokeMethod(object, "resetAlias"); + QVERIFY(object->property("sourceComponentAlias").value() == 0); + QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true)); + delete object; + + // test that an alias to a bound property works correctly + QDeclarativeComponent c3(&engine, TEST_FILE("aliasreset/aliasPropertyReset.3.qml")); + object = c3.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value() != 0); + QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false)); + QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false)); + QMetaObject::invokeMethod(object, "resetAlias"); + QVERIFY(object->property("sourceComponentAlias").value() == 0); + QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true)); + QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false)); + delete object; + + // test that a manual write (of undefined) to a resettable alias property + // whose aliased property's object has been deleted, does not crash. + QDeclarativeComponent c4(&engine, TEST_FILE("aliasreset/aliasPropertyReset.4.qml")); + object = c4.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value() != 0); + QObject *loader = object->findChild("loader"); + QVERIFY(loader != 0); + delete loader; + QVERIFY(object->property("sourceComponentAlias").value() == 0); // deletion should have caused value unset. + QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash. + QVERIFY(object->property("sourceComponentAlias").value() == 0); + QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything). + QVERIFY(object->property("sourceComponentAlias").value() == 0); + delete object; + + // test that binding an alias property to an undefined value works correctly + QDeclarativeComponent c5(&engine, TEST_FILE("aliasreset/aliasPropertyReset.5.qml")); + object = c5.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value() == 0); // bound to undefined value. + delete object; + + // test that a manual write (of undefined) to a non-resettable property fails properly + QUrl url = TEST_FILE("aliasreset/aliasPropertyReset.error.1.qml"); + QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int"); + QDeclarativeComponent e1(&engine, url); + object = e1.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("intAlias").value(), 12); + QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false)); + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QMetaObject::invokeMethod(object, "resetAlias"); + QCOMPARE(object->property("intAlias").value(), 12); + QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false)); + delete object; +} + void tst_qdeclarativeecmascript::dynamicCreation_data() { QTest::addColumn("method"); -- 1.7.2.5