Fix composite type property support
authorChris Adams <christopher.adams@nokia.com>
Wed, 9 May 2012 02:21:41 +0000 (12:21 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 16 May 2012 00:14:57 +0000 (02:14 +0200)
The metatype changes caused a regression in composite type properties
for which there was no autotest.  In particular, declarative previously
used the transitional QMetaType::registerType() function, whose
implementation specifies a zero-size, which means that accessing a
property of that type via QObject::property would fail.

This commit adds some unit tests to track the issue, and also fixes the
implementation of QQmlEnginePrivate::registerCompositeType to use the
updated metatype registration functions.

Task-number: QTBUG-25697
Change-Id: Ic4abc1711aee8cf4a50a2ef1769465ef73f5d022
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
src/qml/qml/qqmlengine.cpp
tests/auto/qml/qqmlproperty/data/SecondComponent.qml [new file with mode: 0644]
tests/auto/qml/qqmlproperty/data/componentDir/FirstComponent.qml [new file with mode: 0644]
tests/auto/qml/qqmlproperty/data/registeredCompositeTypeProperty.qml [new file with mode: 0644]
tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp

index 849c83d..788d5ed 100644 (file)
@@ -1651,21 +1651,6 @@ QString QQmlEngine::offlineStoragePath() const
     return d->offlineStoragePath;
 }
 
-static void voidptr_destructor(void *v)
-{
-    void **ptr = (void **)v;
-    delete ptr;
-}
-
-static void *voidptr_constructor(const void *v)
-{
-    if (!v) {
-        return new void*;
-    } else {
-        return new void*(*(void **)v);
-    }
-}
-
 QQmlPropertyCache *QQmlEnginePrivate::createCache(const QMetaObject *mo)
 {
     Q_Q(QQmlEngine);
@@ -1878,10 +1863,22 @@ void QQmlEnginePrivate::registerCompositeType(QQmlCompiledData *data)
     QByteArray ptr = name + '*';
     QByteArray lst = "QQmlListProperty<" + name + '>';
 
-    int ptr_type = QMetaType::registerType(ptr.constData(), voidptr_destructor,
-                                           voidptr_constructor);
-    int lst_type = QMetaType::registerType(lst.constData(), voidptr_destructor,
-                                           voidptr_constructor);
+    int ptr_type = QMetaType::registerNormalizedType(ptr,
+                                                     qMetaTypeDeleteHelper<QObject*>,
+                                                     qMetaTypeCreateHelper<QObject*>,
+                                                     qMetaTypeDestructHelper<QObject*>,
+                                                     qMetaTypeConstructHelper<QObject*>,
+                                                     sizeof(QObject*),
+                                                     static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QObject*>::Flags),
+                                                     data->root);
+    int lst_type = QMetaType::registerNormalizedType(lst,
+                                                     qMetaTypeDeleteHelper<QQmlListProperty<QObject> >,
+                                                     qMetaTypeCreateHelper<QQmlListProperty<QObject> >,
+                                                     qMetaTypeDestructHelper<QQmlListProperty<QObject> >,
+                                                     qMetaTypeConstructHelper<QQmlListProperty<QObject> >,
+                                                     sizeof(QQmlListProperty<QObject>),
+                                                     static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject> >::Flags),
+                                                     static_cast<QMetaObject*>(0));
 
     data->addref();
 
