diff options
-rw-r--r-- | src/qmlmodels/qqmladaptormodel.cpp | 18 | ||||
-rw-r--r-- | src/qmlmodels/qqmladaptormodel_p.h | 6 | ||||
-rw-r--r-- | src/qmlmodels/qqmldelegatemodel.cpp | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml | 32 | ||||
-rw-r--r-- | tests/auto/quick/qquicklistview/tst_qquicklistview.cpp | 24 |
5 files changed, 74 insertions, 8 deletions
diff --git a/src/qmlmodels/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp index f4dca6e518..b82a589788 100644 --- a/src/qmlmodels/qqmladaptormodel.cpp +++ b/src/qmlmodels/qqmladaptormodel.cpp @@ -962,30 +962,36 @@ QQmlAdaptorModel::~QQmlAdaptorModel() accessors->cleanup(*this); } -void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine) +void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *, QQmlEngine *engine) { accessors->cleanup(*this); list.setList(variant, engine); + modelStrongReference.clear(); if (QObject *object = qvariant_cast<QObject *>(list.list())) { - setObject(object, parent); + if (QQmlData *ddata = QQmlData::get(object)) + modelStrongReference = ddata->jsWrapper; + setObject(object); if (qobject_cast<QAbstractItemModel *>(object)) accessors = new VDMAbstractItemModelDataType(this); else accessors = new VDMObjectDelegateDataType; } else if (list.type() == QQmlListAccessor::ListProperty) { - setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent); + auto object = static_cast<const QQmlListReference *>(variant.constData())->object(); + if (QQmlData *ddata = QQmlData::get(object)) + modelStrongReference = ddata->jsWrapper; + setObject(object); accessors = new VDMObjectDelegateDataType; } else if (list.type() == QQmlListAccessor::ObjectList) { - setObject(nullptr, parent); + setObject(nullptr); accessors = new VDMObjectDelegateDataType; } else if (list.type() != QQmlListAccessor::Invalid && list.type() != QQmlListAccessor::Instance) { // Null QObject - setObject(nullptr, parent); + setObject(nullptr); accessors = new VDMListDelegateDataType; } else { - setObject(nullptr, parent); + setObject(nullptr); accessors = &qt_vdm_null_accessors; } } diff --git a/src/qmlmodels/qqmladaptormodel_p.h b/src/qmlmodels/qqmladaptormodel_p.h index 537ec65eed..4f01a37f0a 100644 --- a/src/qmlmodels/qqmladaptormodel_p.h +++ b/src/qmlmodels/qqmladaptormodel_p.h @@ -70,7 +70,7 @@ class QQmlDelegateModel; class QQmlDelegateModelItem; class QQmlDelegateModelItemMetaType; -class Q_QMLMODELS_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject> +class Q_QMLMODELS_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlGuard<QObject> { public: class Accessors @@ -114,6 +114,10 @@ public: const Accessors *accessors; QPersistentModelIndex rootIndex; QQmlListAccessor list; + // we need to ensure that a JS created model does not get gced, but cannot + // arbitrarily set the parent (using QQmlStrongJSQObjectReference) of QObject based models, + // as that causes issues with singletons + QV4::PersistentValue modelStrongReference; int modelItemRevision = 0; diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 751d9e301e..8e9ca0ce55 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -258,7 +258,7 @@ QQmlDelegateModel::~QQmlDelegateModel() { Q_D(QQmlDelegateModel); d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setObject(nullptr, this); + d->m_adaptorModel.setObject(nullptr); for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) { if (cacheItem->object) { diff --git a/tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml b/tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml new file mode 100644 index 0000000000..f230786723 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/singletonModelLifetime.qml @@ -0,0 +1,32 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 +import test 1.0 + +Window { + id: root + visible: true + width: 800 + height: 680 + property bool alive: false + + Component { + id: view + ListView { + model: SingletonModel + } + } + function compare(a,b) { + root.alive = (a === b) + } + + function test_singletonModelCrash() { + SingletonModel.objectName = "model" + var o = view.createObject(root) + o.destroy() + Qt.callLater(function() { + compare(SingletonModel.objectName, "model") + }) + } + + Component.onCompleted: root.test_singletonModelCrash() +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index d3deb513d0..7b17c014d5 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -33,6 +33,8 @@ #include <QtGui/QStyleHints> #include <QtQuick/qquickview.h> #include <QtQuickTest/QtQuickTest> +#include <QStringListModel> +#include <QQmlApplicationEngine> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlexpression.h> @@ -302,6 +304,9 @@ private slots: void dragDelegateWithMouseArea(); void dragDelegateWithMouseArea_data(); + + void singletonModelLifetime(); + private: template <class T> void items(const QUrl &source); template <class T> void changed(const QUrl &source); @@ -10200,6 +10205,25 @@ void tst_QQuickListView::dragDelegateWithMouseArea_data() } } +class SingletonModel : public QStringListModel +{ + Q_OBJECT +public: + SingletonModel(QObject* parent = nullptr) : QStringListModel(parent) { } +}; + +void tst_QQuickListView::singletonModelLifetime() +{ + // this does not really test any functionality of listview, but we do not have a good way + // to unit test QQmlAdaptorModel in isolation. + qmlRegisterSingletonType<SingletonModel>("test", 1, 0, "SingletonModel", + [](QQmlEngine* , QJSEngine*) -> QObject* { return new SingletonModel; }); + + QQmlApplicationEngine engine(testFile("singletonModelLifetime.qml")); + // needs event loop iteration for callLater to execute + QTRY_VERIFY(engine.rootObjects().first()->property("alive").toBool()); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" |