diff options
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 10 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator_p.h | 4 | ||||
-rw-r--r-- | src/qml/types/qqmlbind.cpp | 101 | ||||
-rw-r--r-- | tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml | 18 | ||||
-rw-r--r-- | tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp | 13 |
5 files changed, 121 insertions, 25 deletions
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index ea31ff0b56..42bac5d1d1 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -290,6 +290,16 @@ void QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty, } } +void QQmlObjectCreator::populateDeferredInstance( + QObject *outerObject, int deferredIndex, int index, QObject *instance, + QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty, + const QV4::CompiledData::Binding *binding) +{ + doPopulateDeferred(outerObject, deferredIndex, [&]() { + populateInstance(index, instance, bindingTarget, valueTypeProperty, binding); + }); +} + void QQmlObjectCreator::finalizePopulateDeferred() { phase = ObjectsCreated; diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 0a2f1ecda9..0e73feeac4 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -120,6 +120,10 @@ public: void beginPopulateDeferred(const QQmlRefPointer<QQmlContextData> &context); void populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex, const QV4::CompiledData::Binding *binding); + void populateDeferredInstance(QObject *outerObject, int deferredIndex, + int index, QObject *instance, QObject *bindingTarget, + const QQmlPropertyData *valueTypeProperty, + const QV4::CompiledData::Binding *binding = nullptr); void finalizePopulateDeferred(); bool finalize(QQmlInstantiationInterrupt &interrupt); diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 8bfe9d4c4e..cdca6e89d4 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -3,28 +3,29 @@ #include "qqmlbind_p.h" -#include <private/qqmlnullablevalue_p.h> -#include <private/qqmlproperty_p.h> +#include <private/qqmlanybinding_p.h> #include <private/qqmlbinding_p.h> +#include <private/qqmlcomponent_p.h> #include <private/qqmlmetatype_p.h> +#include <private/qqmlnullablevalue_p.h> +#include <private/qqmlproperty_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qv4persistent_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4resolvedtypereference_p.h> -#include <qqmlengine.h> -#include <qqmlcontext.h> -#include <qqmlproperty.h> -#include <qqmlpropertymap.h> -#include <qqmlinfo.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlinfo.h> +#include <QtQml/qqmlproperty.h> +#include <QtQml/qqmlpropertymap.h> + +#include <QtCore/private/qobject_p.h> -#include <QtCore/qfile.h> #include <QtCore/qdebug.h> -#include <QtCore/qtimer.h> +#include <QtCore/qfile.h> #include <QtCore/qloggingcategory.h> -#include <private/qqmlanybinding_p.h> -#include <private/qv4qmlcontext_p.h> -#include <private/qqmlcomponent_p.h> - -#include <private/qobject_p.h> +#include <QtCore/qtimer.h> QT_BEGIN_NAMESPACE @@ -736,6 +737,20 @@ static QQmlAnyBinding createBinding( return QQmlAnyBinding(); } +static void initCreator( + QQmlData::DeferredData *deferredData, + const QQmlRefPointer<QQmlContextData> &contextData, + QQmlComponentPrivate::ConstructionState *immediateState) +{ + if (!immediateState->hasCreator()) { + immediateState->setCompletePending(true); + immediateState->initCreator( + deferredData->context->parent(), deferredData->compilationUnit, + contextData); + immediateState->creator()->beginPopulateDeferred(deferredData->context); + } +} + void QQmlBindPrivate::decodeBinding( QQmlBind *q, const QString &propertyPrefix, QQmlData::DeferredData *deferredData, @@ -744,12 +759,54 @@ void QQmlBindPrivate::decodeBinding( { const QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit = deferredData->compilationUnit; - const QString propertyName = propertyPrefix - + compilationUnit->stringAt(binding->propertyNameIndex); + const QString propertySuffix = compilationUnit->stringAt(binding->propertyNameIndex); + const QString propertyName = propertyPrefix + propertySuffix; switch (binding->type()) { - case QV4::CompiledData::Binding::Type_GroupProperty: - case QV4::CompiledData::Binding::Type_AttachedProperty: { + case QV4::CompiledData::Binding::Type_AttachedProperty: + if (propertyPrefix.isEmpty()) { + // Top-level attached properties cannot be generalized grouped properties. + // Treat them as regular properties. + // ... unless we're not supposed to handle regular properties. Then ignore them. + if (!immediateState) + return; + + Q_ASSERT(compilationUnit->stringAt(compilationUnit->objectAt(binding->value.objectIndex) + ->inheritedTypeNameIndex).isEmpty()); + + const QV4::ResolvedTypeReference *typeReference + = compilationUnit->resolvedType(binding->propertyNameIndex); + Q_ASSERT(typeReference); + QQmlType attachedType = typeReference->type(); + if (!attachedType.isValid()) { + const QQmlTypeNameCache::Result result + = deferredData->context->imports()->query(propertySuffix); + if (!result.isValid()) { + qmlWarning(q).nospace() + << "Unknown name " << propertySuffix << ". The binding is ignored."; + return; + } + attachedType = result.type; + } + + QQmlContext *context = qmlContext(q); + QObject *attachedObject = qmlAttachedPropertiesObject( + q, attachedType.attachedPropertiesFunction( + QQmlEnginePrivate::get(context->engine()))); + if (!attachedObject) { + qmlWarning(q).nospace() <<"Could not create attached properties object '" + << attachedType.typeName() << "'"; + return; + } + + initCreator(deferredData, QQmlContextData::get(context), immediateState); + immediateState->creator()->populateDeferredInstance( + q, deferredData->deferredIdx, binding->value.objectIndex, attachedObject, + attachedObject, /*value type property*/ nullptr, binding); + return; + } + Q_FALLTHROUGH(); + case QV4::CompiledData::Binding::Type_GroupProperty: { const QString pre = propertyName + u'.'; const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex); @@ -776,13 +833,7 @@ void QQmlBindPrivate::decodeBinding( QQmlProperty property = QQmlPropertyPrivate::create( q, propertyName, contextData, QQmlPropertyPrivate::InitFlag::AllowSignal); if (property.isValid()) { - if (!immediateState->hasCreator()) { - immediateState->setCompletePending(true); - immediateState->initCreator( - deferredData->context->parent(), deferredData->compilationUnit, - contextData); - immediateState->creator()->beginPopulateDeferred(deferredData->context); - } + initCreator(deferredData, contextData, immediateState); immediateState->creator()->populateDeferredBinding( property, deferredData->deferredIdx, binding); } else { diff --git a/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml b/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml new file mode 100644 index 0000000000..98b3aa6606 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/propertiesAttachedToBindingItself.qml @@ -0,0 +1,18 @@ +import QtQml.Models +import QtQuick + +Instantiator { + id: inst + model: 1 + property int check: 0 + + delegate: Binding { + ListView.delayRemove: true + Component.onCompleted: inst.check += 1 + } + + Component.onCompleted: { + if (inst.objectAt(0)) + inst.check += 2 + } +} diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index 237a92f070..fbb805d86a 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -39,6 +39,7 @@ private slots: void generalizedGroupedProperties(); void localSignalHandler(); void whenEvaluatedEarlyEnough(); + void propertiesAttachedToBindingItself(); private: QQmlEngine engine; @@ -616,6 +617,18 @@ void tst_qqmlbinding::whenEvaluatedEarlyEnough() root->setProperty("forceEnable", true); } +void tst_qqmlbinding::propertiesAttachedToBindingItself() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("propertiesAttachedToBindingItself.qml")); + QTest::failOnWarning(QRegularExpression(".*")); + std::unique_ptr<QObject> root { c.create() }; + QVERIFY2(root, qPrintable(c.errorString())); + // 0 => everything broken; 1 => normal attached properties broken; + // 2 => Component.onCompleted broken, 3 => everything works + QTRY_COMPARE(root->property("check").toInt(), 3); +} + QTEST_MAIN(tst_qqmlbinding) #include "tst_qqmlbinding.moc" |