From d8a6a67579fe5f3a6b70b6774b6eee00a55834d5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 6 Jun 2013 13:51:19 +0200 Subject: Add internal functions to QObjectPrivate for signal & slot connections in QML In QML it is common to connect signals to slots that are implemented as JavaScript functions. QML used to maintain separate data structures that mirrored the QObject connection list and kept references to the JavaScript objects necessary to perform the call on signal activation. The recent addition of functor based QObject::connect makes it possible to store this information in QSlotObjectBase sub-class instead, which eliminates any extra bookkeeping. This patch adds internal connect and disconnect overloads to QObjectPrivate that allow for connecting QObject *sender, int signalIndex to a given QSlotObjectBase and similar for disconnect. Change-Id: I90f43d13eb95bd884d752484cf4faacc446f4d6a Reviewed-by: Olivier Goffart --- src/corelib/kernel/qobject.cpp | 60 ++++++++++++++++++++++- src/corelib/kernel/qobject_p.h | 7 +++ src/corelib/kernel/qobjectdefs.h | 1 + tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 51 +++++++++++++++++++ 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index d4d2d527d4..495ce7c948 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4255,7 +4255,7 @@ void qDeleteInEventHandler(QObject *o) must not have an overloaded or templated operator(). */ -/** +/*! \internal Implementation of the template version of connect @@ -4280,12 +4280,13 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, const int *types, const QMetaObject *senderMetaObject) { - if (!sender || !signal || !slotObj || !senderMetaObject) { + if (!signal) { qWarning("QObject::connect: invalid null parameter"); if (slotObj) slotObj->destroyIfLastRef(); return QMetaObject::Connection(); } + int signal_index = -1; void *args[] = { &signal_index, signal }; for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) { @@ -4299,6 +4300,27 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa return QMetaObject::Connection(0); } signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject); + return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject); +} + +/*! + \internal + + Internal version of connect used by the template version of QObject::connect (called via connectImpl) and + also used by the QObjectPrivate::connect version used by QML. The signal_index is expected to be relative + to the number of signals. + */ +QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index, + const QObject *receiver, void **slot, + QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, + const int *types, const QMetaObject *senderMetaObject) +{ + if (!sender || !slotObj || !senderMetaObject) { + qWarning("QObject::connect: invalid null parameter"); + if (slotObj) + slotObj->destroyIfLastRef(); + return QMetaObject::Connection(); + } QObject *s = const_cast(sender); QObject *r = const_cast(receiver); @@ -4469,6 +4491,40 @@ bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, receiver, -1, slot); } +/*! + \internal + Used by QML to connect a signal by index to a slot implemented in JavaScript (wrapped in a custom QSlotOBjectBase subclass). + + 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) +{ + if (!sender) { + qWarning("QObject::connect: invalid null parameter"); + if (slotObj) + slotObj->destroyIfLastRef(); + return QMetaObject::Connection(); + } + const QMetaObject *senderMetaObject = sender->metaObject(); + signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); + + return QObjectPrivate::connectImpl(sender, signal_index, sender, /*slot*/0, slotObj, type, /*types*/0, senderMetaObject); +} + +/*! + \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. + */ +bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, void **slot) +{ + const QMetaObject *senderMetaObject = sender->metaObject(); + signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); + + return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, sender, -1, slot); +} + /*! \class QMetaObject::Connection \inmodule QtCore Represents a handle to a signal-slot connection. diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index e849ec1599..3c43972ac9 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -207,6 +207,13 @@ public: template static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot); + + static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index, + const QObject *receiver, void **slot, + QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, + const int *types, const QMetaObject *senderMetaObject); + static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type); + static bool disconnect(const QObject *sender, int signal_index, void **slot); public: ExtraData *extraData; // extra data set by the user QThreadData *threadData; // id of the thread that owns the object diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index afbe1a5ece..c489344b10 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -456,6 +456,7 @@ class Q_CORE_EXPORT QMetaObject::Connection { void *d_ptr; //QObjectPrivate::Connection* explicit Connection(void *data) : d_ptr(data) { } friend class QObject; + friend class QObjectPrivate; friend struct QMetaObject; public: ~Connection(); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index d16369de02..9bf28430b0 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -146,6 +146,7 @@ private slots: void connectFunctorOverloads(); void disconnectDoesNotLeakFunctor(); void connectBase(); + void qmlConnect(); }; struct QObjectCreatedOnShutdown @@ -5835,6 +5836,56 @@ void tst_QObject::connectBase() QCOMPARE( r1.count_slot3, 1 ); } +struct QmlReceiver : public QtPrivate::QSlotObjectBase +{ + int callCount; + void *magic; + + QmlReceiver() + : QtPrivate::QSlotObjectBase(&impl) + , callCount(0) + , magic(0) + {} + + static void impl(int which, QSlotObjectBase *this_, QObject *, void **metaArgs, bool *ret) + { + switch (which) { + case Destroy: delete static_cast(this_); return; + case Call: static_cast(this_)->callCount++; return; + case Compare: *ret = static_cast(this_)->magic == metaArgs[0]; return; + case NumOperations: break; + } + } +}; + +void tst_QObject::qmlConnect() +{ +#ifdef QT_BUILD_INTERNAL + SenderObject sender; + QmlReceiver *receiver = new QmlReceiver; + receiver->magic = receiver; + receiver->ref(); + + QVERIFY(QObjectPrivate::connect(&sender, sender.metaObject()->indexOfSignal("signal1()"), + receiver, Qt::AutoConnection)); + + QCOMPARE(receiver->callCount, 0); + sender.emitSignal1(); + QCOMPARE(receiver->callCount, 1); + + void *a[] = { + receiver + }; + QVERIFY(QObjectPrivate::disconnect(&sender, sender.metaObject()->indexOfSignal("signal1()"), reinterpret_cast(&a))); + + sender.emitSignal1(); + QCOMPARE(receiver->callCount, 1); + + receiver->destroyIfLastRef(); +#else + QSKIP("Needs QT_BUILD_INTERNAL"); +#endif +} QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" -- cgit v1.2.3