aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2025-02-27 15:10:06 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2025-09-03 16:33:14 +0000
commit2f76b2f140b2cfe95b287a2742f44d24a46f75ad (patch)
tree189c5a44233e7dbcce4414c91860c6338e361221
parent6efdecd3612bd4a5688ac1b701590413888b3d8e (diff)
Binding: Avoid inconsistent state transition
When a QML element gets destroyed, it will at some point call QQmlData::destroyed, which then sets addedToObject to false for all bindings. It will also decrement the reference count of the bindings. However, that doesn't necessarily result in the destruction of the bindings, something else might still be referencing them. Moerover, we also didn't disable the bindigs at this point. That means that the bindings would still evaluate, even after the object has been destroyed – and if the object is compleltely gone, QQmlData::wasDeleted as used in QQmlBinding::update won't save us as we only have a pointer to freed memory. We avoid this whole mess by ensuring that a binding can't be in a state where it's not attached to an object, but enabled. This is based on changes from bbf2c03226c330b934294c5e3332e341deb0b78d "Add sticky bit to QQmlAbstractBinding", and therefore the actualy change only needs to be added to older branches. We cherry-pick to dev to get the test together with the context from the commit message. Fixes: QTBUG-139306 Change-Id: I70d063a85c005afa9795d55095e5e834c88c1985 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> (cherry picked from commit 55ebad523db84b132b1b95d4e37881fd44068a52) (cherry picked from commit 1e302b9d1288022d9059b9bbc9d4fec0ef362109) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--tests/auto/qml/qqmlecmascript/data/bindingNoEvaluationIfObjectGone.qml9
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp22
2 files changed, 31 insertions, 0 deletions
diff --git a/tests/auto/qml/qqmlecmascript/data/bindingNoEvaluationIfObjectGone.qml b/tests/auto/qml/qqmlecmascript/data/bindingNoEvaluationIfObjectGone.qml
new file mode 100644
index 0000000000..488d1d248a
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/bindingNoEvaluationIfObjectGone.qml
@@ -0,0 +1,9 @@
+import QtQml
+
+QtObject {
+ id: root
+ property int x: 42
+ property QtObject obj: QtObject {
+ property int y: root.x
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 90fec70ab8..189d491640 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -132,6 +132,7 @@ private slots:
void exceptionClearsOnReeval();
void exceptionSlotProducesWarning();
void exceptionBindingProducesWarning();
+ void bindingNoEvaluationIfObjectGone();
void compileInvalidBinding();
void transientErrors();
void shutdownErrors();
@@ -2535,6 +2536,27 @@ void tst_qqmlecmascript::exceptionBindingProducesWarning()
QVERIFY(object != nullptr);
}
+void tst_qqmlecmascript::bindingNoEvaluationIfObjectGone()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("bindingNoEvaluationIfObjectGone.qml"));
+ std::unique_ptr<QObject> root(component.create());
+ QVERIFY(root);
+ QObject *targetObject = root->property("obj").value<QObject *>();
+ QVERIFY(targetObject);
+ QQmlProperty yProp(targetObject, u"y"_s, &engine);
+ // hold a reference to the binding to keep it alive
+ auto binding = QQmlAnyBinding::ofProperty(yProp);
+ QVERIFY(binding.asAbstractBinding());
+ QCOMPARE(binding.asAbstractBinding()->targetObject(), targetObject);
+ delete targetObject;
+ // the target object pointer of the binding hasn't been reset; we might
+ // want to change this in the future; that line can be then adapted
+ QVERIFY(binding.asAbstractBinding()->targetObject());
+ // trigger a binding reevaluation
+ root->setProperty("x", 10); // no ASAN warning/crash
+}
+
void tst_qqmlecmascript::compileInvalidBinding()
{
// QTBUG-23387: ensure that invalid bindings don't cause a crash.