diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2024-05-23 13:51:15 +0200 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-05-30 09:55:41 +0200 |
| commit | f5f99985b8cb1d04a3d230a21ccf603b220dcdfd (patch) | |
| tree | 61c0ff4184535ea86b67c224cc420682067af2c6 | |
| parent | 5911f307e1b0b5c6e263e74fd5ec9c73c8c865ec (diff) | |
QtQml: Document and uphold precondition of metaTypeFromJS()
The value needs to be a default-constructed instance. Otherwise a number
of branches in this method produce unwanted effects, such as appending
to an already existing array rather than creating a new one.
Amends commit 1b89c1edcae68351632c2755e5408410c2ff98e3.
Pick-to: 6.5
Fixes: QTBUG-125429
Change-Id: If175a02b3a794573abc03df206fbddd41f2855b4
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit c16a3e5adb59f6da5e39e51ca15a4d5324d68d1c)
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
| -rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 16 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/data/nestedVectors.qml | 27 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/testtypes.cpp | 1 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/testtypes.h | 32 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 22 |
5 files changed, 92 insertions, 6 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 3a45823734..dd70e84dcc 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -2440,18 +2440,22 @@ bool convertToIterable(QMetaType metaType, void *data, Source *sequence) return false; const QMetaType elementMetaType = iterable.valueMetaType(); - QVariant element(elementMetaType); for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) { - if (!ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data())) - element = QVariant(elementMetaType); + QVariant element(elementMetaType); + ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()); iterable.addValue(element, QSequentialIterable::AtEnd); } return true; } -// Converts a JS value to a meta-type. -// data must point to a place that can store a value of the given type. -// Returns true if conversion succeeded, false otherwise. +/*! + * \internal + * + * Converts a JS value to a meta-type. + * \a data must point to a default-constructed instance of \a metaType. + * Returns \c true if conversion succeeded, \c false otherwise. In the latter case, + * \a data is not modified. + */ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, void *data) { // check if it's one of the types we know diff --git a/tests/auto/qml/qqmllanguage/data/nestedVectors.qml b/tests/auto/qml/qqmllanguage/data/nestedVectors.qml new file mode 100644 index 0000000000..0bcea52133 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nestedVectors.qml @@ -0,0 +1,27 @@ +import Test +import QtQml + +NestedVectors { + id: self + + property var list1 + + Component.onCompleted: { + list1 = self.getList() + + let list2 = [] + let data1 = [] + data1.push(2) + data1.push(3) + data1.push(4) + + let data2 = [] + data2.push(5) + data2.push(6) + + list2.push(data1) + list2.push(data2) + + self.setList(list2) + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index ffff0a6979..2fce6567be 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -174,6 +174,7 @@ void registerTypes() qmlRegisterTypesAndRevisions<NonSingleton>("EnumScopeTest", 1); qmlRegisterTypesAndRevisions<EnumProviderSingletonQml>("EnumScopeTest", 1); + qmlRegisterTypesAndRevisions<NestedVectors>("Test", 1); } QVariant myCustomVariantTypeConverter(const QString &data) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index bcf02c1cf9..6d5c9b5b38 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -2942,4 +2942,36 @@ public: } }; +class NestedVectors : public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + NestedVectors(QObject *parent = nullptr) : QObject(parent) + { + std::vector<int> data; + data.push_back(1); + data.push_back(2); + data.push_back(3); + m_list.push_back(data); + data.clear(); + data.push_back(4); + data.push_back(5); + m_list.push_back(data); + } + + Q_INVOKABLE std::vector<std::vector<int>> getList() + { + return m_list; + } + + Q_INVOKABLE void setList(std::vector<std::vector<int>> list) + { + m_list = list; + } + +private: + std::vector<std::vector<int>> m_list; +}; + #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 7231b977ce..1e4de3449c 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -454,6 +454,8 @@ private slots: void typedObjectList(); + void nestedVectors(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -8655,6 +8657,26 @@ void tst_qqmllanguage::typedObjectList() QVERIFY(list.at(&list, 0) != nullptr); } +void tst_qqmllanguage::nestedVectors() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("nestedVectors.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + NestedVectors *n = qobject_cast<NestedVectors *>(o.data()); + QVERIFY(n); + + const std::vector<std::vector<int>> expected1 { { 1, 2, 3 }, { 4, 5 } }; + const QVariant list1 = n->property("list1"); + QCOMPARE(list1.metaType(), QMetaType::fromType<std::vector<std::vector<int>>>()); + QCOMPARE(list1.value<std::vector<std::vector<int>>>(), expected1); + + const std::vector<std::vector<int>> expected2 { { 2, 3, 4 }, { 5, 6 } }; + QCOMPARE(n->getList(), expected2); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" |
