diff options
Diffstat (limited to 'src/corelib/kernel/qproperty_p.h')
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 221 |
1 files changed, 193 insertions, 28 deletions
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index 00e811f4c3..8ae6664a2b 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -18,9 +18,12 @@ #include <private/qglobal_p.h> #include <qproperty.h> +#include <qmetaobject.h> #include <qscopedpointer.h> #include <qscopedvaluerollback.h> +#include <qvariant.h> #include <vector> +#include <QtCore/QVarLengthArray> QT_BEGIN_NAMESPACE @@ -29,6 +32,34 @@ namespace QtPrivate { struct QBindingStatusAccessToken {}; } + +/*! + \internal + Similar to \c QPropertyBindingPrivatePtr, but stores a + \c QPropertyObserver * linking to the QPropertyBindingPrivate* + instead of the QPropertyBindingPrivate* itself + */ +struct QBindingObserverPtr +{ +private: + QPropertyObserver *d = nullptr; +public: + QBindingObserverPtr() = default; + Q_DISABLE_COPY(QBindingObserverPtr); + void swap(QBindingObserverPtr &other) noexcept + { qt_ptr_swap(d, other.d); } + QBindingObserverPtr(QBindingObserverPtr &&other) noexcept : d(std::exchange(other.d, nullptr)) {} + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QBindingObserverPtr); + + + inline QBindingObserverPtr(QPropertyObserver *observer) noexcept; + inline ~QBindingObserverPtr(); + inline QPropertyBindingPrivate *binding() const noexcept; + inline QPropertyObserver *operator ->(); +}; + +using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>; + // Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and // we need to allow the compiler to inline where it makes sense. @@ -52,6 +83,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; @@ -62,11 +94,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 @@ -92,33 +125,55 @@ 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}; + void notify(QUntypedPropertyData *propertyDataPtr); #ifndef QT_NO_DEBUG void noSelfDependencies(QPropertyBindingPrivate *binding); #else void noSelfDependencies(QPropertyBindingPrivate *) {} #endif - void evaluateBindings(QBindingStatus *status); + void evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status); void observeProperty(QPropertyBindingDataPointer property); explicit operator bool() const { return ptr != nullptr; } QPropertyObserverPointer nextObserver() const { return {ptr->next.data()}; } + QPropertyBindingPrivate *binding() const + { + Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding); + return ptr->binding; + } + private: void unlink_common() { @@ -151,6 +206,7 @@ struct BindingEvaluationState QPropertyBindingPrivate *binding; BindingEvaluationState *previousState = nullptr; BindingEvaluationState **currentState = nullptr; + QVarLengthArray<const QPropertyBindingData *, 8> alreadyCaptureProperties; }; /*! @@ -175,6 +231,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 @@ -321,10 +404,13 @@ public: void unlinkAndDeref(); - void evaluateRecursive(QBindingStatus *status = nullptr); - void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status); + bool evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status = nullptr); - void notifyRecursive(); + bool Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status); + + void notifyNonRecursive(const PendingBindingObserverList &bindingObservers); + enum NotificationState : bool { Delayed, Sent }; + NotificationState notifyNonRecursive(); static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding) { return static_cast<QPropertyBindingPrivate *>(binding.d.data()); } @@ -373,9 +459,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 @@ -393,6 +479,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; @@ -429,20 +526,23 @@ 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; } bool inBindingWrapper(const QBindingStorage *storage) const { - return storage->bindingStatus->currentCompatProperty + return storage->bindingStatus && storage->bindingStatus->currentCompatProperty && QtPrivate::isPropertyInBindingWrapper(this); } @@ -467,7 +567,7 @@ public: { const QBindingStorage *storage = qGetBindingStorage(owner()); // make sure we don't register this binding as a dependency to itself - if (storage->bindingStatus->currentlyEvaluatingBinding && !inBindingWrapper(storage)) + if (storage->bindingStatus && storage->bindingStatus->currentlyEvaluatingBinding && !inBindingWrapper(storage)) storage->registerDependency_helper(this); return this->val; } @@ -527,7 +627,7 @@ public: return true; } -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC template <typename Functor> QPropertyBinding<T> setBinding(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION, @@ -566,11 +666,14 @@ public: QPropertyBindingDataPointer d{bd}; if (QPropertyObserverPointer observer = d.firstObserver()) { if (!inBindingWrapper(storage)) { - if (bd->notifyObserver_helper(this, observer, storage) + PendingBindingObserverList bindingObservers; + if (bd->notifyObserver_helper(this, storage, observer, bindingObservers) == QtPrivate::QPropertyBindingData::Evaluated) { // evaluateBindings() can trash the observers. We need to re-fetch here. if (QPropertyObserverPointer observer = d.firstObserver()) observer.notify(this); + for (auto&& bindingObserver: bindingObservers) + bindingObserver.binding()->notifyNonRecursive(); } } } @@ -727,13 +830,13 @@ struct QUntypedBindablePrivate } }; -inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *status) +inline bool QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status) { if (updating) { error = QPropertyBindingError(QPropertyBindingError::BindingLoop); if (isQQmlPropertyBinding) errorCallBack(this); - return; + return false; } /* @@ -763,12 +866,21 @@ inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *st // If there was not, we must not clear it, as that only should happen in notifyRecursive pendingNotify = pendingNotify || changed; if (!changed || !firstObserver) - return; + return changed; firstObserver.noSelfDependencies(this); - firstObserver.evaluateBindings(status); + firstObserver.evaluateBindings(bindingObservers, status); + return true; } +/*! + \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); @@ -807,18 +919,16 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP break; } case QPropertyObserver::ObserverNotifiesBinding: - { - 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; @@ -831,6 +941,61 @@ inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector() d.unlink_fast(); } +QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept : d(observer) +{ + Q_ASSERT(d); + QPropertyObserverPointer{d}.binding()->addRef(); +} + +QBindingObserverPtr::~QBindingObserverPtr() +{ + if (!d) + return; + + QPropertyBindingPrivate *bindingPrivate = binding(); + if (!bindingPrivate->deref()) + QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate); +} + +QPropertyBindingPrivate *QBindingObserverPtr::binding() const noexcept { return QPropertyObserverPointer{d}.binding(); } + +QPropertyObserver *QBindingObserverPtr::operator->() { return d; } + +namespace QtPrivate { +class QPropertyAdaptorSlotObject : public QUntypedPropertyData, public QSlotObjectBase +{ + QPropertyBindingData bindingData_; + 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); + +public: + static QPropertyAdaptorSlotObject *cast(QSlotObjectBase *ptr, int propertyIndex) + { + if (ptr->isImpl(&QPropertyAdaptorSlotObject::impl)) { + auto p = static_cast<QPropertyAdaptorSlotObject *>(ptr); + if (p->metaProperty_.propertyIndex() == propertyIndex) + return p; + } + return nullptr; + } + + inline const QPropertyBindingData &bindingData() const { return bindingData_; } + inline QPropertyBindingData &bindingData() { return bindingData_; } + inline QObject *object() const { return obj; } + inline const QMetaProperty &metaProperty() const { return metaProperty_; } + + friend class QT_PREPEND_NAMESPACE(QUntypedBindable); +}; +} + QT_END_NAMESPACE #endif // QPROPERTY_P_H |