Unwrap QJSValue from QVariant in QV8Engine::fromVariant
authorJędrzej Nowacki <jedrzej.nowacki@nokia.com>
Fri, 16 Sep 2011 12:06:02 +0000 (14:06 +0200)
committerQt by Nokia <qt-info@nokia.com>
Mon, 19 Sep 2011 11:25:39 +0000 (13:25 +0200)
When QML tries to unwrap real value from a QVariant and
the value is a QJSValue instance, then no conversion is needed,
QJSValue already contains a v8 handle.

This patch, for example, solves a problem of emitting QJSValue
instance in a signal that has QVariant as an argument. The QJSValue
can be unwrapped and used as a normal JS value in a connected slot.
This feature may be used also in a plugin model that stores QJSValues
internally. Then the model in data() function can return a QJSValue
which would be understood by QML.

Change-Id: I1d5ede40ce2637123b09839fd848b27ad3af3dda
Reviewed-on: http://codereview.qt-project.org/4451
Reviewed-by: Kent Hansen <kent.hansen@nokia.com>
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
src/declarative/qml/v8/qv8engine.cpp
tests/auto/declarative/qdeclarativeecmascript/data/signalWithJSValueInVariant.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/testtypes.h
tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp

index c10f048..7be74f8 100644 (file)
@@ -341,6 +341,11 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
             } else {
                 return v8::Null();
             }
+        } else if (type == qMetaTypeId<QJSValue>()) {
+            const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr);
+            QJSValuePrivate *valuep = QJSValuePrivate::get(*value);
+            if (valuep->assignEngine(this))
+                return v8::Local<v8::Value>::New(*valuep);
         } else if (type == qMetaTypeId<QList<QObject *> >()) {
             // XXX Can this be made more by using Array as a prototype and implementing
             // directly against QList<QObject*>?
@@ -358,7 +363,6 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
     }
 
     // XXX TODO: To be compatible, we still need to handle:
-    //    + QJSValue
     //    + QObjectList
     //    + QList<int>
 
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/signalWithJSValueInVariant.qml b/tests/auto/declarative/qdeclarativeecmascript/data/signalWithJSValueInVariant.qml
new file mode 100644 (file)
index 0000000..a6f1aa3
--- /dev/null
@@ -0,0 +1,12 @@
+import Qt.test 1.0
+
+MyQmlObject {
+    property string expression
+    property string compare
+    property bool pass: false
+    onSignalWithVariant:
+    {
+        var expected = eval(expression);
+        pass = eval(compare)(arg, expected);
+    }
+}
index 52b74af..ebe8cc5 100644 (file)
@@ -170,6 +170,7 @@ signals:
     void anotherBasicSignal();
     void thirdBasicSignal();
     void signalWithUnknownType(const MyQmlObject::MyType &arg);
+    void signalWithVariant(const QVariant &arg);
 
 public slots:
     void deleteMe() { delete this; }
index 36941af..941765d 100644 (file)
@@ -146,6 +146,10 @@ private slots:
     void numberAssignment();
     void propertySplicing();
     void signalWithUnknownTypes();
+    void signalWithJSValueInVariant_data();
+    void signalWithJSValueInVariant();
+    void signalWithJSValueInVariant_twoEngines_data();
+    void signalWithJSValueInVariant_twoEngines();
     void moduleApi_data();
     void moduleApi();
     void importScripts();
@@ -2758,6 +2762,77 @@ void tst_qdeclarativeecmascript::signalWithUnknownTypes()
     delete object;
 }
 
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant_data()
+{
+    QTest::addColumn<QString>("expression");
+    QTest::addColumn<QString>("compare");
+
+    QString compareStrict("(function(a, b) { return a === b; })");
+    QTest::newRow("true") << "true" << compareStrict;
+    QTest::newRow("undefined") << "undefined" << compareStrict;
+    QTest::newRow("null") << "null" << compareStrict;
+    QTest::newRow("123") << "123" << compareStrict;
+    QTest::newRow("'ciao'") << "'ciao'" << compareStrict;
+
+    QString comparePropertiesStrict(
+        "(function(a, b) {"
+        "  if (typeof b != 'object')"
+        "    return a === b;"
+        "  var props = Object.getOwnPropertyNames(b);"
+        "  for (var i = 0; i < props.length; ++i) {"
+        "    var p = props[i];"
+        "    return arguments.callee(a[p], b[p]);"
+        "  }"
+        "})");
+    QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })"  << comparePropertiesStrict;
+    QTest::newRow("[10,20,30]") << "[10,20,30]"  << comparePropertiesStrict;
+}
+
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant()
+{
+    QFETCH(QString, expression);
+    QFETCH(QString, compare);
+
+    QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
+    QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
+    QVERIFY(object != 0);
+
+    QJSValue value = engine.evaluate(expression);
+    QVERIFY(!engine.hasUncaughtException());
+    object->setProperty("expression", expression);
+    object->setProperty("compare", compare);
+    object->setProperty("pass", false);
+
+    emit object->signalWithVariant(QVariant::fromValue(value));
+    QVERIFY(object->property("pass").toBool());
+}
+
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines_data()
+{
+    signalWithJSValueInVariant_data();
+}
+
+void tst_qdeclarativeecmascript::signalWithJSValueInVariant_twoEngines()
+{
+    QFETCH(QString, expression);
+    QFETCH(QString, compare);
+
+    QDeclarativeComponent component(&engine, TEST_FILE("signalWithJSValueInVariant.qml"));
+    QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create()));
+    QVERIFY(object != 0);
+
+    QJSEngine engine2;
+    QJSValue value = engine2.evaluate(expression);
+    QVERIFY(!engine2.hasUncaughtException());
+    object->setProperty("expression", expression);
+    object->setProperty("compare", compare);
+    object->setProperty("pass", false);
+
+    QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine.");
+    emit object->signalWithVariant(QVariant::fromValue(value));
+    QVERIFY(!object->property("pass").toBool());
+}
+
 void tst_qdeclarativeecmascript::moduleApi_data()
 {
     QTest::addColumn<QUrl>("testfile");