Skip to content

Commit

Permalink
Honour the resettable flag of aliased properties
Browse files Browse the repository at this point in the history
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 <qt_sanity_bot@ovi.com>
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
  • Loading branch information
Chris Adams authored and Qt by Nokia committed Aug 4, 2011
1 parent 29af49f commit fd68b86
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/declarative/qml/qdeclarativecompiler.cpp
Expand Up @@ -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());

Expand All @@ -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()];
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/declarative/qml/qdeclarativevmemetaobject.cpp
Expand Up @@ -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;

Expand Down
@@ -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
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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
}
}
@@ -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;
}
}
@@ -0,0 +1,14 @@
import QtQuick 1.0
import Qt.test 1.0

Item {
id: root

AliasPropertyComponent {
sourceComponent: returnsUndefined()
}

function returnsUndefined() {
return undefined;
}
}
@@ -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);
}
}
Expand Up @@ -104,6 +104,7 @@ private slots:
void constantsOverrideBindings();
void outerBindingOverridesInnerBinding();
void aliasPropertyAndBinding();
void aliasPropertyReset();
void nonExistentAttachedObject();
void scope();
void importScope();
Expand Down Expand Up @@ -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<QDeclarativeComponent*>() != 0);
QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
QMetaObject::invokeMethod(object, "resetAliased");
QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 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<QDeclarativeComponent*>() != 0);
QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
QMetaObject::invokeMethod(object, "resetAlias");
QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 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<QDeclarativeComponent*>() != 0);
QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
QMetaObject::invokeMethod(object, "resetAlias");
QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 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<QDeclarativeComponent*>() != 0);
QObject *loader = object->findChild<QObject*>("loader");
QVERIFY(loader != 0);
delete loader;
QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 0); // deletion should have caused value unset.
QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash.
QVERIFY(object->property("sourceComponentAlias").value<QDeclarativeComponent*>() == 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<QDeclarativeComponent*>() == 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<QDeclarativeComponent*>() == 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<int>(), 12);
QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData());
QMetaObject::invokeMethod(object, "resetAlias");
QCOMPARE(object->property("intAlias").value<int>(), 12);
QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
delete object;
}

void tst_qdeclarativeecmascript::dynamicCreation_data()
{
QTest::addColumn<QString>("method");
Expand Down

0 comments on commit fd68b86

Please sign in to comment.