Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Unwrap QJSValue from QVariant in QV8Engine::fromVariant
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>
  • Loading branch information
nierob authored and Qt by Nokia committed Sep 19, 2011
1 parent c970d47 commit 8de9a07
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/declarative/qml/v8/qv8engine.cpp
Expand Up @@ -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*>?
Expand All @@ -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>

Expand Down
@@ -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);
}
}
1 change: 1 addition & 0 deletions tests/auto/declarative/qdeclarativeecmascript/testtypes.h
Expand Up @@ -170,6 +170,7 @@ class MyQmlObject : public QObject
void anotherBasicSignal();
void thirdBasicSignal();
void signalWithUnknownType(const MyQmlObject::MyType &arg);
void signalWithVariant(const QVariant &arg);

public slots:
void deleteMe() { delete this; }
Expand Down
Expand Up @@ -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();
Expand Down Expand Up @@ -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");
Expand Down

0 comments on commit 8de9a07

Please sign in to comment.