Fix proxy-data handling
This addresses two different issues: - Firstly, we were casting the resolved binding data pointer to QPropertyProxyBindingData, instead of the d_ptr of QPropertyBindingData. Fix this by introducing a helper function, and consistently using it to access the proxy data. - Secondly, we were not resetting the originalBindingData when the pointed to object was destoyed. Fix that, too. Pick-to: 6.5 6.4 6.2 Task-number: QTBUG-110899 Change-Id: I7691c9df5cc26e761f6b0e5f16d152f7f2183208 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
efce30bb43
commit
55ca636180
@ -446,6 +446,8 @@ QMetaType QUntypedPropertyBinding::valueMetaType() const
|
||||
QPropertyBindingData::~QPropertyBindingData()
|
||||
{
|
||||
QPropertyBindingDataPointer d{this};
|
||||
if (isNotificationDelayed())
|
||||
proxyData()->originalBindingData = nullptr;
|
||||
for (auto observer = d.firstObserver(); observer;) {
|
||||
auto next = observer.nextObserver();
|
||||
observer.unlink();
|
||||
|
@ -83,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;
|
||||
|
||||
@ -418,9 +419,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 +439,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;
|
||||
|
@ -303,10 +303,15 @@ private:
|
||||
{
|
||||
quintptr &d = d_ptr;
|
||||
if (isNotificationDelayed())
|
||||
return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit))->d_ptr;
|
||||
return proxyData()->d_ptr;
|
||||
return d;
|
||||
}
|
||||
quintptr d() const { return d_ref(); }
|
||||
QPropertyProxyBindingData *proxyData() const
|
||||
{
|
||||
Q_ASSERT(isNotificationDelayed());
|
||||
return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit));
|
||||
}
|
||||
void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
|
||||
void removeBinding_helper();
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <qtest.h>
|
||||
#include <qproperty.h>
|
||||
#include <private/qproperty_p.h>
|
||||
#include <private/qobject_p.h>
|
||||
|
||||
#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_QDOC)
|
||||
#include <source_location>
|
||||
@ -87,6 +88,7 @@ private slots:
|
||||
void noDoubleNotification();
|
||||
void groupedNotifications();
|
||||
void groupedNotificationConsistency();
|
||||
void bindingGroupMovingBindingData();
|
||||
void uninstalledBindingDoesNotEvaluate();
|
||||
|
||||
void notify();
|
||||
@ -2132,6 +2134,38 @@ void tst_QProperty::groupedNotificationConsistency()
|
||||
QVERIFY(areEqual); // value changed runs after everything has been evaluated
|
||||
}
|
||||
|
||||
void tst_QProperty::bindingGroupMovingBindingData()
|
||||
{
|
||||
auto tester = std::make_unique<ClassWithNotifiedProperty>();
|
||||
auto testerPriv = QObjectPrivate::get(tester.get());
|
||||
|
||||
auto dummyNotifier = tester->property.addNotifier([](){});
|
||||
auto bindingData = testerPriv->bindingStorage.bindingData(&tester->property);
|
||||
QVERIFY(bindingData); // we have a notifier, so there should be binding data
|
||||
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
auto cleanup = qScopeGuard([](){ Qt::endPropertyUpdateGroup(); });
|
||||
tester->property = 42;
|
||||
QCOMPARE(testerPriv->bindingStorage.bindingData(&tester->property), bindingData);
|
||||
auto proxyData = QPropertyBindingDataPointer::proxyData(bindingData);
|
||||
// as we've modified the property, we now should have a proxy for the delayed notification
|
||||
QVERIFY(proxyData);
|
||||
// trigger binding data reallocation
|
||||
std::array<QUntypedPropertyData, 10> propertyDataArray;
|
||||
for (auto&& data: propertyDataArray)
|
||||
testerPriv->bindingStorage.bindingData(&data, true);
|
||||
// binding data has moved
|
||||
QVERIFY(testerPriv->bindingStorage.bindingData(&tester->property) != bindingData);
|
||||
bindingData = testerPriv->bindingStorage.bindingData(&tester->property);
|
||||
// the proxy data has been updated
|
||||
QCOMPARE(proxyData->originalBindingData, bindingData);
|
||||
|
||||
tester.reset();
|
||||
// the property data is gone, proxyData should have been informed
|
||||
QCOMPARE(proxyData->originalBindingData, nullptr);
|
||||
QVERIFY(proxyData);
|
||||
}
|
||||
|
||||
void tst_QProperty::uninstalledBindingDoesNotEvaluate()
|
||||
{
|
||||
QProperty<int> i;
|
||||
|
Loading…
Reference in New Issue
Block a user