diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2021-12-30 13:29:41 +0100 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2022-02-22 09:20:17 +0100 |
commit | eaf00da10a4ae10b6b26684fe5893f671ad8f631 (patch) | |
tree | 1a9409d5c81d561bd3c16034cccaefad30121198 /tools/qmltc | |
parent | c07a77c96eb2ff3ce3317da8336d9a6891b50e98 (diff) |
qmltc: Handle (simple) deferred properties correctly
Simple deferred properties occur quite often in QML (throughout
Qt Quick, for example), so qmltc should be able to deal with
them as with deferred properties
Ignore generalized group properties, PropertyChanges and similar
types for now. They require more testing and are well out of
scope of the tech preview
Task-number: QTBUG-100053
Change-Id: I0f3588789d188cd6bec81de0b61d3205b665a917
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 68924be5b5282fb9f0276c743cf450f2e2aa5274)
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tools/qmltc')
-rw-r--r-- | tools/qmltc/prototype/codegenerator.cpp | 38 | ||||
-rw-r--r-- | tools/qmltc/prototype/qml2cppdefaultpasses.cpp | 38 | ||||
-rw-r--r-- | tools/qmltc/prototype/qml2cppdefaultpasses.h | 2 |
3 files changed, 77 insertions, 1 deletions
diff --git a/tools/qmltc/prototype/codegenerator.cpp b/tools/qmltc/prototype/codegenerator.cpp index f7c13657a7..f0e2ca2993 100644 --- a/tools/qmltc/prototype/codegenerator.cpp +++ b/tools/qmltc/prototype/codegenerator.cpp @@ -291,6 +291,7 @@ void CodeGenerator::constructObjects(QSet<QString> &requiredCppIncludes) m_ignoredTypes = collectIgnoredTypes(context, objects); }; executor.addPass(setIgnoredTypes); + executor.addPass(&setDeferredBindings); // run all passes: executor.run(m_logger); @@ -650,6 +651,17 @@ void CodeGenerator::compileObject( + u"(engine, /* finalize */ false);"; compiled.endInit.body << u"}"_qs; } + + if (object.irObject->flags & QV4::CompiledData::Object::HasDeferredBindings) { + compiled.endInit.body << u"{ // defer bindings"_qs; + compiled.endInit.body << u"auto ddata = QQmlData::get(this);"_qs; + compiled.endInit.body << u"auto thisContext = ddata->outerContext;"_qs; + compiled.endInit.body << u"Q_ASSERT(thisContext);"_qs; + compiled.endInit.body << u"ddata->deferData(" + QString::number(objectIndex) + u", " + + CodeGeneratorUtility::compilationUnitVariable.name + u", thisContext);"; + compiled.endInit.body << u"}"_qs; + } + // TODO: decide whether begin/end property update group is needed // compiled.endInit.body << u"Qt::beginPropertyUpdateGroup(); // defer binding evaluation"_qs; @@ -1168,6 +1180,29 @@ void CodeGenerator::compileBinding(QQmlJSAotObject ¤t, const QmlIR::Bindin const CodeGenObject &object, const CodeGenerator::AccessorData &accessor) { + // Note: unlike QQmlObjectCreator, we don't have to do a complicated + // deferral logic for bindings: if a binding is deferred, it is not compiled + // (potentially, with all the bindings inside of it), period. + if (binding.flags & QV4::CompiledData::Binding::IsDeferredBinding) { + if (binding.type == QmlIR::Binding::Type_GroupProperty) { + // TODO: we should warn about this in QmlCompiler library + qCWarning(lcCodeGenerator) + << QStringLiteral("Binding at line %1 column %2 is not deferred as it is a " + "binding on a group property.") + .arg(QString::number(binding.location.line), + QString::number(binding.location.column)); + // we do not support PropertyChanges and other types with similar + // behavior yet, so this binding is compiled + } else { + qCDebug(lcCodeGenerator) + << QStringLiteral( + "Binding at line %1 column %2 is deferred and thus not compiled") + .arg(QString::number(binding.location.line), + QString::number(binding.location.column)); + return; + } + } + // TODO: cache property name somehow, so we don't need to look it up again QString propertyName = m_doc->stringAt(binding.propertyNameIndex); if (propertyName.isEmpty()) { @@ -1413,9 +1448,10 @@ void CodeGenerator::compileBinding(QQmlJSAotObject ¤t, const QmlIR::Bindin // compile bindings of the attached property auto sortedBindings = toOrderedSequence( irObject->bindingsBegin(), irObject->bindingsEnd(), irObject->bindingCount()); - for (auto it : qAsConst(sortedBindings)) + for (auto it : qAsConst(sortedBindings)) { compileBinding(current, *it, attachedObject, { object.type, attachedMemberName, propertyName, false }); + } } break; } diff --git a/tools/qmltc/prototype/qml2cppdefaultpasses.cpp b/tools/qmltc/prototype/qml2cppdefaultpasses.cpp index 586cc85754..611c317490 100644 --- a/tools/qmltc/prototype/qml2cppdefaultpasses.cpp +++ b/tools/qmltc/prototype/qml2cppdefaultpasses.cpp @@ -1079,3 +1079,41 @@ QSet<QQmlJSScope::ConstPtr> collectIgnoredTypes(const Qml2CppContext &context, return ignored; } + +static void setDeferred(const Qml2CppContext &context, qsizetype objectIndex, + QList<Qml2CppObject> &objects) +{ + Q_UNUSED(objects); + + Qml2CppObject &o = objects[objectIndex]; + + // c.f. QQmlDeferredAndCustomParserBindingScanner::scanObject() + if (o.irObject->flags & QV4::CompiledData::Object::IsComponent) { + // unlike QmlIR compiler, qmltc should not care about anything within a + // component (let the QQmlComponent wrapper - at runtime anyway - take + // care of this type instead) + return; + } + + const auto setRecursive = [&](QmlIR::Binding &binding) { + if (binding.type >= QmlIR::Binding::Type_Object) + setDeferred(context, binding.value.objectIndex, objects); // Note: recursive call here! + + const QString propName = findPropertyName(context, o.type, binding); + Q_ASSERT(!propName.isEmpty()); + + if (o.type->isNameDeferred(propName)) { + binding.flags |= QV4::CompiledData::Binding::IsDeferredBinding; + o.irObject->flags |= QV4::CompiledData::Object::HasDeferredBindings; + } + }; + + std::for_each(o.irObject->bindingsBegin(), o.irObject->bindingsEnd(), setRecursive); +} + +void setDeferredBindings(const Qml2CppContext &context, QList<Qml2CppObject> &objects) +{ + // as we do not support InlineComponents just yet, we can shortcut the logic + // here to only work with root object + setDeferred(context, 0, objects); +} diff --git a/tools/qmltc/prototype/qml2cppdefaultpasses.h b/tools/qmltc/prototype/qml2cppdefaultpasses.h index 29e5acf985..443292b30f 100644 --- a/tools/qmltc/prototype/qml2cppdefaultpasses.h +++ b/tools/qmltc/prototype/qml2cppdefaultpasses.h @@ -86,4 +86,6 @@ findImmediateParents(const Qml2CppContext &context, QList<Qml2CppObject> &object QSet<QQmlJSScope::ConstPtr> collectIgnoredTypes(const Qml2CppContext &context, QList<Qml2CppObject> &objects); +void setDeferredBindings(const Qml2CppContext &context, QList<Qml2CppObject> &objects); + #endif // QML2CPPPASSES_H |