From a243f1eeeb9eda237ea2f6dad403984ab14aa375 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 22 Sep 2020 10:48:23 +0200 Subject: Fix QProperty property interaction with aliases With this change, an alias of a bindable property is also bindable, and shares its bindable interface with the target. Moreover, the logic in QQmlTypeCompiler is adjusted so that a change handler of an alias uses the bindable interface if possible, instead of connecting to the alias' change signal. That would never be emitted if the target is a QProperty without a notify signal. Alias properties still have a change signal, but those never get emitted. Change-Id: I857dfdbe51048a2b604ad632982e7f4adac6b907 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlobjectcreator.cpp | 16 +++++++++++++++- src/qml/qml/qqmlpropertycache.cpp | 1 + src/qml/qml/qqmlpropertycachecreator_p.h | 5 ++++- src/qml/qml/qqmltypecompiler.cpp | 5 +++-- src/qml/qml/qqmlvmemetaobject.cpp | 2 +- 5 files changed, 24 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 45c9400934..43c639ce8d 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -890,10 +890,24 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper _scopeObject, runtimeFunction, currentQmlContext()); if (bindingProperty->isBindable()) { + auto target = _bindingTarget; + if (bindingProperty->isAlias()) { + // If the property is an alias, we cannot obtain the bindable interface directly with qt_metacall + // so instead, we resolve the alias to obtain the actual target + // This should be faster than doing a detour through the metaobject of the target, and relying on + // QMetaObject::metacall doing the correct resolution + QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); + QQmlPropertyIndex propIndex; + QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); + QQmlData *data = QQmlData::get(target); + Q_ASSERT(data && data->propertyCache); + bindingProperty = data->propertyCache->property(propIndex.coreIndex()); + } auto &observer = QQmlData::get(_scopeObject)->propertyObservers.emplace_back(expr); QUntypedBindable bindable; void *argv[] = { &bindable }; - _bindingTarget->qt_metacall(QMetaObject::BindableProperty, bindingProperty->coreIndex(), argv); + target->qt_metacall(QMetaObject::BindableProperty, bindingProperty->coreIndex(), argv); + Q_ASSERT(bindable.isValid()); bindable.observe(&observer); } else { QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 3c6c0c82de..4f557527c9 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -1147,6 +1147,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) property.setReadable(true); property.setWritable(data->isWritable()); property.setResettable(data->isResettable()); + property.setBindable(data->isBindable()); } for (int ii = 0; ii < methods.count(); ++ii) { diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 626b274c2f..21551db5c4 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -785,6 +785,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator::propertyDataFor *type = 0; bool writable = false; bool resettable = false; + bool bindable = false; propertyFlags->setIsAlias(true); @@ -861,13 +862,14 @@ inline QQmlError QQmlPropertyCacheAliasCreator::propertyDataFor *type = targetProperty->propType(); writable = targetProperty->isWritable(); resettable = targetProperty->isResettable(); - + bindable = targetProperty->isBindable(); } else { // value type or primitive type or enum *type = targetProperty->propType(); writable = targetProperty->isWritable(); resettable = targetProperty->isResettable(); + bindable = targetProperty->isBindable(); if (valueTypeIndex != -1) { const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); @@ -891,6 +893,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator::propertyDataFor propertyFlags->setIsWritable(!(alias.flags & QV4::CompiledData::Alias::IsReadOnly) && writable); propertyFlags->setIsResettable(resettable); + propertyFlags->setIsBindable(bindable); return QQmlError(); } diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index ca8a24439b..eb3b0e43ac 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -382,7 +382,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio QString finalSignalHandlerPropertyName = signalNameCandidate; uint flags = QV4::CompiledData::Binding::IsSignalHandlerExpression; - if (signal) { + const bool isPropertyObserver = !signalPropertyData && qPropertyData && qPropertyData->isBindable(); + if (signal && !(qPropertyData && qPropertyData->isAlias() && isPropertyObserver)) { int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex()); sigIndex = propertyCache->originalClone(sigIndex); @@ -400,7 +401,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } parameters += param; } - } else if (!signalPropertyData && qPropertyData && qPropertyData->isBindable()) { + } else if (isPropertyObserver) { finalSignalHandlerPropertyName = qPropertyName; flags = QV4::CompiledData::Binding::IsPropertyObserver; } else { diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 2d0f131477..6919c6dba3 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -687,7 +687,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * const int signalCount = compiledObject ? int(compiledObject->nSignals) : 0; const int methodCount = compiledObject ? int(compiledObject->nFunctions) : 0; - if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) { + if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty || c == QMetaObject::BindableProperty) { if (id >= propOffset()) { id -= propOffset(); -- cgit v1.2.3