aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2024-01-17 09:01:20 +0100
committerUlf Hermann <ulf.hermann@qt.io>2024-01-17 20:30:47 +0100
commit9d8e78a2f2661d8a1f2909bfe8a20e15f833af2e (patch)
tree571b61e916ce6baf1ab92f3dceff942f62f15f20
parent1a5f10746ab42527f9a0c7aa170b3f593b31bffb (diff)
QtQml: Do not call signal handlers on half-deleted objects
Pick-to: 6.7 6.6 Fixes: QTBUG-121022 Change-Id: Icdefd6bef4906700d88eca47c09d0abe54f1eec9 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp4
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml36
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp18
3 files changed, 57 insertions, 1 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index c4ae949ea8..5463133801 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1197,13 +1197,15 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
{
- Q_UNUSED(receiver);
switch (which) {
case Destroy: {
delete static_cast<QObjectSlotDispatcher*>(this_);
}
break;
case Call: {
+ if (QQmlData::wasDeleted(receiver))
+ break;
+
QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
ExecutionEngine *v4 = This->function.engine();
// Might be that we're still connected to a signal that's emitted long
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml
new file mode 100644
index 0000000000..efbbc9fedc
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml
@@ -0,0 +1,36 @@
+import Qt.test
+import QtQml
+
+QtObject {
+ id: root
+
+ property int a: 0
+ property int b: 0
+
+ signal someSignal
+
+ function destroyObj() {
+ obj.destroy()
+ }
+
+ function test() {
+ ++a
+ }
+
+ component DestructionReceiver: QtObject {
+ // Has its own context and therefore can receive Component.onDestruction
+ }
+
+ property QtObject obj: QtObject {
+ property QtObject inner: DestructionReceiver {
+ Component.onDestruction: {
+ // The outer obj is already queued for deletion.
+ // We don't want to see this signal delivered.
+ root.someSignal();
+ ++root.b
+ }
+ }
+ }
+
+ Component.onCompleted: someSignal.connect(obj, test)
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 35b7e6a535..6680dd1429 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -3910,7 +3910,25 @@ void tst_qqmlecmascript::scriptConnect()
engine.clearSingletons();
QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
QCOMPARE(obj.data()->property("a").toInt(), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.deletion.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(!obj.isNull());
+
+ QCOMPARE(obj->property("a"), 0);
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj->property("a"), 1);
+
+ QCOMPARE(obj->property("b"), 0);
+ QMetaObject::invokeMethod(obj.data(), "destroyObj", Qt::DirectConnection);
+ QTRY_COMPARE(obj->property("b"), 1);
+ QCOMPARE(obj->property("a"), 1);
}
}