diff options
Diffstat (limited to 'src/corelib/kernel/qproperty_p.h')
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 130 |
1 files changed, 98 insertions, 32 deletions
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index a7fb9e9a1a..376482a6af 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -19,12 +19,13 @@ #include <qproperty.h> #include <qmetaobject.h> -#include <qscopedpointer.h> #include <qscopedvaluerollback.h> #include <qvariant.h> #include <vector> #include <QtCore/QVarLengthArray> +#include <memory> + QT_BEGIN_NAMESPACE namespace QtPrivate { @@ -83,6 +84,7 @@ struct QPropertyBindingDataPointer void Q_ALWAYS_INLINE addObserver(QPropertyObserver *observer); inline void setFirstObserver(QPropertyObserver *observer); inline QPropertyObserverPointer firstObserver() const; + static QPropertyProxyBindingData *proxyData(QtPrivate::QPropertyBindingData *ptr); inline int observerCount() const; @@ -93,11 +95,12 @@ struct QPropertyBindingDataPointer } }; -struct [[nodiscard]] QPropertyObserverNodeProtector +struct QPropertyObserverNodeProtector { Q_DISABLE_COPY_MOVE(QPropertyObserverNodeProtector) QPropertyObserverBase m_placeHolder; + Q_NODISCARD_CTOR QPropertyObserverNodeProtector(QPropertyObserver *observer) { // insert m_placeholder after observer into the linked list @@ -123,25 +126,37 @@ struct QPropertyObserverPointer void unlink() { unlink_common(); +#if QT_DEPRECATED_SINCE(6, 6) + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias) ptr->aliasData = nullptr; + QT_WARNING_POP +#endif } void unlink_fast() { +#if QT_DEPRECATED_SINCE(6, 6) + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsAlias); + QT_WARNING_POP +#endif unlink_common(); } - void setBindingToNotify(QPropertyBindingPrivate *binding); + void setBindingToNotify(QPropertyBindingPrivate *binding) + { + Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder); + ptr->binding = binding; + ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding); + } + void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding); void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler); enum class Notify {Everything, OnlyChangeHandlers}; - template<Notify notifyPolicy = Notify::Everything> void notify(QUntypedPropertyData *propertyDataPtr); - void notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr); #ifndef QT_NO_DEBUG void noSelfDependencies(QPropertyBindingPrivate *binding); #else @@ -158,7 +173,7 @@ struct QPropertyObserverPointer { Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding); return ptr->binding; - }; + } private: void unlink_common() @@ -192,6 +207,7 @@ struct BindingEvaluationState QPropertyBindingPrivate *binding; BindingEvaluationState *previousState = nullptr; BindingEvaluationState **currentState = nullptr; + QVarLengthArray<const QPropertyBindingData *, 8> alreadyCaptureProperties; }; /*! @@ -216,6 +232,33 @@ struct CompatPropertySafePoint QtPrivate::BindingEvaluationState *bindingState = nullptr; }; +/*! + * \internal + * While the regular QProperty notification for a compat property runs we + * don't want to have any currentCompatProperty set. This would be a _different_ + * one than the one we are current evaluating. Therefore it's misleading and + * prevents the registering of actual dependencies. + */ +struct CurrentCompatPropertyThief +{ + Q_DISABLE_COPY_MOVE(CurrentCompatPropertyThief) +public: + CurrentCompatPropertyThief(QBindingStatus *status) + : status(&status->currentCompatProperty) + , stolen(std::exchange(status->currentCompatProperty, nullptr)) + { + } + + ~CurrentCompatPropertyThief() + { + *status = stolen; + } + +private: + CompatPropertySafePoint **status = nullptr; + CompatPropertySafePoint *stolen = nullptr; +}; + } class Q_CORE_EXPORT QPropertyBindingPrivate : public QtPrivate::RefCounted @@ -250,7 +293,7 @@ private: ObserverArray inlineDependencyObservers; // for things we are observing QPropertyObserverPointer firstObserver; // list of observers observing us - QScopedPointer<std::vector<QPropertyObserver>> heapObservers; // for things we are observing + std::unique_ptr<std::vector<QPropertyObserver>> heapObservers; // for things we are observing protected: QUntypedPropertyData *propertyDataPtr = nullptr; @@ -366,7 +409,6 @@ public: bool Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status); - void notifyRecursive(); void notifyNonRecursive(const PendingBindingObserverList &bindingObservers); enum NotificationState : bool { Delayed, Sent }; NotificationState notifyNonRecursive(); @@ -418,9 +460,9 @@ inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBind { auto &d = ptr->d_ref(); if (ptr->isNotificationDelayed()) { - QPropertyProxyBindingData *proxyData - = reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit); - proxyData->originalBindingData = ptr; + QPropertyProxyBindingData *proxy = ptr->proxyData(); + Q_ASSERT(proxy); + proxy->originalBindingData = ptr; } // If QPropertyBindingData has been moved, and it has an observer // we have to adjust the firstObserver's prev pointer to point to @@ -438,6 +480,17 @@ inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() con return { reinterpret_cast<QPropertyObserver *>(ptr->d()) }; } +/*! + \internal + Returns the proxy data of \a ptr, or \c nullptr if \a ptr has no delayed notification + */ +inline QPropertyProxyBindingData *QPropertyBindingDataPointer::proxyData(QtPrivate::QPropertyBindingData *ptr) +{ + if (!ptr->isNotificationDelayed()) + return nullptr; + return ptr->proxyData(); +} + inline int QPropertyBindingDataPointer::observerCount() const { int count = 0; @@ -474,13 +527,16 @@ class QObjectCompatProperty : public QPropertyData<T> static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, QtPrivate::QPropertyBindingFunction binding) { auto *thisData = static_cast<ThisType *>(dataPtr); + QBindingStorage *storage = qGetBindingStorage(thisData->owner()); QPropertyData<T> copy; - binding.vtable->call(type, ©, binding.functor); - if constexpr (QTypeTraits::has_operator_equal_v<T>) - if (copy.valueBypassingBindings() == thisData->valueBypassingBindings()) - return false; + { + QtPrivate::CurrentCompatPropertyThief thief(storage->bindingStatus); + binding.vtable->call(type, ©, binding.functor); + if constexpr (QTypeTraits::has_operator_equal_v<T>) + if (copy.valueBypassingBindings() == thisData->valueBypassingBindings()) + return false; + } // ensure value and setValue know we're currently evaluating our binding - QBindingStorage *storage = qGetBindingStorage(thisData->owner()); QtPrivate::CompatPropertySafePoint guardThis(storage->bindingStatus, thisData); (thisData->owner()->*Setter)(copy.valueBypassingBindings()); return true; @@ -616,7 +672,7 @@ public: == QtPrivate::QPropertyBindingData::Evaluated) { // evaluateBindings() can trash the observers. We need to re-fetch here. if (QPropertyObserverPointer observer = d.firstObserver()) - observer.notifyOnlyChangeHandler(this); + observer.notify(this); for (auto&& bindingObserver: bindingObservers) bindingObserver.binding()->notifyNonRecursive(); } @@ -818,7 +874,14 @@ inline bool QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObse return true; } -template<QPropertyObserverPointer::Notify notifyPolicy> +/*! + \internal + + Walks through the list of property observers, and calls any ChangeHandler + found there. + It doesn't do anything with bindings, which are only handled in + QPropertyBindingPrivate::evaluateRecursive. + */ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr) { auto observer = const_cast<QPropertyObserver*>(ptr); @@ -857,31 +920,22 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP break; } case QPropertyObserver::ObserverNotifiesBinding: - { - if constexpr (notifyPolicy == Notify::Everything) { - auto bindingToNotify = observer->binding; - QPropertyObserverNodeProtector protector(observer); - bindingToNotify->notifyRecursive(); - next = protector.next(); - } break; - } case QPropertyObserver::ObserverIsPlaceholder: // recursion is already properly handled somewhere else break; +#if QT_DEPRECATED_SINCE(6, 6) + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED case QPropertyObserver::ObserverIsAlias: break; + QT_WARNING_POP +#endif default: Q_UNREACHABLE(); } observer = next; } } -inline void QPropertyObserverPointer::notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr) -{ - notify<Notify::OnlyChangeHandlers>(propertyDataPtr); -} - inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector() { QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)}; @@ -894,7 +948,15 @@ QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept : QPropertyObserverPointer{d}.binding()->addRef(); } -QBindingObserverPtr::~QBindingObserverPtr() { if (d) QPropertyObserverPointer{d}.binding()->deref(); } +QBindingObserverPtr::~QBindingObserverPtr() +{ + if (!d) + return; + + QPropertyBindingPrivate *bindingPrivate = binding(); + if (!bindingPrivate->deref()) + QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate); +} QPropertyBindingPrivate *QBindingObserverPtr::binding() const noexcept { return QPropertyObserverPointer{d}.binding(); } @@ -907,7 +969,11 @@ class QPropertyAdaptorSlotObject : public QUntypedPropertyData, public QSlotObje QObject *obj; QMetaProperty metaProperty_; +#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 QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty& p); |