Allow deferring of attached properties

They should behave just like everything else. Also, realize that when
applying deferred bindings, we have to apply the deferred bindings of
any subobject, too. Otherwise we get rather confusing behavior.

Change-Id: I7a5b0630284f7a0020549de6dee903f46352d12e
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Ulf Hermann 2021-11-08 11:00:23 +01:00
parent 332099038d
commit cdd8fc015f
5 changed files with 75 additions and 49 deletions

View File

@ -285,7 +285,7 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex)
{
doPopulateDeferred(instance, deferredIndex, [this]() { setupBindings(true); });
doPopulateDeferred(instance, deferredIndex, [this]() { setupBindings(ApplyDeferred); });
}
bool QQmlObjectCreator::populateDeferredProperties(QObject *instance,
@ -625,7 +625,7 @@ static QQmlType qmlTypeForObject(QObject *object)
return type;
}
void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
{
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
@ -698,11 +698,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
continue;
if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) {
if (!applyDeferredBindings)
continue;
} else {
if (applyDeferredBindings)
if (!(mode & ApplyDeferred))
continue;
} else if (!(mode & ApplyImmediate)) {
continue;
}
if (property && property->isQList()) {
@ -1656,7 +1655,9 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
if (_compiledObject->nFunctions > 0)
setupFunctions();
setupBindings();
setupBindings((binding && (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding))
? BindingMode::ApplyAll
: BindingMode::ApplyImmediate);
for (int aliasIndex = 0; aliasIndex != _compiledObject->aliasCount(); ++aliasIndex) {
const QV4::CompiledData::Alias* alias = _compiledObject->aliasesBegin() + aliasIndex;

View File

@ -171,7 +171,15 @@ private:
const QQmlPropertyPrivate *qmlProperty,
const QV4::CompiledData::Binding *binding);
void setupBindings(bool applyDeferredBindings = false);
enum BindingMode {
ApplyNone = 0x0,
ApplyImmediate = 0x1,
ApplyDeferred = 0x2,
ApplyAll = ApplyImmediate | ApplyDeferred,
};
Q_DECLARE_FLAGS(BindingSetupFlags, BindingMode);
void setupBindings(BindingSetupFlags mode = BindingMode::ApplyImmediate);
bool setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setupFunctions();

View File

@ -1226,14 +1226,16 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject()
bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
{
using namespace QV4::CompiledData;
QmlIR::Object *obj = qmlObjects->at(objectIndex);
if (obj->idNameIndex != 0)
_seenObjectWithId = true;
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
if (obj->flags & Object::IsComponent) {
Q_ASSERT(obj->bindingCount() == 1);
const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
const Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == Binding::Type_Object);
return scanObject(componentBinding->value.objectIndex);
}
@ -1280,40 +1282,61 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
}
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
QQmlPropertyData *pd = nullptr;
QString name = stringAt(binding->propertyNameIndex);
if (customParser) {
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (binding->type == Binding::Type_AttachedProperty) {
if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
binding->flags |= Binding::IsCustomParserBinding;
obj->flags |= Object::HasCustomParserBindings;
continue;
}
} else if (QmlIR::IRBuilder::isSignalPropertyName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
obj->flags |= Object::HasCustomParserBindings;
binding->flags |= Binding::IsCustomParserBinding;
continue;
}
}
if (name.isEmpty()) {
pd = defaultProperty;
name = defaultPropertyName;
} else {
if (name.constData()->isUpper())
continue;
const bool hasPropertyData = [&]() {
if (name.isEmpty()) {
name = defaultPropertyName;
if (defaultProperty)
return true;
} else if (name.constData()->isUpper()) {
// Upper case names cannot be custom-parsed unless they are attached properties
// and the custom parser explicitly accepts them. See above for that case.
return false;
} else {
bool notInRevision = false;
if (propertyResolver.property(
name, &notInRevision, QQmlPropertyResolver::CheckRevision)) {
return true;
}
}
bool notInRevision = false;
pd = propertyResolver.property(name, &notInRevision,
QQmlPropertyResolver::CheckRevision);
}
if (!customParser)
return false;
if (binding->flags & Binding::IsSignalHandlerExpression
|| binding->flags & Binding::IsSignalHandlerObject
|| binding->flags & Binding::IsPropertyObserver) {
// These signal handlers cannot be custom-parsed. We have already established
// that the signal exists.
return false;
}
// If the property isn't found, we may want to custom-parse the binding.
obj->flags |= Object::HasCustomParserBindings;
binding->flags |= Binding::IsCustomParserBinding;
return false;
}();
bool seenSubObjectWithId = false;
bool isExternal = false;
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
const bool isOwnProperty = pd || binding->isAttachedProperty();
if (binding->type >= Binding::Type_Object) {
const bool isOwnProperty = hasPropertyData || binding->isAttachedProperty();
isExternal = !isOwnProperty && binding->isGroupProperty();
if (isOwnProperty || isExternal) {
qSwap(_seenObjectWithId, seenSubObjectWithId);
@ -1337,7 +1360,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
qWarning("Binding on %s is not deferred as requested by the DeferredPropertyNames "
"class info because one or more of its sub-objects contain an id.",
qPrintable(name));
} else if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
} else if (binding->type == Binding::Type_GroupProperty) {
qWarning("Binding on %s is not deferred as requested by the DeferredPropertyNames "
"class info because it constitutes a group property.", qPrintable(name));
} else {
@ -1345,7 +1368,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
}
}
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
if (binding->type >= Binding::Type_Object) {
if (isExternal && !isDeferred && !customParser) {
COMPILE_EXCEPTION(
binding, tr("Cannot assign to non-existent property \"%1\"").arg(name));
@ -1353,20 +1376,8 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
}
if (isDeferred) {
binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
}
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
|| binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject
|| binding->flags & QV4::CompiledData::Binding::IsPropertyObserver)
continue;
if (!pd) {
if (customParser) {
obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
}
binding->flags |= Binding::IsDeferredBinding;
obj->flags |= Object::HasDeferredBindings;
}
}

View File

@ -12,6 +12,7 @@ QtObject {
root.objectName: objectName + " ..."
root.onSomething: objectName = "rabrab"
root.MyQmlObject.value: 10
MyQmlObject.value: 4
}
property QtObject meanChild: ImmediateProperties {

View File

@ -6595,15 +6595,20 @@ void tst_qqmllanguage::generalizedGroupedProperty()
ImmediateProperties *child = qvariant_cast<ImmediateProperties *>(o->property("child"));
QVERIFY(child);
QCOMPARE(child->objectName(), QStringLiteral("barrrrr"));
qmlExecuteDeferred(child);
QCOMPARE(o->objectName(), QStringLiteral("barrrrr ..."));
QCOMPARE(rootAttached->value(), 10);
QCOMPARE(rootAttached->value2(), 0);
MyAttachedObject *childAttached = static_cast<MyAttachedObject *>(
qmlAttachedPropertiesObject<MyQmlObject>(child));
QVERIFY(childAttached);
QCOMPARE(childAttached->value(), 0);
qmlExecuteDeferred(child);
QCOMPARE(childAttached->value(), 4);
QCOMPARE(o->objectName(), QStringLiteral("barrrrr ..."));
QCOMPARE(rootAttached->value(), 10);
QCOMPARE(rootAttached->value2(), 0);
QCOMPARE(childAttached->value(), 4);
o->metaObject()->invokeMethod(o.data(), "something");
QCOMPARE(o->objectName(), QStringLiteral("rabrab ..."));