Fix roundtrip conversion between JS var and QVariant
authorLars Knoll <lars.knoll@digia.com>
Mon, 8 Sep 2014 12:06:22 +0000 (14:06 +0200)
committerLars Knoll <lars.knoll@digia.com>
Thu, 11 Sep 2014 07:56:48 +0000 (09:56 +0200)
Always convert null to a QVariant(VoidStar) as documented in
QJSValue. Make sure the reverse conversion will lead back to
a null JS value.

Adjusted two test cases that expected an invalid QVariant when
setting the property to null, and added test cases for the
correct conversion.

Task-number: QTBUG-40880
Change-Id: I6eb01f0067f2c89779c53fd2cd0a1193047ed2cc
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/qml/qqmlproperty.cpp
src/qml/qml/v8/qv8engine.cpp
tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.var.qml
tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.variant.qml
tests/auto/qml/qqmlecmascript/data/variants.qml [new file with mode: 0644]
tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp

index 42bf0dd..09b735a 100644 (file)
@@ -1585,11 +1585,14 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
                     propertyType = propertyMetaObject.className();
             }
         } else if (value.userType() != QVariant::Invalid) {
-            valueType = QMetaType::typeName(value.userType());
+            if (value.userType() == QMetaType::VoidStar)
+                valueType = "null";
+            else
+                valueType = QMetaType::typeName(value.userType());
         }
 
         if (!valueType)
-            valueType = "null";
+            valueType = "undefined";
         if (!propertyType)
             propertyType = QMetaType::typeName(type);
         if (!propertyType)
index 35d31e4..b56af2e 100644 (file)
@@ -240,6 +240,8 @@ QV4::ReturnedValue QV8Engine::fromVariant(const QVariant &variant)
             case QMetaType::UnknownType:
             case QMetaType::Void:
                 return QV4::Encode::undefined();
+            case QMetaType::VoidStar:
+                return QV4::Encode::null();
             case QMetaType::Bool:
                 return QV4::Encode(*reinterpret_cast<const bool*>(ptr));
             case QMetaType::Int:
@@ -379,8 +381,10 @@ QQmlContextData *QV8Engine::callingContext()
 // [Any other object] -> QVariantMap(...)
 QVariant QV8Engine::toBasicVariant(const QV4::ValueRef value)
 {
-    if (value->isNullOrUndefined())
+    if (value->isUndefined())
         return QVariant();
+    if (value->isNull())
+        return QVariant(QMetaType::VoidStar, (void *)0);
     if (value->isBoolean())
         return value->booleanValue();
     if (value->isInteger())
@@ -644,6 +648,8 @@ QV4::ReturnedValue QV8Engine::metaTypeToJS(int type, const void *data)
     case QMetaType::UnknownType:
     case QMetaType::Void:
         return QV4::Encode::undefined();
+    case QMetaType::VoidStar:
+        return QV4::Encode::null();
     case QMetaType::Bool:
         return QV4::Encode(*reinterpret_cast<const bool*>(data));
     case QMetaType::Int:
index 23e4c8d..bb6ddc7 100644 (file)
@@ -17,7 +17,7 @@ QtObject {
     }
 
     function releaseScarceResource() {
-        root.scarceResourceCopy = null;
+        root.scarceResourceCopy = undefined;
     }
 }
 
index fe3707b..e147188 100644 (file)
@@ -17,7 +17,7 @@ QtObject {
     }
 
     function releaseScarceResource() {
-        root.scarceResourceCopy = null;
+        root.scarceResourceCopy = undefined;
     }
 }
 
diff --git a/tests/auto/qml/qqmlecmascript/data/variants.qml b/tests/auto/qml/qqmlecmascript/data/variants.qml
new file mode 100644 (file)
index 0000000..d38c5a1
--- /dev/null
@@ -0,0 +1,55 @@
+import QtQuick 2.2
+
+ListView
+{
+    property variant undefinedVariant: undefined
+    property variant nullVariant: null
+    property variant intVariant: 1
+    property variant doubleVariant: 1.2
+
+    property var testVar
+    property variant testVariant
+
+    function checkNull() {
+        var result = [{'test': null}];
+        model = result;
+        if (model[0].test !== null)
+            return false;
+        testVar = null;
+        testVariant = testVar;
+        if (testVariant !== null)
+            return false;
+        testVar = testVariant;
+        if (testVar !== null)
+            return false;
+        return true;
+    }
+    function checkUndefined() {
+        var result = [{'test': undefined}];
+        model = result;
+        if (model[0].test !== undefined)
+            return false;
+        testVar = undefined;
+        testVariant = testVar;
+        if (testVariant !== undefined)
+            return false;
+        testVar = testVariant;
+        if (testVar !== undefined)
+            return false;
+        return true;
+    }
+    function checkNumber() {
+        var result = [{'test': 1}];
+        model = result;
+        if (model[0].test !== 1)
+            return false;
+        testVar = 1;
+        testVariant = testVar;
+        if (testVariant !== 1)
+            return false;
+        testVar = testVariant;
+        if (testVar !== 1)
+            return false;
+        return true;
+    }
+}
index 06d061a..01125f8 100644 (file)
@@ -213,6 +213,7 @@ private slots:
     void deletedEngine();
     void libraryScriptAssert();
     void variantsAssignedUndefined();
+    void variants();
     void qtbug_9792();
     void qtcreatorbug_1289();
     void noSpuriousWarningsAtShutdown();
@@ -5737,6 +5738,29 @@ void tst_qqmlecmascript::variantsAssignedUndefined()
     delete object;
 }
 
+void tst_qqmlecmascript::variants()
+{
+    QQmlComponent component(&engine, testFileUrl("variants.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QVERIFY(object->property("undefinedVariant").type() == QVariant::Invalid);
+    QVERIFY(object->property("nullVariant").type() == (int)QMetaType::VoidStar);
+    QVERIFY(object->property("intVariant").type() == QVariant::Int);
+    QVERIFY(object->property("doubleVariant").type() == QVariant::Double);
+
+    QVariant result;
+    QMetaObject::invokeMethod(object, "checkNull", Q_RETURN_ARG(QVariant, result));
+    QCOMPARE(result.toBool(), true);
+
+    QMetaObject::invokeMethod(object, "checkUndefined", Q_RETURN_ARG(QVariant, result));
+    QCOMPARE(result.toBool(), true);
+
+    QMetaObject::invokeMethod(object, "checkNumber", Q_RETURN_ARG(QVariant, result));
+    QCOMPARE(result.toBool(), true);
+}
+
 void tst_qqmlecmascript::qtbug_9792()
 {
     QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml"));