Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Change repeater item to handle model being deleted.
The repeater item previously stored a raw QObject pointer in
a variant. When this pointer was a dynamic list model element
that was deleted, the variant would continue to hold a stale
pointer. Change repeater to use a guard object to hold the model
when it is a QObject. Continue to use a variant to hold models
that are not based on QObject to maintain same semantics.

Change-Id: Ie100947132923803263c725e86efa68206382f12
Reviewed-by: Martin Jones <martin.jones@nokia.com>
  • Loading branch information
Glenn Watson authored and Qt by Nokia committed Mar 13, 2012
1 parent fc5ddb1 commit be8675a
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 1 deletion.
10 changes: 9 additions & 1 deletion src/quick/items/qquickrepeater.cpp
Expand Up @@ -51,7 +51,7 @@
QT_BEGIN_NAMESPACE

QQuickRepeaterPrivate::QQuickRepeaterPrivate()
: model(0), ownModel(false), inRequest(false), itemCount(0), createFrom(-1)
: model(0), ownModel(false), inRequest(false), dataSourceIsObject(false), itemCount(0), createFrom(-1)
{
}

Expand Down Expand Up @@ -175,6 +175,12 @@ QQuickRepeater::~QQuickRepeater()
QVariant QQuickRepeater::model() const
{
Q_D(const QQuickRepeater);

if (d->dataSourceIsObject) {
QObject *o = d->dataSourceAsObject;
return QVariant::fromValue(o);
}

return d->dataSource;
}

Expand All @@ -194,6 +200,8 @@ void QQuickRepeater::setModel(const QVariant &model)
}
d->dataSource = model;
QObject *object = qvariant_cast<QObject*>(model);
d->dataSourceAsObject = object;
d->dataSourceIsObject = object != 0;
QQuickVisualModel *vim = 0;
if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
if (d->ownModel) {
Expand Down
2 changes: 2 additions & 0 deletions src/quick/items/qquickrepeater_p_p.h
Expand Up @@ -75,8 +75,10 @@ class QQuickRepeaterPrivate : public QQuickItemPrivate

QQuickVisualModel *model;
QVariant dataSource;
QQmlGuard<QObject> dataSourceAsObject;
bool ownModel : 1;
bool inRequest : 1;
bool dataSourceIsObject : 1;
int itemCount;
int createFrom;

Expand Down
20 changes: 20 additions & 0 deletions tests/auto/quick/qquickrepeater/data/dynamicmodelcrash.qml
@@ -0,0 +1,20 @@
import QtQuick 2.0

Item {
ListModel {
id: lm;
}

Component.onCompleted: {
lm.append({ subModel: [ {d:0} ] });
rep.model = lm.get(0).subModel;
rep.model;
lm.remove(0);
rep.model;
}

Repeater {
objectName: "rep"
id: rep
}
}
15 changes: 15 additions & 0 deletions tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp
Expand Up @@ -77,6 +77,7 @@ private slots:
void properties();
void asynchronous();
void initParent();
void dynamicModelCrash();
};

class TestObject : public QObject
Expand Down Expand Up @@ -639,6 +640,20 @@ void tst_QQuickRepeater::initParent()
QCOMPARE(qvariant_cast<QQuickItem*>(rootObject->property("parentItem")), rootObject);
}

void tst_QQuickRepeater::dynamicModelCrash()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("dynamicmodelcrash.qml"));

// Don't crash
QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
QVERIFY(rootObject);

QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "rep");
QVERIFY(repeater);
QVERIFY(qvariant_cast<QObject *>(repeater->model()) == 0);
}

QTEST_MAIN(tst_QQuickRepeater)

#include "tst_qquickrepeater.moc"

0 comments on commit be8675a

Please sign in to comment.