aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2024-04-22 12:45:34 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2024-04-23 01:11:44 +0000
commita61222c44714756c058b5407613ac3b95e30e4e2 (patch)
treeef3fed8f05809866ee1c2d0e775985c96aa995a8
parenta7debb56f25558cb6a9afdf8d799555aa881d3eb (diff)
QtQml: Fix some type conversion edge cases
If the type conversion code fails to convert an argument, we still need to make sure the argument has a definite value. Otherwise we may trigger undefined behavior somewhere down the line. Furthermore, we want to look at the precise type when converting list properties. Otherwise we get a list property without any methods back when converting. Pick-to: 6.5 6.2 Change-Id: I012c0360ef1578c768362d5a4648252d3e6803d8 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 37bd19f30102d3e266386e3b81068f2e9cb20425) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/qml/jsruntime/qv4engine.cpp11
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h28
-rw-r--r--tests/auto/qml/qqmllanguage/data/typedObjectList.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp17
4 files changed, 51 insertions, 15 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 506b920142..3a45823734 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -2649,16 +2649,15 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
return true;
}
- if (metaType == QMetaType::fromType<QQmlListReference>()) {
- if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
+ if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
+ if (metaType == QMetaType::fromType<QQmlListReference>()) {
*reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference();
return true;
}
- }
- if (metaType == QMetaType::fromType<QQmlListProperty<QObject>>()) {
- if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
- *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapper->d()->property();
+ const auto wrapperPrivate = wrapper->d();
+ if (wrapperPrivate->propertyType() == metaType) {
+ *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapperPrivate->property();
return true;
}
}
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index a26eb36b1a..6ea1dbedcb 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -125,10 +125,18 @@ ReturnedValue convertAndCall(
types[i + 1] = argumentType;
if (const qsizetype argumentSize = argumentType.sizeOf()) {
Q_ALLOCA_VAR(void, argument, argumentSize);
- if (argumentType.flags() & QMetaType::NeedsConstruction)
+ if (argumentType.flags() & QMetaType::NeedsConstruction) {
argumentType.construct(argument);
- if (i < argc)
- ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument);
+ if (i < argc)
+ ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument);
+ } else if (i >= argc
+ || !ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument)) {
+ // If we can't convert the argument, we need to default-construct it even if it
+ // doesn't formally need construction.
+ // E.g. an int doesn't need construction, but we still want it to be 0.
+ argumentType.construct(argument);
+ }
+
values[i + 1] = argument;
} else {
values[i + 1] = nullptr;
@@ -201,13 +209,15 @@ bool convertAndCall(ExecutionEngine *engine, QObject *thisObject,
// Clear the return value
resultType.destruct(result);
resultType.construct(result);
- } else {
+ } else if (resultType == QMetaType::fromType<QVariant>()) {
// When the return type is QVariant, JS objects are to be returned as
// QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately.
- if (resultType == QMetaType::fromType<QVariant>())
- *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(jsResult, QMetaType {});
- else
- ExecutionEngine::metaTypeFromJS(jsResult, resultType, result);
+ *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(jsResult, QMetaType {});
+ } else if (!ExecutionEngine::metaTypeFromJS(jsResult, resultType, result)) {
+ // If we cannot convert, also clear the return value.
+ // The caller may have given us an uninitialized QObject*, expecting it to be overwritten.
+ resultType.destruct(result);
+ resultType.construct(result);
}
return !jsResult->isUndefined();
}
@@ -276,7 +286,7 @@ inline ReturnedValue coerceListType(
}
if (listValueType.flags() & QMetaType::PointerToQObject) {
- QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, listValueType));
+ QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, type));
QQmlListProperty<QObject> *listProperty = newList->d()->property();
const qsizetype length = array->getLength();
diff --git a/tests/auto/qml/qqmllanguage/data/typedObjectList.qml b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
new file mode 100644
index 0000000000..7e6f6e8dd9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
@@ -0,0 +1,10 @@
+import QtQml
+
+QtObject {
+ property var b;
+ property Component c: QtObject {}
+
+ function returnList(a: Component) : list<Component> { return [a] }
+
+ Component.onCompleted: b = { b: returnList(c) }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 53d3dce03c..7231b977ce 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -452,6 +452,8 @@ private slots:
void overrideDefaultProperty();
void enumScopes();
+ void typedObjectList();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -8638,6 +8640,21 @@ void tst_qqmllanguage::enumScopes()
QCOMPARE(o->property("singletonUnscopedValue").toInt(), int(EnumProviderSingleton::Expected::Value));
}
+void tst_qqmllanguage::typedObjectList()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("typedObjectList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QJSValue b = o->property("b").value<QJSValue>();
+ auto list = qjsvalue_cast<QQmlListProperty<QQmlComponent>>(b.property(QStringLiteral("b")));
+
+ QCOMPARE(list.count(&list), 1);
+ QVERIFY(list.at(&list, 0) != nullptr);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"