diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index 97854f8ac3..0055e6a5ff 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -285,44 +285,7 @@ void QPropertyBindingPrivate::unlinkAndDeref() void QPropertyBindingPrivate::evaluateRecursive(QBindingStatus *status) { - if (updating) { - error = QPropertyBindingError(QPropertyBindingError::BindingLoop); - if (isQQmlPropertyBinding) - errorCallBack(this); - return; - } - - QScopedValueRollback updateGuard(updating, true); - - /* - * Evaluating the binding might lead to the binding being broken. This can - * cause ref to reach zero at the end of the function. However, the - * updateGuard's destructor will then still trigger, trying to set the - * updating bool to its old value - * To prevent this, we create a QPropertyBindingPrivatePtr which ensures - * that the object is still alive when updateGuard's dtor runs. - */ - QPropertyBindingPrivatePtr keepAlive {this}; - - BindingEvaluationState evaluationFrame(this, status); - - auto bindingFunctor = reinterpret_cast(this) + - QPropertyBindingPrivate::getSizeEnsuringAlignment(); - bool changed = false; - if (hasBindingWrapper) { - changed = staticBindingWrapper(metaType, propertyDataPtr, - {vtable, bindingFunctor}); - } else { - changed = vtable->call(metaType, propertyDataPtr, bindingFunctor); - } - // If there was a change, we must set pendingNotify. - // If there was not, we must not clear it, as that only should happen in notifyRecursive - pendingNotify = pendingNotify || changed; - if (!changed || !firstObserver) - return; - - firstObserver.noSelfDependencies(this); - firstObserver.evaluateBindings(status); + return evaluateRecursive_inline(status); } void QPropertyBindingPrivate::notifyRecursive() @@ -539,7 +502,8 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding QPropertyBindingDataPointer d{this}; QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver(); - dependencyObserver.setBindingToNotify(currentState->binding); + Q_ASSERT(QPropertyObserver::ObserverNotifiesBinding == 0); + dependencyObserver.setBindingToNotify_unsafe(currentState->binding); d.addObserver(dependencyObserver.ptr); } @@ -663,6 +627,16 @@ void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *bindi ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding); } +/*! + \internal + The same as as setBindingToNotify, but assumes that the tag is already correct. + */ +void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding) +{ + Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding); + ptr->binding = binding; +} + /*! \internal QPropertyObserverNodeProtector is a RAII wrapper which takes care of the internal switching logic @@ -775,7 +749,7 @@ void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status) if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) { auto bindingToEvaluate = observer->binding; QPropertyObserverNodeProtector protector(observer); - bindingToEvaluate->evaluateRecursive(status); + bindingToEvaluate->evaluateRecursive_inline(status); next = protector.next(); } diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index 96e89926db..f1b6ef6aa2 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -55,6 +55,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -104,6 +105,7 @@ struct QPropertyObserverPointer void unlink(); void setBindingToNotify(QPropertyBindingPrivate *binding); + void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding); void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler); void notify(QUntypedPropertyData *propertyDataPtr); @@ -325,6 +327,8 @@ public: void unlinkAndDeref(); void evaluateRecursive(QBindingStatus *status = nullptr); + void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status = nullptr); + void notifyRecursive(); static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding) @@ -693,6 +697,48 @@ struct QUntypedBindablePrivate } }; +inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *status) +{ + if (updating) { + error = QPropertyBindingError(QPropertyBindingError::BindingLoop); + if (isQQmlPropertyBinding) + errorCallBack(this); + return; + } + + QScopedValueRollback updateGuard(updating, true); + + /* + * Evaluating the binding might lead to the binding being broken. This can + * cause ref to reach zero at the end of the function. However, the + * updateGuard's destructor will then still trigger, trying to set the + * updating bool to its old value + * To prevent this, we create a QPropertyBindingPrivatePtr which ensures + * that the object is still alive when updateGuard's dtor runs. + */ + QPropertyBindingPrivatePtr keepAlive {this}; + + QtPrivate::BindingEvaluationState evaluationFrame(this, status); + + auto bindingFunctor = reinterpret_cast(this) + + QPropertyBindingPrivate::getSizeEnsuringAlignment(); + bool changed = false; + if (hasBindingWrapper) { + changed = staticBindingWrapper(metaType, propertyDataPtr, + {vtable, bindingFunctor}); + } else { + changed = vtable->call(metaType, propertyDataPtr, bindingFunctor); + } + // If there was a change, we must set pendingNotify. + // If there was not, we must not clear it, as that only should happen in notifyRecursive + pendingNotify = pendingNotify || changed; + if (!changed || !firstObserver) + return; + + firstObserver.noSelfDependencies(this); + firstObserver.evaluateBindings(status); +} + QT_END_NAMESPACE #endif // QPROPERTY_P_H