QML: Before processing deep aliases, remove pending bindings

We may have additional bindings scheduled for the deep alias, but those
are to be overridden by the alias. They should therefore be removed like
bindings to target properties of shallow aliases.

For QProperty bindings we have to apply a separate trick and set and
clear the binding bit in the right places. We don't have access to the
actual binding when writing the value, after all.

Pick-to: 6.2
Fixes: QTBUG-115579
Change-Id: Ia915e59905d7e3185a17c5b6613926264ad9bc6b
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 0fdf9042ce)
(cherry picked from commit c07f63d064)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Ulf Hermann 2023-11-06 16:23:35 +01:00
parent dc4bd4fb3f
commit 11045230c3
5 changed files with 41 additions and 17 deletions

View File

@ -913,10 +913,13 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
&& !(bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver) && !(bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
&& !_valueTypeProperty; && !_valueTypeProperty;
if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && allowedToRemoveBinding) { if (allowedToRemoveBinding) {
QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex())); if (bindingProperty->isBindable()) {
} else if (bindingProperty->isBindable() && allowedToRemoveBinding) { removePendingBinding(_bindingTarget, bindingProperty->coreIndex());
removePendingBinding(_bindingTarget, bindingProperty->coreIndex()); } else {
QQmlPropertyPrivate::removeBinding(
_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
}
} }
if (bindingType == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) { if (bindingType == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
@ -962,6 +965,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
qmlBinding = QQmlPropertyBinding::create(bindingProperty, runtimeFunction, _scopeObject, context, currentQmlContext(), _bindingTarget, index); qmlBinding = QQmlPropertyBinding::create(bindingProperty, runtimeFunction, _scopeObject, context, currentQmlContext(), _bindingTarget, index);
} }
sharedState.data()->allQPropertyBindings.push_back(DeferredQPropertyBinding {_bindingTarget, bindingProperty->coreIndex(), qmlBinding }); sharedState.data()->allQPropertyBindings.push_back(DeferredQPropertyBinding {_bindingTarget, bindingProperty->coreIndex(), qmlBinding });
QQmlData *data = QQmlData::get(_bindingTarget, true);
data->setBindingBit(_bindingTarget, bindingProperty->coreIndex());
} else { } else {
// When writing bindings to grouped properties implemented as value types, // When writing bindings to grouped properties implemented as value types,
// such as point.x: { someExpression; }, then the binding is installed on // such as point.x: { someExpression; }, then the binding is installed on
@ -1458,6 +1464,14 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt)
while (!sharedState->allQPropertyBindings.isEmpty()) { while (!sharedState->allQPropertyBindings.isEmpty()) {
auto& [target, index, qmlBinding] = sharedState->allQPropertyBindings.first(); auto& [target, index, qmlBinding] = sharedState->allQPropertyBindings.first();
QQmlData *data = QQmlData::get(target);
if (!data || !data->hasBindingBit(index)) {
// The target property has been overwritten since we stashed the binding.
sharedState->allQPropertyBindings.pop_front();
continue;
}
QUntypedBindable bindable; QUntypedBindable bindable;
void *argv[] = { &bindable }; void *argv[] = { &bindable };
// allow interception // allow interception

View File

@ -1031,15 +1031,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
int coreIndex = encodedIndex.coreIndex(); int coreIndex = encodedIndex.coreIndex();
const int valueTypePropertyIndex = encodedIndex.valueTypeIndex(); const int valueTypePropertyIndex = encodedIndex.valueTypeIndex();
// Remove binding (if any) on write const auto removePendingBinding
if(c == QMetaObject::WriteProperty) { = [c, a](QObject *target, int coreIndex, QQmlPropertyIndex encodedIndex) {
int flags = *reinterpret_cast<int*>(a[3]); // Remove binding (if any) on write
if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) { if (c == QMetaObject::WriteProperty) {
QQmlData *targetData = QQmlData::get(target); int flags = *reinterpret_cast<int*>(a[3]);
if (targetData && targetData->hasBindingBit(coreIndex)) if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) {
QQmlPropertyPrivate::removeBinding(target, encodedIndex); QQmlData *targetData = QQmlData::get(target);
if (targetData && targetData->hasBindingBit(coreIndex)) {
QQmlPropertyPrivate::removeBinding(target, encodedIndex);
targetData->clearBindingBit(coreIndex);
}
}
} }
} };
if (valueTypePropertyIndex != -1) { if (valueTypePropertyIndex != -1) {
if (!targetDData->propertyCache) if (!targetDData->propertyCache)
@ -1049,6 +1054,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
ctxt->engine(), pd->propType()); ctxt->engine(), pd->propType());
if (valueType) { if (valueType) {
removePendingBinding(target, coreIndex, encodedIndex);
valueType->read(target, coreIndex); valueType->read(target, coreIndex);
int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
@ -1061,10 +1067,14 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
// deep alias // deep alias
void *argv[1] = { &target }; void *argv[1] = { &target };
QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
removePendingBinding(
target, valueTypePropertyIndex,
QQmlPropertyIndex(valueTypePropertyIndex));
return QMetaObject::metacall(target, c, valueTypePropertyIndex, a); return QMetaObject::metacall(target, c, valueTypePropertyIndex, a);
} }
} else { } else {
removePendingBinding(target, coreIndex, encodedIndex);
return QMetaObject::metacall(target, c, coreIndex, a); return QMetaObject::metacall(target, c, coreIndex, a);
} }

View File

@ -2,6 +2,7 @@ import QtQml
QtObject { QtObject {
id: root id: root
objectName: "theRoot"
component ObjectWithColor: QtObject { component ObjectWithColor: QtObject {
property string color property string color
@ -10,12 +11,14 @@ QtObject {
property ObjectWithColor border: ObjectWithColor { property ObjectWithColor border: ObjectWithColor {
id: border id: border
objectName: root.objectName
color: root.trueBorderColor color: root.trueBorderColor
varvar: root.trueBorderVarvar varvar: root.trueBorderVarvar
} }
readonly property rect readonlyRect: ({x: 12, y: 13, width: 14, height: 15}) readonly property rect readonlyRect: ({x: 12, y: 13, width: 14, height: 15})
property alias borderObjectName: border.objectName
property alias borderColor: border.color property alias borderColor: border.color
property alias borderVarvar: border.varvar property alias borderVarvar: border.varvar
property alias readonlyRectX: root.readonlyRect.x property alias readonlyRectX: root.readonlyRect.x

View File

@ -1,6 +1,7 @@
import QtQml import QtQml
DeepAliasOnIC { DeepAliasOnIC {
borderObjectName: "theLeaf"
borderColor: "black" borderColor: "black"
borderVarvar: "mauve" borderVarvar: "mauve"
} }

View File

@ -8125,15 +8125,11 @@ void tst_qqmllanguage::deepAliasOnICOrReadonly()
QScopedPointer<QObject> o(c.create()); QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull()); QVERIFY(!o.isNull());
// We are mostly testing that it doesn't crash here. The actual bug is fixed separately.
QEXPECT_FAIL("", "QTBUG-115579 is not fixed yet", Continue);
QCOMPARE(o->property("borderColor").toString(), QLatin1String("black")); QCOMPARE(o->property("borderColor").toString(), QLatin1String("black"));
QCOMPARE(o->property("borderObjectName").toString(), QLatin1String("theLeaf"));
const QVariant var = o->property("borderVarvar"); const QVariant var = o->property("borderVarvar");
QEXPECT_FAIL("", "QTBUG-115579 is not fixed yet", Continue);
QCOMPARE(var.metaType(), QMetaType::fromType<QString>()); QCOMPARE(var.metaType(), QMetaType::fromType<QString>());
QEXPECT_FAIL("", "QTBUG-115579 is not fixed yet", Continue);
QCOMPARE(var.toString(), QLatin1String("mauve")); QCOMPARE(var.toString(), QLatin1String("mauve"));
QQmlComponent c2(&engine, testFileUrl("deepAliasOnReadonly.qml")); QQmlComponent c2(&engine, testFileUrl("deepAliasOnReadonly.qml"));