aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2020-01-02 14:42:12 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2020-01-31 14:48:28 +0100
commitecdb4ed275a0869dc668d73d774735575d43a0a3 (patch)
tree40c4b67bc8446898e80c0d085c09252641994092
parent41893b00fac8e1b18589694a35c20cf5251282fe (diff)
Enable conversion from QJSValues containing arrays to container types
We started to convert containers to QJSValues, so that we could use them as JavaScript arrays. Unfortunately, this would then lead to a type missmatch when those same values where to be stored in a property of the container type. This commit fixes this by converting them back to the original type. Fixes: QTBUG-80916 Change-Id: I30a3b03e17c34b171d4a6881dfd7801c13e94d80 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/jsruntime/qv4engine.cpp25
-rw-r--r--tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp9
-rw-r--r--tests/auto/qml/qqmllanguage/data/arrayToContainer.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp27
5 files changed, 63 insertions, 7 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index c81661033a..81f55673cd 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1508,12 +1508,35 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
}
+ QVariant retn;
#if QT_CONFIG(qml_sequence_object)
bool succeeded = false;
- QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded);
+ retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded);
if (succeeded)
return retn;
#endif
+ retn = QVariant(typeHint, QMetaType::create(typeHint));
+ auto retnAsIterable = retn.value<QtMetaTypePrivate::QSequentialIterableImpl>();
+ if (retnAsIterable._iteratorCapabilities & QtMetaTypePrivate::ContainerIsAppendable) {
+ auto const length = a->getLength();
+ QV4::ScopedValue arrayValue(scope);
+ for (qint64 i = 0; i < length; ++i) {
+ arrayValue = a->get(i);
+ QVariant asVariant = toVariant(e, arrayValue, retnAsIterable._metaType_id, false, visitedObjects);
+ auto originalType = asVariant.userType();
+ bool couldConvert = asVariant.convert(retnAsIterable._metaType_id);
+ if (!couldConvert) {
+ qWarning() << QLatin1String("Could not convert array value at position %1 from %2 to %3")
+ .arg(QString::number(i),
+ QMetaType::typeName(originalType),
+ QMetaType::typeName(retnAsIterable._metaType_id));
+ // create default constructed value
+ asVariant = QVariant(retnAsIterable._metaType_id, nullptr);
+ }
+ retnAsIterable.append(asVariant.constData());
+ }
+ return retn;
+ }
}
if (value.isUndefined())
diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml
index 75beafd1ee..c2c8c1b52b 100644
--- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml
+++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml
@@ -12,7 +12,7 @@ Item {
function performTest() {
// we have NOT registered QList<QPoint> as a type
- var pointList = [ Qt.point(7,7), Qt.point(8,8), Qt.point(9,9) ];
+ var pointList = [ Qt.point(7,7), "hello world", Qt.point(8,8), Qt.point(9,9) ];
msco.pointListProperty = pointList; // error.
}
}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 370ef5f065..26232ac1a3 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -5792,13 +5792,12 @@ void tst_qqmlecmascript::sequenceConversionWrite()
MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco");
QVERIFY(seq != nullptr);
- // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work.
- QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QJSValue to QList<QPoint>");
- QTest::ignoreMessage(QtWarningMsg, warningOne.toLatin1().constData());
-
+ // Behavior change in 5.14: due to added auto-magical conversions, it is possible to assign to
+ // QList<QPoint>, even though it is not a registered sequence type
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression("Could not convert array value at position 1 from QString to QPoint"));
QMetaObject::invokeMethod(object, "performTest");
- QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed
+ QList<QPoint> pointList; pointList << QPoint(7, 7) << QPoint(0,0) << QPoint(8, 8) << QPoint(9, 9); // original values, shouldn't have changed
QCOMPARE(seq->pointListProperty(), pointList);
delete object;
diff --git a/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml b/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml
new file mode 100644
index 0000000000..ee400eb41f
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/arrayToContainer.qml
@@ -0,0 +1,7 @@
+import QtQml 2.14
+import qt.test 1.0
+
+TestItem {
+ property var vector
+ positions: vector
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index fae74f1f25..4a8ce77f92 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -303,6 +303,8 @@ private slots:
void typeWrapperToVariant();
+ void arrayToContainer();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -5238,6 +5240,31 @@ void tst_qqmllanguage::typeWrapperToVariant()
QVERIFY(target);
}
+class TestItem : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY( QVector<QPointF> positions MEMBER m_points )
+
+public:
+ TestItem() = default;
+ QVector< QPointF > m_points;
+};
+
+
+Q_DECLARE_METATYPE(QVector<QPointF>);
+void tst_qqmllanguage::arrayToContainer()
+{
+ QQmlEngine engine;
+ qmlRegisterType<TestItem>("qt.test", 1, 0, "TestItem");
+ QVector<QPointF> points { QPointF (2.0, 3.0) };
+ engine.rootContext()->setContextProperty("test", QVariant::fromValue(points));
+ QQmlComponent component(&engine, testFileUrl("arrayToContainer.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<TestItem> root(qobject_cast<TestItem *>(component.createWithInitialProperties( {{"vector", QVariant::fromValue(points)}} )));
+ QVERIFY(root);
+ QCOMPARE(root->m_points.at(0), QPointF (2.0, 3.0) );
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"