From 2547e8be4d507361527d422184d3cae205aa76ff Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 15 Aug 2025 10:01:59 +0200 Subject: QtQml: Store detached VariantAssociation objects on the JS heap While the VariantAssociation is detached it is subject to the GC or unrelated C++ code deleting objects from its internals. Since it's then not the owning object's responsibility to track this anymore, we need to track it ourselves. The way to do it is to use the existing V4 objects. Pick-to: 6.10 6.9 Task-number: QTBUG-139025 Change-Id: Ic1d5aa85171b5d91c2b9d546963268b6f09c2802 Reviewed-by: Sami Shalayel --- tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp') diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index a3082d0d75..df26b97488 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -97,6 +98,7 @@ private slots: void deduplicateConversionOrigins(); void destroyAndToString(); void detachOnAssignment(); + void detachedReferences(); void dialogButtonBox(); void enumConversion(); void enumFromBadSingleton(); @@ -1739,6 +1741,59 @@ void tst_QmlCppCodegen::detachOnAssignment() QCOMPARE(p->things()[0], QStringLiteral("c")); } +void tst_QmlCppCodegen::detachedReferences() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/detachedreferences.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer o(c.create()); + QVERIFY(o); + + DetachedReferences *d = qobject_cast(o.data()); + QVERIFY(d); + + const QVariantHash hash = d->getHash(); + QObject *collectable1 = hash["collectable"_L1].value(); + QVERIFY(collectable1); + QSignalSpy spy1(collectable1, &QObject::destroyed); + + const QVariantMap map = d->getMap(); + QObject *collectable2 = map["collectable"_L1].value(); + QVERIFY(collectable2); + QSignalSpy spy2(collectable2, &QObject::destroyed); + + // The detached containers retain their types. + QCOMPARE(d->property("markedMap").metaType(), QMetaType::fromType()); + QCOMPARE(d->property("markedHash").metaType(), QMetaType::fromType()); + + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QCOMPARE(spy1.count(), 0); + QCOMPARE(spy2.count(), 0); + + // Resetting the hash alone does not cause collectible1 to be collected + // because it's also in the map (recursively). + d->setProperty("markedHash", QVariant()); + + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QCOMPARE(spy1.count(), 0); + QCOMPARE(spy2.count(), 0); + + d->setProperty("markedMap", QVariant()); + + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QCOMPARE(spy1.count(), 1); + QCOMPARE(spy2.count(), 1); +} + void tst_QmlCppCodegen::dialogButtonBox() { QQmlEngine engine; -- cgit v1.2.3