// Copyright (C) 2019 The Qt Company Ltd. // Copyright (C) 2013 Olivier Goffart // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QOBJECT_P_H #define QOBJECT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience // of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header // file may change from version to version without notice, or even be removed. // // We mean it. // #include #include "QtCore/qcoreevent.h" #include "QtCore/qlist.h" #include "QtCore/qobject.h" #include "QtCore/qpointer.h" #include "QtCore/qreadwritelock.h" #include "QtCore/qsharedpointer.h" #include "QtCore/qvariant.h" #include "QtCore/qproperty.h" #include "QtCore/private/qproperty_p.h" #include QT_BEGIN_NAMESPACE class QVariant; class QThreadData; class QObjectConnectionListVector; namespace QtSharedPointer { struct ExternalRefCountData; } /* for Qt Test */ struct QSignalSpyCallbackSet { typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv); typedef void (*EndCallback)(QObject *caller, int signal_or_method_index); BeginCallback signal_begin_callback, slot_begin_callback; EndCallback signal_end_callback, slot_end_callback; }; void Q_CORE_EXPORT qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set); extern Q_CORE_EXPORT QBasicAtomicPointer qt_signal_spy_callback_set; enum { QObjectPrivateVersion = QT_VERSION }; class Q_CORE_EXPORT QAbstractDeclarativeData { public: static void (*destroyed)(QAbstractDeclarativeData *, QObject *); static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **); static int (*receivers)(QAbstractDeclarativeData *, const QObject *, int); static bool (*isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int); static void (*setWidgetParent)(QObject *, QObject *); // Used by the QML engine to specify parents for widgets. Set by QtWidgets. }; class Q_CORE_EXPORT QObjectPrivate : public QObjectData { public: Q_DECLARE_PUBLIC(QObject) struct ExtraData { ExtraData(QObjectPrivate *ptr) : parent(ptr) { } inline void setObjectNameForwarder(const QString &name) { parent->q_func()->setObjectName(name); } inline void nameChangedForwarder(const QString &name) { Q_EMIT parent->q_func()->objectNameChanged(name, QObject::QPrivateSignal()); } QList propertyNames; QList propertyValues; QList runningTimers; QList> eventFilters; Q_OBJECT_COMPAT_PROPERTY(QObjectPrivate::ExtraData, QString, objectName, &QObjectPrivate::ExtraData::setObjectNameForwarder, &QObjectPrivate::ExtraData::nameChangedForwarder) QObjectPrivate *parent; }; void ensureExtraData() { if (!extraData) extraData = new ExtraData(this); } typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); struct Connection; struct ConnectionData; struct ConnectionList; struct ConnectionOrSignalVector; struct SignalVector; struct Sender; /* This contains the all connections from and to an object. The signalVector contains the lists of connections for a given signal. The index in the vector correspond to the signal index. The signal index is the one returned by QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal). allsignals contains a list of special connections that will get invoked on any signal emission. This is done by connecting to signal index -1. This vector is protected by the object mutex (signalSlotLock()) Each Connection is also part of a 'senders' linked list. This one contains all connections connected to a slot in this object. The mutex of the receiver must be locked when touching the pointers of this linked list. */ QObjectPrivate(int version = QObjectPrivateVersion); virtual ~QObjectPrivate(); void deleteChildren(); // used to clear binding storage early in ~QObject void clearBindingStorage(); inline void checkForIncompatibleLibraryVersion(int version) const; void setParent_helper(QObject *); void moveToThread_helper(); void setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status); void _q_reregisterTimers(void *pointer); bool isSender(const QObject *receiver, const char *signal) const; QObjectList receiverList(const char *signal) const; QObjectList senderList() const; inline void ensureConnectionData(); inline void addConnection(int signal, Connection *c); static inline bool removeConnection(Connection *c); static QObjectPrivate *get(QObject *o) { return o->d_func(); } static const QObjectPrivate *get(const QObject *o) { return o->d_func(); } int signalIndex(const char *signalName, const QMetaObject **meta = nullptr) const; bool isSignalConnected(uint signalIdx, bool checkDeclarative = true) const; bool maybeSignalConnected(uint signalIndex) const; inline bool isDeclarativeSignalConnected(uint signalIdx) const; // To allow abitrary objects to call connectNotify()/disconnectNotify() without making // the API public in QObject. This is used by QQmlNotifierEndpoint. inline void connectNotify(const QMetaMethod &signal); inline void disconnectNotify(const QMetaMethod &signal); void reinitBindingStorageAfterThreadMove(); template static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection); 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, 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); virtual std::string flagsForDumping() const; public: mutable ExtraData *extraData; // extra data set by the user // This atomic requires acquire/release semantics in a few places, // e.g. QObject::moveToThread must synchronize with QCoreApplication::postEvent, // because postEvent is thread-safe. // However, most of the code paths involving QObject are only reentrant and // not thread-safe, so synchronization should not be necessary there. QAtomicPointer threadData; // id of the thread that owns the object using ConnectionDataPointer = QExplicitlySharedDataPointer; QAtomicPointer connections; union { QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is set QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module }; // these objects are all used to indicate that a QObject was deleted // plus QPointer, which keeps a separate list QAtomicPointer sharedRefcount; }; /* Catch mixing of incompatible library versions. Should be called from the constructor of every non-final subclass of QObjectPrivate, to ensure we catch incompatibilities between the intermediate base and subclasses thereof. */ inline void QObjectPrivate::checkForIncompatibleLibraryVersion(int version) const { #if defined(QT_BUILD_INTERNAL) // Don't check the version parameter in internal builds. // This allows incompatible versions to be loaded, possibly for testing. Q_UNUSED(version); #else if (Q_UNLIKELY(version != QObjectPrivateVersion)) { qFatal("Cannot mix incompatible Qt library (%d.%d.%d) with this library (%d.%d.%d)", (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff, (QObjectPrivateVersion >> 16) & 0xff, (QObjectPrivateVersion >> 8) & 0xff, QObjectPrivateVersion & 0xff); } #endif } inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const { return declarativeData && QAbstractDeclarativeData::isSignalConnected && QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index); } inline void QObjectPrivate::connectNotify(const QMetaMethod &signal) { q_ptr->connectNotify(signal); } inline void QObjectPrivate::disconnectNotify(const QMetaMethod &signal) { q_ptr->disconnectNotify(signal); } namespace QtPrivate { template struct FunctionStorageByValue { Func f; Func &func() noexcept { return f; } }; template struct FunctionStorageEmptyBaseClassOptimization : Func { Func &func() noexcept { return *this; } using Func::Func; }; template using FunctionStorage = typename std::conditional_t< std::conjunction_v< std::is_empty, std::negation> >, FunctionStorageEmptyBaseClassOptimization, FunctionStorageByValue >; template inline void assertObjectType(QObjectPrivate *d) { using Obj = std::remove_pointer_t()->q_func())>; assertObjectType(d->q_ptr); } template class QPrivateSlotObject : public QSlotObjectBase, private FunctionStorage { typedef QtPrivate::FunctionPointer FuncType; static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) { switch (which) { case Destroy: delete static_cast(this_); break; case Call: FuncType::template call(static_cast(this_)->func(), static_cast(QObjectPrivate::get(r)), a); break; case Compare: *ret = *reinterpret_cast(a) == static_cast(this_)->func(); break; case NumOperations: ; } } public: explicit QPrivateSlotObject(Func f) : QSlotObjectBase(&impl), FunctionStorage{std::move(f)} {} }; } //namespace QtPrivate template inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type) { typedef QtPrivate::FunctionPointer SignalType; typedef QtPrivate::FunctionPointer SlotType; static_assert(QtPrivate::HasQ_OBJECT_Macro::Value, "No Q_OBJECT in the class with the signal"); //compilation error if the arguments does not match. static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount), "The slot requires more arguments than the signal provides."); static_assert((QtPrivate::CheckCompatibleArguments::value), "Signal and slot arguments are not compatible."); static_assert((QtPrivate::AreArgumentsCompatible::value), "Return type of the slot is not compatible with the return type of the signal."); const int *types = nullptr; if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) types = QtPrivate::ConnectionTypes::types(); return QObject::connectImpl(sender, reinterpret_cast(&signal), receiverPrivate->q_ptr, reinterpret_cast(&slot), new QtPrivate::QPrivateSlotObject::Value, typename SignalType::ReturnType>(slot), type, types, &SignalType::Object::staticMetaObject); } template bool QObjectPrivate::disconnect(const typename QtPrivate::FunctionPointer< Func1 >::Object* sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object* receiverPrivate, Func2 slot) { typedef QtPrivate::FunctionPointer SignalType; typedef QtPrivate::FunctionPointer SlotType; static_assert(QtPrivate::HasQ_OBJECT_Macro::Value, "No Q_OBJECT in the class with the signal"); //compilation error if the arguments does not match. static_assert((QtPrivate::CheckCompatibleArguments::value), "Signal and slot arguments are not compatible."); return QObject::disconnectImpl(sender, reinterpret_cast(&signal), receiverPrivate->q_ptr, reinterpret_cast(&slot), &SignalType::Object::staticMetaObject); } class QSemaphore; class Q_CORE_EXPORT QAbstractMetaCallEvent : public QEvent { public: QAbstractMetaCallEvent(const QObject *sender, int signalId, QSemaphore *semaphore = nullptr) : QEvent(MetaCall), signalId_(signalId), sender_(sender) #if QT_CONFIG(thread) , semaphore_(semaphore) #endif { Q_UNUSED(semaphore); } ~QAbstractMetaCallEvent(); virtual void placeMetaCall(QObject *object) = 0; inline const QObject *sender() const { return sender_; } inline int signalId() const { return signalId_; } private: int signalId_; const QObject *sender_; #if QT_CONFIG(thread) QSemaphore *semaphore_; #endif }; class Q_CORE_EXPORT QMetaCallEvent : public QAbstractMetaCallEvent { public: // blocking queued with semaphore - args always owned by caller QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, void **args, QSemaphore *semaphore); QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender, int signalId, void **args, QSemaphore *semaphore); // queued - args allocated by event, copied by caller QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, int nargs); QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender, int signalId, int nargs); ~QMetaCallEvent() override; inline int id() const { return d.method_offset_ + d.method_relative_; } inline const void * const* args() const { return d.args_; } inline void ** args() { return d.args_; } inline const QMetaType *types() const { return reinterpret_cast(d.args_ + d.nargs_); } inline QMetaType *types() { return reinterpret_cast(d.args_ + d.nargs_); } virtual void placeMetaCall(QObject *object) override; private: inline void allocArgs(); struct Data { QtPrivate::QSlotObjectBase *slotObj_; void **args_; QObjectPrivate::StaticMetaCallFunction callFunction_; int nargs_; ushort method_offset_; ushort method_relative_; } d; // preallocate enough space for three arguments alignas(void *) char prealloc_[3 * sizeof(void *) + 3 * sizeof(QMetaType)]; }; class QBoolBlocker { Q_DISABLE_COPY_MOVE(QBoolBlocker) public: explicit inline QBoolBlocker(bool &b, bool value = true) : block(b), reset(b) { block = value; } inline ~QBoolBlocker() { block = reset; } private: bool █ bool reset; }; void Q_CORE_EXPORT qDeleteInEventHandler(QObject *o); struct QAbstractDynamicMetaObject; struct Q_CORE_EXPORT QDynamicMetaObjectData { virtual ~QDynamicMetaObjectData(); virtual void objectDestroyed(QObject *) { delete this; } virtual QMetaObject *toDynamicMetaObject(QObject *) = 0; virtual int metaCall(QObject *, QMetaObject::Call, int _id, void **) = 0; }; struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QDynamicMetaObjectData, public QMetaObject { ~QAbstractDynamicMetaObject(); QMetaObject *toDynamicMetaObject(QObject *) override { return this; } virtual int createProperty(const char *, const char *) { return -1; } int metaCall(QObject *, QMetaObject::Call c, int _id, void **a) override { return metaCall(c, _id, a); } virtual int metaCall(QMetaObject::Call, int _id, void **) { return _id; } // Compat overload }; inline const QBindingStorage *qGetBindingStorage(const QObjectPrivate *o) { return &o->bindingStorage; } inline QBindingStorage *qGetBindingStorage(QObjectPrivate *o) { return &o->bindingStorage; } inline const QBindingStorage *qGetBindingStorage(const QObjectPrivate::ExtraData *ed) { return &ed->parent->bindingStorage; } inline QBindingStorage *qGetBindingStorage(QObjectPrivate::ExtraData *ed) { return &ed->parent->bindingStorage; } QT_END_NAMESPACE #endif // QOBJECT_P_H