diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2021-01-15 09:42:19 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-01-21 09:06:54 +0000 |
commit | f83f4badce46b5572fa4c85ba2c3420a19d28450 (patch) | |
tree | 383d2a3c5afdf838a4f6ca0dc9d7e1f2b9344ecf | |
parent | 563f8bc744f79a093cfda5467c9ee5a6d0081cf7 (diff) |
Add new special QObjectPrivate::{connect, disconnect} for QML
Original QML-specific connection mechanism ignores the receiver argument
and uses sender as receiver. This causes uncontrollable memory growth
in certain cases as connections on receiver persist even after receiver
is destroyed
New connect() with receiver parameter uses underlying API correctly,
disconnect is provided for the symmetry (not sure it's really needed)
Task-number: QTBUG-86368
Change-Id: I4580d75b617cb2c4dfb971a4dfb8e943e325572b
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit 6f520abdab7120789800208dde837b3836f762cc)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 49 | ||||
-rw-r--r-- | src/corelib/kernel/qobject_p.h | 6 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 28 |
3 files changed, 80 insertions, 3 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 68082910dc..3eda1b384e 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -5062,12 +5062,33 @@ bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject /*! \internal - Used by QML to connect a signal by index to a slot implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass). + Used by QML to connect a signal by index to a slot implemented in JavaScript + (wrapped in a custom QSlotObjectBase subclass). + + This version of connect assumes that sender and receiver are the same object. The signal_index is an index relative to the number of methods. */ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type) { + return QObjectPrivate::connect(sender, signal_index, sender, slotObj, type); +} + +/*! + \internal + Used by QML to connect a signal by index to a slot implemented in JavaScript + (wrapped in a custom QSlotObjectBase subclass). + + This is an overload that should be used when \a sender and \a receiver are + different objects. + + The signal_index is an index relative to the number of methods. + */ +QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index, + const QObject *receiver, + QtPrivate::QSlotObjectBase *slotObj, + Qt::ConnectionType type) +{ if (!sender) { qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter"); if (slotObj) @@ -5077,7 +5098,8 @@ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signa const QMetaObject *senderMetaObject = sender->metaObject(); signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); - return QObjectPrivate::connectImpl(sender, signal_index, sender, /*slot*/nullptr, slotObj, type, /*types*/nullptr, senderMetaObject); + return QObjectPrivate::connectImpl(sender, signal_index, receiver, /*slot*/ nullptr, slotObj, + type, /*types*/ nullptr, senderMetaObject); } /*! @@ -5085,13 +5107,34 @@ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signa Used by QML to disconnect a signal by index that's connected to a slot implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass) In the QML case the slot is not a pointer to a pointer to the function to disconnect, but instead it is a pointer to an array of internal values required for the disconnect. + + This version of disconnect assumes that sender and receiver are the same object. */ bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, void **slot) { + return QObjectPrivate::disconnect(sender, signal_index, sender, slot); +} + +/*! + \internal + + Used by QML to disconnect a signal by index that's connected to a slot + implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass) In the + QML case the slot is not a pointer to a pointer to the function to disconnect, + but instead it is a pointer to an array of internal values required for the + disconnect. + + This is an overload that should be used when \a sender and \a receiver are + different objects. + */ +bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, const QObject *receiver, + void **slot) +{ const QMetaObject *senderMetaObject = sender->metaObject(); signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); - return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, sender, -1, slot); + return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, receiver, -1, + slot); } /*! diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index a9626a8ab0..4483e2b6ce 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -353,7 +353,13 @@ public: QtPrivate::QSlotObjectBase *slotObj, int type, const int *types, const QMetaObject *senderMetaObject); static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type); + static QMetaObject::Connection connect(const QObject *sender, int signal_index, + const QObject *receiver, + QtPrivate::QSlotObjectBase *slotObj, + Qt::ConnectionType type); static bool disconnect(const QObject *sender, int signal_index, void **slot); + static bool disconnect(const QObject *sender, int signal_index, const QObject *receiver, + void **slot); static bool disconnect(Connection *c); void ensureConnectionData() diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 5cf1f0e50f..68d90987e1 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -149,6 +149,7 @@ private slots: void connectBase(); void connectWarnings(); void qmlConnect(); + void qmlConnectToQObjectReceiver(); void exceptions(); void noDeclarativeParentChangedOnDestruction(); void deleteLaterInAboutToBlockHandler(); @@ -6787,6 +6788,33 @@ void tst_QObject::qmlConnect() #endif } +void tst_QObject::qmlConnectToQObjectReceiver() +{ +#ifdef QT_BUILD_INTERNAL + SenderObject sender; + QScopedPointer<QObject> receiver(new QObject); + QmlReceiver *slotObject = new QmlReceiver; + slotObject->magic = slotObject; + slotObject->ref(); // extra ref so that slot object is not implicitly deleted + + QVERIFY(QObjectPrivate::connect(&sender, sender.metaObject()->indexOfSignal("signal1()"), + receiver.get(), slotObject, Qt::AutoConnection)); + + QCOMPARE(slotObject->callCount, 0); + sender.emitSignal1(); + QCOMPARE(slotObject->callCount, 1); + + receiver.reset(); // this should disconnect the slotObject + + sender.emitSignal1(); + QCOMPARE(slotObject->callCount, 1); + + slotObject->destroyIfLastRef(); +#else + QSKIP("Needs QT_BUILD_INTERNAL"); +#endif +} + #ifndef QT_NO_EXCEPTIONS class ObjectException : public std::exception { }; |