// 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 #include "QtCore/qlist.h" #include "QtCore/qobject.h" #include "QtCore/qpointer.h" #include "QtCore/qvariant.h" #include "QtCore/qproperty.h" #include #include "QtCore/private/qproperty_p.h" #include QT_BEGIN_NAMESPACE #ifdef Q_MOC_RUN #define QT_ANONYMOUS_PROPERTY(text) QT_ANONYMOUS_PROPERTY(text) #define QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) #elif !defined QT_NO_META_MACROS #define QT_ANONYMOUS_PROPERTY(...) QT_ANNOTATE_CLASS(qt_anonymous_property, __VA_ARGS__) #define QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) QT_ANNOTATE_CLASS2(qt_anonymous_private_property, d, text) #endif #define QT_CONCAT(B, M, m, u) QT_CONCAT2(B, M, m, u) #define QT_CONCAT2(B, M, m, u) B ## M ## _ ## m ## _ ## u #if defined(QT_BUILD_INTERNAL) && !QT_CONFIG(elf_private_full_version) // Don't check the version parameter in internal builds. // This allows incompatible versions to be loaded, possibly for testing. enum QObjectPrivateVersionEnum #else enum QT_CONCAT(QtPrivate_, QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_PATCH) #endif { QObjectPrivateVersion = QT_VERSION }; #undef QT_CONCAT #undef QT_CONCAT2 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; 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); } void setObjectNameWithoutBindings(const QString &name); typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); struct Connection; struct ConnectionData; struct ConnectionList; struct ConnectionOrSignalVector; struct SignalVector; struct Sender; struct TaggedSignalVector; /* 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(decltype(QObjectPrivateVersion) version = QObjectPrivateVersion); virtual ~QObjectPrivate(); void deleteChildren(); // used to clear binding storage early in ~QObject void clearBindingStorage(); void setParent_helper(QObject *); void moveToThread_helper(); void setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status); QObjectList receiverList(const char *signal) 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; #ifndef QT_NO_DEBUG_STREAM virtual void writeToDebugStream(QDebug &) const; #endif QtPrivate::QPropertyAdaptorSlotObject * getPropertyAdaptorSlotObject(const QMetaProperty &property); 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; }; inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const { return !isDeletingChildren && 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 { inline const QObject *getQObject(const QObjectPrivate *d) { return d->q_func(); } template using FunctionStorage = QtPrivate::CompactStorage; 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; #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) #else static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret) #endif { const auto that = static_cast(this_); switch (which) { case Destroy: delete that; break; case Call: FuncType::template call(that->object(), static_cast(QObjectPrivate::get(r)), a); break; case Compare: *ret = *reinterpret_cast(a) == that->object(); 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), QtPrivate::getQObject(receiverPrivate), 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 QLatch; class Q_CORE_EXPORT QAbstractMetaCallEvent : public QEvent { public: QAbstractMetaCallEvent(const QObject *sender, int signalId, QLatch *latch = nullptr) : QEvent(MetaCall), signalId_(signalId), sender_(sender), latch(latch) {} ~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_; QLatch *latch; }; class Q_CORE_EXPORT QMetaCallEvent : public QAbstractMetaCallEvent { public: // blocking queued with latch - arguments always remain owned by the caller QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, void **args, QLatch *latch); QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender, int signalId, void **args, QLatch *latch); QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj, const QObject *sender, int signalId, void **args, QLatch *latch); // OLD - queued - args allocated by event, copied by caller Q_DECL_DEPRECATED_X("Remove this constructor once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, int nargs); Q_DECL_DEPRECATED_X("Remove this constructor once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender, int signalId, int nargs); Q_DECL_DEPRECATED_X("Remove this constructor once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj, const QObject *sender, int signalId, int nargs); ~QMetaCallEvent() override; inline int id() const { return d.method_offset_ + d.method_relative_; } Q_DECL_DEPRECATED_X("Remove this function once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") inline void ** args() { return d.args_; } Q_DECL_DEPRECATED_X("Remove this function once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") inline QMetaType *types() { return reinterpret_cast(d.args_ + d.nargs_); } virtual void placeMetaCall(QObject *object) override; protected: // Move to QQueuedMetaCallEvent once the qtdeclarative patch is merged. // QMetaCallEvent should not alloc anything anymore. inline void allocArgs(); struct Data { QtPrivate::SlotObjUniquePtr slotObj_; void **args_; QObjectPrivate::StaticMetaCallFunction callFunction_; int nargs_; ushort method_offset_; ushort method_relative_; } d; inline QMetaCallEvent(const QObject *sender, int signalId, Data &&data); inline void * const *args() const { return d.args_; } inline QMetaType const *types() const { return reinterpret_cast(d.args_ + d.nargs_); } // Space for 5 argument pointers and types (including 1 return arg). // Contiguous so that we can make one calloc() for both the pointers and the types when necessary. // Move to QQueuedMetaCallEvent once the qtdeclarative patch is merged. alignas(void *) char prealloc_[5 * sizeof(void *) + 5 * sizeof(QMetaType)]; }; class Q_CORE_EXPORT QQueuedMetaCallEvent : public QMetaCallEvent { public: // queued - arguments are allocated and copied from argValues by these constructors QQueuedMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, const void * const *argValues); QQueuedMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender, int signalId, int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, const void * const *argValues); QQueuedMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj, const QObject *sender, int signalId, int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, const void * const *argValues); ~QQueuedMetaCallEvent() override; private: inline void copyArgValues(int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, const void * const *argValues); static inline bool typeFitsInPlace(const QMetaType type); struct ArgValueStorage { // size and alignment matching QString, QList, etc static constexpr size_t MaxSize = 3 * sizeof(void *); alignas(void *) char storage[MaxSize]; }; static constexpr int InplaceValuesCapacity = 3; ArgValueStorage valuesPrealloc_[InplaceValuesCapacity]; }; // The total QQueuedMetaCallEvent size is 224 bytes which is a 32-byte multiple, efficient for memory allocators. struct QAbstractDynamicMetaObject; struct Q_CORE_EXPORT QDynamicMetaObjectData { virtual ~QDynamicMetaObjectData(); virtual void objectDestroyed(QObject *) { delete this; } #if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) virtual const QMetaObject *toDynamicMetaObject(QObject *) const = 0; #else virtual QMetaObject *toDynamicMetaObject(QObject *) = 0; #endif virtual int metaCall(QObject *, QMetaObject::Call, int _id, void **) = 0; }; struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QDynamicMetaObjectData, public QMetaObject { ~QAbstractDynamicMetaObject(); #if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) const QMetaObject *toDynamicMetaObject(QObject *) const override { return this; } #else QMetaObject *toDynamicMetaObject(QObject *) override { return this; } #endif 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