From adaedcb9ead1c536ff3d8fba0fb9f4fc262d4d45 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 5 Dec 2013 16:21:03 +0100 Subject: Fix crash in QQuickLoader when source component is garbage collected It may happen that the QQuickLoader is the last entity left in the system holding a reference to the QQmlComponent *sourceComponent. We have to let the garbage collector know about that by keeping a persistent value for it. Task-number: QTBUG-35334 Change-Id: I715864440378fd9dd4f2d5ef8ff2f171c81ed7ef Reviewed-by: Lars Knoll --- src/quick/items/qquickloader.cpp | 5 +++++ src/quick/items/qquickloader_p_p.h | 1 + .../qquickloader/data/SimpleTestComponent.qml | 2 ++ .../data/sourceComponentGarbageCollection.qml | 8 ++++++++ tests/auto/quick/qquickloader/tst_qquickloader.cpp | 22 ++++++++++++++++++++++ 5 files changed, 38 insertions(+) create mode 100644 tests/auto/quick/qquickloader/data/SimpleTestComponent.qml create mode 100644 tests/auto/quick/qquickloader/data/sourceComponentGarbageCollection.qml diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 7d04be2393..b83c21428c 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -106,6 +106,7 @@ void QQuickLoaderPrivate::clear() component->deleteLater(); component = 0; } + componentStrongReference.clear(); source = QUrl(); if (item) { @@ -472,6 +473,10 @@ void QQuickLoader::setSourceComponent(QQmlComponent *comp) d->clear(); d->component = comp; + if (comp) { + if (QQmlData *ddata = QQmlData::get(comp)) + d->componentStrongReference = ddata->jsWrapper.value(); + } d->loadingFromSource = false; if (d->active) diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index 9c94b4ce38..32c271222d 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -106,6 +106,7 @@ public: QQuickItem *item; QObject *object; QQmlComponent *component; + QV4::PersistentValue componentStrongReference; // To ensure GC doesn't delete components created by Qt.createComponent QQmlContext *itemContext; QQuickLoaderIncubator *incubator; QV4::PersistentValue initialPropertyValues; diff --git a/tests/auto/quick/qquickloader/data/SimpleTestComponent.qml b/tests/auto/quick/qquickloader/data/SimpleTestComponent.qml new file mode 100644 index 0000000000..0e69012662 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/SimpleTestComponent.qml @@ -0,0 +1,2 @@ +import QtQml 2.0 +QtObject {} diff --git a/tests/auto/quick/qquickloader/data/sourceComponentGarbageCollection.qml b/tests/auto/quick/qquickloader/data/sourceComponentGarbageCollection.qml new file mode 100644 index 0000000000..ab86883af5 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/sourceComponentGarbageCollection.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Loader { + active: false + function setSourceComponent() { + sourceComponent = Qt.createComponent("SimpleTestComponent.qml"); + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index 50ded4d95a..1e23d689ff 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -128,6 +128,8 @@ private slots: void sizeBound(); void QTBUG_30183(); + void sourceComponentGarbageCollection(); + private: QQmlEngine engine; }; @@ -1144,6 +1146,26 @@ void tst_QQuickLoader::QTBUG_30183() delete loader; } +void tst_QQuickLoader::sourceComponentGarbageCollection() +{ + QQmlComponent component(&engine, testFileUrl("sourceComponentGarbageCollection.qml")); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + QMetaObject::invokeMethod(obj.data(), "setSourceComponent"); + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + + QSignalSpy spy(obj.data(), SIGNAL(loaded())); + + obj->setProperty("active", true); + + if (spy.isEmpty()) + QVERIFY(spy.wait()); + + QCOMPARE(spy.count(), 1); +} + QTEST_MAIN(tst_QQuickLoader) #include "tst_qquickloader.moc" -- cgit v1.2.3