diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2024-04-22 12:45:34 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-04-23 01:03:34 +0200 |
commit | 37bd19f30102d3e266386e3b81068f2e9cb20425 (patch) | |
tree | eb515f6b046ba9bf2638c5ee291a796fa1d1d300 | |
parent | 19b09affee8698f80d386e3b286753974f6bf10a (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.7 6.5 6.2
Change-Id: I012c0360ef1578c768362d5a4648252d3e6803d8
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4jscall_p.h | 28 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/typedObjectList.qml | 10 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 17 |
4 files changed, 51 insertions, 15 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index d09dcced19..6b84146866 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -2686,16 +2686,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 ef4b742590..59f594c939 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 61295ec940..2f382e8d8e 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; @@ -8642,6 +8644,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" |