QtQml: Do not call signal handlers on half-deleted objects
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> (cherry picked from commit9d8e78a2f2
) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit5d53ea9844
)
This commit is contained in:
parent
cddf2b0b4c
commit
c24716c7fe
|
@ -1085,13 +1085,15 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
|
||||||
|
|
||||||
static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
|
static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret)
|
||||||
{
|
{
|
||||||
Q_UNUSED(receiver);
|
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case Destroy: {
|
case Destroy: {
|
||||||
delete static_cast<QObjectSlotDispatcher*>(this_);
|
delete static_cast<QObjectSlotDispatcher*>(this_);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Call: {
|
case Call: {
|
||||||
|
if (QQmlData::wasDeleted(receiver))
|
||||||
|
break;
|
||||||
|
|
||||||
QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
|
QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
|
||||||
ExecutionEngine *v4 = This->function.engine();
|
ExecutionEngine *v4 = This->function.engine();
|
||||||
// Might be that we're still connected to a signal that's emitted long
|
// Might be that we're still connected to a signal that's emitted long
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -3914,7 +3914,25 @@ void tst_qqmlecmascript::scriptConnect()
|
||||||
engine.clearSingletons();
|
engine.clearSingletons();
|
||||||
QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
|
QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
|
||||||
QCOMPARE(obj.data()->property("a").toInt(), 1);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue