Skip to content

Commit 90c55e8

Browse files
committed
QQmlListModel: Invalidate ModelObject when necessary
Both the object and the listmodel may be deleted during the life time of ModelObject. Don't crash when that happens. Also, fix QV4QPointer to actually name the type of the pointer it stores. Apparently this is the first time we add a QV4QPointer of something that's not a plain QObject. Pick-to: 6.8 6.7 6.5 6.2 5.15 Task-number: QTBUG-118024 Change-Id: I208d8749bcd67970f7bfbe569eed7a472f909508 Reviewed-by: Fabian Kosmale <[email protected]>
1 parent 84a0da1 commit 90c55e8

File tree

6 files changed

+144
-17
lines changed

6 files changed

+144
-17
lines changed

src/qml/memory/qv4heap_p.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ struct QV4QPointer {
215215

216216
private:
217217
QtSharedPointer::ExternalRefCountData *d;
218-
QObject *qObject;
218+
T *qObject;
219219
};
220220
Q_STATIC_ASSERT(std::is_trivial_v<QV4QPointer<QObject>>);
221221
#endif

src/qmlmodels/qqmllistmodel.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,9 +1665,12 @@ bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Val
16651665

16661666
ExecutionEngine *eng = that->engine();
16671667
const int elementIndex = that->d()->elementIndex();
1668-
int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng);
1669-
if (roleIndex != -1)
1670-
that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex));
1668+
if (QQmlListModel *model = that->d()->m_model) {
1669+
const int roleIndex
1670+
= model->listModel()->setExistingProperty(elementIndex, propName, value, eng);
1671+
if (roleIndex != -1)
1672+
model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex));
1673+
}
16711674

16721675
ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object());
16731676
if (mo->initialized())
@@ -1683,7 +1686,11 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
16831686
const ModelObject *that = static_cast<const ModelObject*>(m);
16841687
Scope scope(that);
16851688
ScopedString name(scope, id.asStringOrSymbol());
1686-
const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name);
1689+
QQmlListModel *model = that->d()->m_model;
1690+
if (!model)
1691+
return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1692+
1693+
const ListLayout::Role *role = model->listModel()->getExistingRole(name);
16871694
if (!role)
16881695
return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
16891696
if (hasProperty)
@@ -1696,7 +1703,7 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
16961703
}
16971704

16981705
const int elementIndex = that->d()->elementIndex();
1699-
QVariant value = that->d()->m_model->data(elementIndex, role->index);
1706+
QVariant value = model->data(elementIndex, role->index);
17001707
return that->engine()->fromVariant(value);
17011708
}
17021709

@@ -1719,16 +1726,19 @@ PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *p
17191726
const ModelObject *that = static_cast<const ModelObject *>(o);
17201727

17211728
ExecutionEngine *v4 = that->engine();
1722-
if (roleNameIndex < that->listModel()->roleCount()) {
1729+
1730+
QQmlListModel *model = that->d()->m_model;
1731+
ListModel *listModel = model ? model->listModel() : nullptr;
1732+
if (listModel && roleNameIndex < listModel->roleCount()) {
17231733
Scope scope(that->engine());
1724-
const ListLayout::Role &role = that->listModel()->getExistingRole(roleNameIndex);
1734+
const ListLayout::Role &role = listModel->getExistingRole(roleNameIndex);
17251735
++roleNameIndex;
17261736
ScopedString roleName(scope, v4->newString(role.name));
17271737
if (attrs)
17281738
*attrs = QV4::Attr_Data;
17291739
if (pd) {
17301740

1731-
QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index);
1741+
QVariant value = model->data(that->d()->elementIndex(), role.index);
17321742
if (auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
17331743
auto size = recursiveListModel->count();
17341744
auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};

src/qmlmodels/qqmllistmodel_p.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class Q_QMLMODELS_EXPORT QQmlListModel : public QAbstractListModel
7979
bool dynamicRoles() const { return m_dynamicRoles; }
8080
void setDynamicRoles(bool enableDynamicRoles);
8181

82+
ListModel *listModel() const { return m_listModel; }
83+
8284
Q_SIGNALS:
8385
void countChanged();
8486

src/qmlmodels/qqmllistmodel_p_p.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,23 @@ struct ModelObject : public QObjectWrapper {
125125
{
126126
QObjectWrapper::init(object);
127127
m_model = model;
128-
QObjectPrivate *op = QObjectPrivate::get(object);
129-
m_nodeModelMetaObject = static_cast<ModelNodeMetaObject *>(op->metaObject);
130128
}
131-
void destroy() { QObjectWrapper::destroy(); }
132-
int elementIndex() const { return m_nodeModelMetaObject->m_elementIndex; }
133-
QQmlListModel *m_model;
134-
ModelNodeMetaObject *m_nodeModelMetaObject;
129+
130+
void destroy()
131+
{
132+
m_model.destroy();
133+
QObjectWrapper::destroy();
134+
}
135+
136+
int elementIndex() const {
137+
if (const QObject *o = object()) {
138+
const QObjectPrivate *op = QObjectPrivate::get(o);
139+
return static_cast<ModelNodeMetaObject *>(op->metaObject)->m_elementIndex;
140+
}
141+
return -1;
142+
}
143+
144+
QV4QPointer<QQmlListModel> m_model;
135145
};
136146

137147
}
@@ -141,8 +151,6 @@ struct ModelObject : public QObjectWrapper
141151
V4_OBJECT2(ModelObject, QObjectWrapper)
142152
V4_NEEDS_DESTROY
143153

144-
ListModel *listModel() const { return d()->m_model->m_listModel; }
145-
146154
protected:
147155
static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver);
148156
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import QtQml
2+
3+
QtObject {
4+
function swapCorpses() {
5+
const lhsData = getModelData(lhsButtonListModel);
6+
const rhsData = getModelData(rhsButtonListModel);
7+
8+
lhsButtonListModel.clear();
9+
rhsButtonListModel.clear();
10+
11+
addToModel(lhsButtonListModel, rhsData);
12+
addToModel(rhsButtonListModel, lhsData);
13+
}
14+
15+
property ListModel l1: ListModel {
16+
id: lhsButtonListModel
17+
}
18+
19+
property ListModel l2: ListModel {
20+
id: rhsButtonListModel
21+
}
22+
23+
Component.onCompleted: {
24+
lhsButtonListModel.append({ "ident": 1, "buttonText": "B 1"});
25+
lhsButtonListModel.append({ "ident": 2, "buttonText": "B 2"});
26+
lhsButtonListModel.append({ "ident": 3, "buttonText": "B 3"});
27+
28+
rhsButtonListModel.append({ "ident": 4, "buttonText": "B 4"});
29+
rhsButtonListModel.append({ "ident": 5, "buttonText": "B 5"});
30+
rhsButtonListModel.append({ "ident": 6, "buttonText": "B 6"});
31+
}
32+
33+
function getModelData(model) {
34+
var dataList = []
35+
for (var i = 0; i < model.count; ++i)
36+
dataList.push(model.get(i));
37+
38+
return dataList;
39+
}
40+
41+
function addToModel(model, buttonData) {
42+
for (var i = 0; i < buttonData.length; ++i)
43+
model.append(buttonData[i]);
44+
}
45+
}

tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ private slots:
122122
void enumsInListElement();
123123
void protectQObjectFromGC();
124124
void nestedLists();
125+
void deadModelData();
125126
};
126127

127128
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -2056,6 +2057,67 @@ void tst_qqmllistmodel::nestedLists()
20562057
verifyLists(list, topLevel);
20572058
}
20582059

2060+
void tst_qqmllistmodel::deadModelData()
2061+
{
2062+
QQmlEngine engine;
2063+
QQmlComponent component(&engine, testFileUrl("deadModelData.qml"));
2064+
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
2065+
QScopedPointer<QObject> o(component.create());
2066+
QVERIFY(!o.isNull());
2067+
2068+
QQmlListModel *l1 = o->property("l1").value<QQmlListModel *>();
2069+
QVERIFY(l1);
2070+
QQmlListModel *l2 = o->property("l2").value<QQmlListModel *>();
2071+
QVERIFY(l2);
2072+
2073+
QCOMPARE(l1->count(), 3);
2074+
QCOMPARE(l2->count(), 3);
2075+
2076+
for (int i = 0; i < 3; ++i) {
2077+
QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
2078+
QVERIFY(i1);
2079+
QCOMPARE(i1->property("ident").value<double>(), i + 1);
2080+
QCOMPARE(i1->property("buttonText").value<QString>(),
2081+
QLatin1String("B %1").arg(QLatin1Char('0' + i + 1)));
2082+
2083+
QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
2084+
QVERIFY(i2);
2085+
QCOMPARE(i2->property("ident").value<double>(), i + 4);
2086+
QCOMPARE(i2->property("buttonText").value<QString>(),
2087+
QLatin1String("B %1").arg(QLatin1Char('0' + i + 4)));
2088+
}
2089+
2090+
for (int i = 0; i < 6; ++i) {
2091+
QTest::ignoreMessage(
2092+
QtWarningMsg,
2093+
QRegularExpression(".*: ident is undefined. Adding an object with a undefined "
2094+
"member does not create a role for it."));
2095+
QTest::ignoreMessage(
2096+
QtWarningMsg,
2097+
QRegularExpression(".*: buttonText is undefined. Adding an object with a undefined "
2098+
"member does not create a role for it."));
2099+
}
2100+
2101+
QMetaObject::invokeMethod(o.data(), "swapCorpses");
2102+
2103+
// We get default-created values for all the roles now.
2104+
2105+
QCOMPARE(l1->count(), 3);
2106+
QCOMPARE(l2->count(), 3);
2107+
2108+
for (int i = 0; i < 3; ++i) {
2109+
QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
2110+
QVERIFY(i1);
2111+
QCOMPARE(i1->property("ident").value<double>(), double());
2112+
QCOMPARE(i1->property("buttonText").value<QString>(), QString());
2113+
2114+
QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
2115+
QVERIFY(i2);
2116+
QCOMPARE(i2->property("ident").value<double>(), double());
2117+
QCOMPARE(i2->property("buttonText").value<QString>(), QString());
2118+
}
2119+
}
2120+
20592121
QTEST_MAIN(tst_qqmllistmodel)
20602122

20612123
#include "tst_qqmllistmodel.moc"

0 commit comments

Comments
 (0)