summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qproperty.cpp8
-rw-r--r--src/corelib/kernel/qproperty_p.h11
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp32
3 files changed, 49 insertions, 2 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 72f74ac1e8..81871587e8 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -41,6 +41,7 @@
#include "qproperty_p.h"
#include <qscopedvaluerollback.h>
+#include <QScopeGuard>
QT_BEGIN_NAMESPACE
@@ -82,6 +83,13 @@ void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
if (dirty)
return;
dirty = true;
+ if (eagerlyUpdating) {
+ error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
+ return;
+ }
+
+ eagerlyUpdating = true;
+ QScopeGuard guard([&](){eagerlyUpdating = false;});
if (requiresEagerEvaluation()) {
// these are compat properties that we will need to evaluate eagerly
evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr);
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index c5c74147c1..999760ec86 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -163,10 +163,15 @@ private:
using ObserverArray = std::array<QPropertyObserver, 4>;
// QSharedData is 4 bytes. Use the padding for the bools as we need 8 byte alignment below.
+
+ // a dependent property has changed, and the binding needs to be reevaluated on access
bool dirty = false;
+ // used to detect binding loops for lazy evaluated properties
bool updating = false;
bool hasStaticObserver = false;
- bool hasBindingWrapper = false;
+ bool hasBindingWrapper:1;
+ // used to detect binding loops for eagerly evaluated properties
+ bool eagerlyUpdating:1;
QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction;
@@ -192,7 +197,9 @@ public:
QPropertyBindingPrivate(QMetaType metaType, QUntypedPropertyBinding::BindingEvaluationFunction evaluationFunction,
const QPropertyBindingSourceLocation &location)
- : evaluationFunction(std::move(evaluationFunction))
+ : hasBindingWrapper(false)
+ , eagerlyUpdating(false)
+ , evaluationFunction(std::move(evaluationFunction))
, inlineDependencyObservers() // Explicit initialization required because of union
, location(location)
, metaType(metaType)
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
index 9bc2ce26b6..0394e9ccf7 100644
--- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
+++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
@@ -514,6 +514,31 @@ void tst_QProperty::bindingError()
QCOMPARE(prop.binding().error().description(), QString("my error"));
}
+
+
+class BindingLoopTester : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int eagerProp READ eagerProp WRITE setEagerProp BINDABLE bindableEagerProp)
+ Q_PROPERTY(int eagerProp2 READ eagerProp2 WRITE setEagerProp2 BINDABLE bindableEagerProp2)
+ public:
+ BindingLoopTester(QProperty<int> *i, QObject *parent = nullptr) : QObject(parent) {
+ eagerData.setBinding(Qt::makePropertyBinding([&](){ return eagerData2.value() + i->value(); } ) );
+ eagerData2.setBinding(Qt::makePropertyBinding([&](){ return eagerData.value(); } ) );
+ i->setValue(42);
+ }
+
+ int eagerProp() {return eagerData.value();}
+ void setEagerProp(int i) { eagerData = i; }
+ QBindable<int> bindableEagerProp() {return QBindable<int>(&eagerData);}
+ Q_OBJECT_COMPAT_PROPERTY(BindingLoopTester, int, eagerData, &BindingLoopTester::setEagerProp)
+
+ int eagerProp2() {return eagerData2.value();}
+ void setEagerProp2(int i) { eagerData2 = i; }
+ QBindable<int> bindableEagerProp2() {return QBindable<int>(&eagerData2);}
+ Q_OBJECT_COMPAT_PROPERTY(BindingLoopTester, int, eagerData2, &BindingLoopTester::setEagerProp2)
+};
+
void tst_QProperty::bindingLoop()
{
QScopedPointer<QProperty<int>> firstProp;
@@ -533,6 +558,13 @@ void tst_QProperty::bindingLoop()
QCOMPARE(thirdProp.value(), 0);
QCOMPARE(secondProp.binding().error().type(), QPropertyBindingError::BindingLoop);
+
+
+ QProperty<int> i;
+ BindingLoopTester tester(&i);
+ QCOMPARE(tester.bindableEagerProp().binding().error().type(), QPropertyBindingError::BindingLoop);
+ QEXPECT_FAIL("", "Only the first property in a dependency cycle is set to the error state", Continue);
+ QCOMPARE(tester.bindableEagerProp2().binding().error().type(), QPropertyBindingError::BindingLoop);
}
void tst_QProperty::changePropertyFromWithinChangeHandler()