diff --git a/tests/auto/qml/qqmlproperty/data/SecondComponent.qml b/tests/auto/qml/qqmlproperty/data/SecondComponent.qml
new file mode 100644 (file)
index 0000000..45d81c9
--- /dev/null
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Item {
+    property string a: "Hello, World"
+    property bool b: false
+}
diff --git a/tests/auto/qml/qqmlproperty/data/componentDir/FirstComponent.qml b/tests/auto/qml/qqmlproperty/data/componentDir/FirstComponent.qml
new file mode 100644 (file)
index 0000000..1b7614d
--- /dev/null
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+    property int a: 10
+}
diff --git a/tests/auto/qml/qqmlproperty/data/registeredCompositeTypeProperty.qml b/tests/auto/qml/qqmlproperty/data/registeredCompositeTypeProperty.qml
new file mode 100644 (file)
index 0000000..92e1675
--- /dev/null
@@ -0,0 +1,32 @@
+import QtQuick 2.0
+import "componentDir"
+
+Item {
+    id: root
+
+    property FirstComponent first: FirstComponent { }
+    property FirstComponent second
+    property SecondComponent third: SecondComponent { }
+
+    property list<FirstComponent> fclist: [
+        FirstComponent {
+            a: 15
+        }
+    ]
+    property list<SecondComponent> sclistOne: [
+        SecondComponent {
+            a: "G'day, World"
+        },
+        SecondComponent { },
+        SecondComponent {
+            b: true
+        }
+    ]
+    property list<SecondComponent> sclistTwo
+
+    Component.onCompleted: {
+        var c1 = Qt.createComponent("./componentDir/FirstComponent.qml");
+        var o1 = c1.createObject(root);
+        second = o1;
+    }
+}
index a65693b..3957dee 100644 (file)
@@ -135,6 +135,7 @@ private slots:
     void noContext();
     void assignEmptyVariantMap();
     void warnOnInvalidBinding();
+    void registeredCompositeTypeProperty();
 
     void copy();
 private:
@@ -189,6 +190,73 @@ void tst_qqmlproperty::qmlmetaproperty()
     delete obj;
 }
 
+void tst_qqmlproperty::registeredCompositeTypeProperty()
+{
+    // Composite type properties
+    {
+        QQmlEngine engine;
+        QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeProperty.qml"));
+        QObject *obj = component.create();
+        QVERIFY(obj);
+
+        // create property accessors and check types.
+        QQmlProperty p1(obj, "first");
+        QQmlProperty p2(obj, "second");
+        QQmlProperty p3(obj, "third");
+        QVERIFY(p1.propertyType() == p2.propertyType());
+        QVERIFY(p1.propertyType() != p3.propertyType());
+
+        // check that the values are retrievable from CPP
+        QVariant first = obj->property("first");
+        QVariant second = obj->property("second");
+        QVariant third = obj->property("third");
+        QVERIFY(first.isValid());
+        QVERIFY(second.isValid());
+        QVERIFY(third.isValid());
+        // ensure that conversion from qobject-derived-ptr to qobject-ptr works.
+        QVERIFY(first.value<QObject*>());
+        QVERIFY(second.value<QObject*>());
+        QVERIFY(third.value<QObject*>());
+
+        // check that the values retrieved via QQmlProperty match.
+        QCOMPARE(p1.read(), first);
+        QCOMPARE(p2.read(), second);
+        QCOMPARE(p3.read(), third);
+
+        delete obj;
+    }
+
+    // List-of-composite-type type properties
+    {
+        QQmlEngine engine;
+        QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeProperty.qml"));
+        QObject *obj = component.create();
+        QVERIFY(obj);
+
+        // create list property accessors and check types
+        QQmlProperty lp1(obj, "fclist");
+        QQmlProperty lp2(obj, "sclistOne");
+        QQmlProperty lp3(obj, "sclistTwo");
+        QVERIFY(lp1.propertyType() != lp2.propertyType());
+        QVERIFY(lp2.propertyType() == lp3.propertyType());
+
+        // check that the list values are retrievable from CPP
+        QVariant firstList = obj->property("fclist");
+        QVariant secondList = obj->property("sclistOne");
+        QVariant thirdList = obj->property("sclistTwo");
+        QVERIFY(firstList.isValid());
+        QVERIFY(secondList.isValid());
+        QVERIFY(thirdList.isValid());
+
+        // check that the values retrieved via QQmlProperty match.
+        QCOMPARE(lp1.read(), firstList);
+        QCOMPARE(lp2.read(), secondList);
+        QCOMPARE(lp3.read(), thirdList);
+
+        delete obj;
+    }
+}
+
 class PropertyObject : public QObject
 {
     Q_OBJECT