From 03df41fbcdb6e1ae3d0792d5b7806e5335b58794 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 10 Jan 2020 15:22:20 +0100 Subject: QML list property: Avoid crash if contained object is deleted Task-number: QTBUG-81123 Change-Id: I3dd1a42e444f817722368cd268c2f987a99fbf1c Reviewed-by: Ulf Hermann (cherry picked from commit e5570eecd3a4fc61020d28699169707a2c1f5dc9) --- src/qml/qml/qqmlvmemetaobject.cpp | 19 ++++++------ src/qml/qml/qqmlvmemetaobject_p.h | 2 +- .../qqmllanguage/data/listContainingDeleted.qml | 36 ++++++++++++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 20 ++++++++++++ 4 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/listContainingDeleted.qml diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 15fb181516..1de8b895e2 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -62,26 +62,27 @@ QT_BEGIN_NAMESPACE static void list_append(QQmlListProperty *prop, QObject *o) { - QList *list = static_cast *>(prop->data); + auto *list = static_cast> *>(prop->data); list->append(o); static_cast(prop->dummy1)->activate(prop->object, reinterpret_cast(prop->dummy2), nullptr); } static int list_count(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); + + auto *list = static_cast> *>(prop->data); return list->count(); } static QObject *list_at(QQmlListProperty *prop, int index) { - QList *list = static_cast *>(prop->data); + auto *list = static_cast> *>(prop->data); return list->at(index); } static void list_clear(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); + auto *list = static_cast> *>(prop->data); list->clear(); static_cast(prop->dummy1)->activate(prop->object, reinterpret_cast(prop->dummy2), nullptr); } @@ -548,7 +549,7 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) const return wrapper->object(); } -QList *QQmlVMEMetaObject::readPropertyAsList(int id) const +QVector> *QQmlVMEMetaObject::readPropertyAsList(int id) const { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) @@ -556,12 +557,12 @@ QList *QQmlVMEMetaObject::readPropertyAsList(int id) const QV4::Scope scope(engine); QV4::Scoped v(scope, *(md->data() + id)); - if (!v || (int)v->d()->data().userType() != qMetaTypeId >()) { - QVariant variant(qVariantFromValue(QList())); + if (!v || (int)v->d()->data().userType() != qMetaTypeId> >()) { + QVariant variant(QVariant::fromValue(QVector>())); v = engine->newVariantObject(variant); md->set(engine, id, v); } - return static_cast *>(v->d()->data().data()); + return static_cast> *>(v->d()->data().data()); } QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const @@ -688,7 +689,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * *reinterpret_cast(a[0]) = readPropertyAsVariant(id); break; case QV4::CompiledData::Property::CustomList: { - QList *list = readPropertyAsList(id); + QVector> *list = readPropertyAsList(id); QQmlListProperty *p = static_cast *>(a[0]); *p = QQmlListProperty(object, list, list_append, list_count, list_at, diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 35bc35ce4b..08428f6123 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -190,7 +190,7 @@ public: QDateTime readPropertyAsDateTime(int id); QRectF readPropertyAsRectF(int id) const; QObject *readPropertyAsQObject(int id) const; - QList *readPropertyAsList(int id) const; + QVector > *readPropertyAsList(int id) const; void writeProperty(int id, int v); void writeProperty(int id, bool v); diff --git a/tests/auto/qml/qqmllanguage/data/listContainingDeleted.qml b/tests/auto/qml/qqmllanguage/data/listContainingDeleted.qml new file mode 100644 index 0000000000..efd273ddc6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/listContainingDeleted.qml @@ -0,0 +1,36 @@ +import QtQuick 2.12 + +Item { + width: 1024 + height: 800 + + property Component a: Component { + id: a + Item { + property list myList: [ + QtObject { + property bool enabled: true + } + ] + } + } + Component { + id: b + Item { + property list myList + + function test() { + for (var i = 0; i < myList.length; ++i) + console.log(i, "==", myList[i].enabled) + } + } + } + property Item instance + function doAssign(o) { + instance = b.createObject(null, {myList: o.myList}) + } + function use() { + instance.test() + } + +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 87468c329c..d7ef9999d0 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -303,6 +303,8 @@ private slots: void typeWrapperToVariant(); + void listContainingDeletedObject(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -5100,6 +5102,24 @@ void tst_qqmllanguage::typeWrapperToVariant() QVERIFY(target); } +void tst_qqmllanguage::listContainingDeletedObject() +{ + QQmlEngine engine; + auto url = testFileUrl("listContainingDeleted.qml"); + const QString message = url.toString() + ":24: TypeError: Cannot read property 'enabled' of null"; + QTest::ignoreMessage(QtMsgType::QtWarningMsg, message.toUtf8().data()); + QQmlComponent comp(&engine, url); + QScopedPointer root(comp.create()); + QVERIFY(root); + + auto cmp = root->property("a").value(); + auto o = cmp->create(); + + QMetaObject::invokeMethod(root.get(), "doAssign", Q_ARG(QVariant, QVariant::fromValue(o))); + delete o; + QMetaObject::invokeMethod(root.get(), "use"); + +} QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3