QtQml: Do not evaluate bindings in invalid contexts

If the context is gone, we are technically still able to evaluate
QProperty bindings, but we really shouldn't.

Pick-to: 6.10 6.9 6.8 6.5
Fixes: QTBUG-137270
Change-Id: I323c26d59788e4b318ec4733e1593e761beb4b6f
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
This commit is contained in:
Ulf Hermann 2025-06-24 09:25:54 +02:00
parent 270bd4dc05
commit ad6d2bc496
3 changed files with 56 additions and 0 deletions

View File

@ -133,6 +133,9 @@ QUntypedPropertyBinding QQmlPropertyBinding::createFromBoundFunction(const QQmlP
void QQmlPropertyBindingJS::expressionChanged()
{
if (!hasValidContext())
return;
auto binding = asBinding();
if (!binding->propertyDataPtr)
return;

View File

@ -0,0 +1,18 @@
import QtQml
QtObject {
id: outer
objectName: "outer"
property Component c: QtObject {
id: inner1
objectName: inner2.objectName + "a"
}
property QtObject inner1: c.createObject()
property QtObject inner2: QtObject {
id: inner2
objectName: "a"
}
}

View File

@ -41,6 +41,7 @@ private slots:
void disabledOnReadonlyProperty();
void delayed();
void bindingOverwriting();
void bindingInDeadContext();
void bindToQmlComponent();
void bindingDoesNoWeirdConversion();
void bindNaNToInt();
@ -500,6 +501,40 @@ void tst_qqmlbinding::bindingOverwriting()
QLoggingCategory::setFilterRules(QString());
}
void tst_qqmlbinding::bindingInDeadContext()
{
// We manually control the deletion order of the objects here.
// This is what some of our views also do. One way to prevent
// the engine from deleting objects is to parent them to the
// application.
QScopedPointer<QObject> o;
QScopedPointer<QObject> inner1;
{
QScopedPointer<QObject> inner2;
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("bindingInDeadContext.qml"));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
o.reset(c.create());
QVERIFY(!o.isNull());
o->setParent(QCoreApplication::instance());
inner1.reset(o->property("inner1").value<QObject *>());
QVERIFY(inner1);
inner1->setParent(QCoreApplication::instance());
inner2.reset(o->property("inner2").value<QObject *>());
QVERIFY(inner2);
inner2->setParent(QCoreApplication::instance());
}
// The objectName binding did not get re-evaluated when inner2 died
// because the engine was gone already.
QCOMPARE(inner1->objectName(), "aa");
}
void tst_qqmlbinding::bindToQmlComponent()
{
QQmlEngine engine;