summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qproperty.cpp2
-rw-r--r--src/corelib/kernel/qproperty_p.h18
-rw-r--r--src/corelib/kernel/qpropertyprivate.h7
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp34
4 files changed, 57 insertions, 4 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 4836b9c857..f54cd73278 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -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();
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index a7fb9e9a1a..42daca036a 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -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;
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index 4387f0fbeb..aca6d14c96 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -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();
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
index e9b2218497..335cc65b5a 100644
--- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
+++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
@@ -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;