diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2022-04-12 16:35:23 +0200 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2022-04-28 09:43:36 +0200 |
commit | f37c7888d001bc1f39edffbdd52b381c65fc0711 (patch) | |
tree | 130645ce3c442dbfd276ba57e6814b3b9d9e1898 /tools | |
parent | 432f651439465d2f9f56c61c2466a7b2b846169d (diff) |
Migrate to QQmlJSScope-based deferred property checks in qmltc
We have enough information to support simple deferred properties
outside of QmlIR. Amends 68924be5b5282fb9f0276c743cf450f2e2aa5274
Change-Id: I4e32180b67af9942f6f8bfba0d498a62c402aa86
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qmltc/prototype/codegenerator.cpp | 29 | ||||
-rw-r--r-- | tools/qmltc/prototype/qml2cppdefaultpasses.cpp | 38 | ||||
-rw-r--r-- | tools/qmltc/prototype/qml2cppdefaultpasses.h | 2 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.cpp | 16 | ||||
-rw-r--r-- | tools/qmltc/qmltccompilerpieces.h | 13 | ||||
-rw-r--r-- | tools/qmltc/qmltcvisitor.cpp | 39 | ||||
-rw-r--r-- | tools/qmltc/qmltcvisitor.h | 20 |
7 files changed, 80 insertions, 77 deletions
diff --git a/tools/qmltc/prototype/codegenerator.cpp b/tools/qmltc/prototype/codegenerator.cpp index 3b8f5d11d2..df4ba6012b 100644 --- a/tools/qmltc/prototype/codegenerator.cpp +++ b/tools/qmltc/prototype/codegenerator.cpp @@ -300,7 +300,6 @@ void CodeGenerator::constructObjects(QSet<QString> &requiredCppIncludes) m_ignoredTypes = collectIgnoredTypes(context, objects); }; executor.addPass(setIgnoredTypes); - executor.addPass(&setDeferredBindings); // run all passes: executor.run(m_logger); @@ -599,10 +598,23 @@ void CodeGenerator::compileBinding(QmltcType ¤t, const QmlIR::Binding &bin const CodeGenObject &object, const CodeGenerator::AccessorData &accessor) { + // 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()) { + // if empty, try default property + for (QQmlJSScope::ConstPtr t = object.type->baseType(); t && propertyName.isEmpty(); + t = t->baseType()) + propertyName = t->defaultPropertyName(); + } + Q_ASSERT(!propertyName.isEmpty()); + QQmlJSMetaProperty p = object.type->property(propertyName); + QQmlJSScope::ConstPtr propertyType = p.type(); + // Q_ASSERT(propertyType); // TODO: doesn't work with signals + // 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 (object.type->isNameDeferred(propertyName)) { if (binding.type == QmlIR::Binding::Type_GroupProperty) { // TODO: we should warn about this in QmlCompiler library qCWarning(lcCodeGenerator) @@ -622,19 +634,6 @@ void CodeGenerator::compileBinding(QmltcType ¤t, const QmlIR::Binding &bin } } - // 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()) { - // if empty, try default property - for (QQmlJSScope::ConstPtr t = object.type->baseType(); t && propertyName.isEmpty(); - t = t->baseType()) - propertyName = t->defaultPropertyName(); - } - Q_ASSERT(!propertyName.isEmpty()); - QQmlJSMetaProperty p = object.type->property(propertyName); - QQmlJSScope::ConstPtr propertyType = p.type(); - // Q_ASSERT(propertyType); // TODO: doesn't work with signals - const auto addPropertyLine = [&](const QString &propertyName, const QQmlJSMetaProperty &p, const QString &value, bool constructQVariant = false) { // TODO: there mustn't be this special case. instead, alias resolution diff --git a/tools/qmltc/prototype/qml2cppdefaultpasses.cpp b/tools/qmltc/prototype/qml2cppdefaultpasses.cpp index c5901f9355..f0c2c3349c 100644 --- a/tools/qmltc/prototype/qml2cppdefaultpasses.cpp +++ b/tools/qmltc/prototype/qml2cppdefaultpasses.cpp @@ -1084,42 +1084,4 @@ 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); -} - QT_END_NAMESPACE diff --git a/tools/qmltc/prototype/qml2cppdefaultpasses.h b/tools/qmltc/prototype/qml2cppdefaultpasses.h index 6a1d0ca124..5f4b750227 100644 --- a/tools/qmltc/prototype/qml2cppdefaultpasses.h +++ b/tools/qmltc/prototype/qml2cppdefaultpasses.h @@ -88,8 +88,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); - QT_END_NAMESPACE #endif // QML2CPPPASSES_H diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp index 9fecb1d0e2..cda3a4ab81 100644 --- a/tools/qmltc/qmltccompiler.cpp +++ b/tools/qmltc/qmltccompiler.cpp @@ -315,22 +315,6 @@ void QmltcCompiler::compileTypeElements(QmltcType ¤t, const QQmlJSScope::C { const CodeGenerator::CodeGenObject &object = m_prototypeCodegen->objectFromType(type); - // TODO: make this a part of QmltcCodeGenerator::generate_endInitCode (and - // stop relying on QmlIR!) - if (object.irObject->flags & QV4::CompiledData::Object::HasDeferredBindings) { - current.endInit.body << u"{ // defer bindings"_qs; - current.endInit.body << u"auto ddata = QQmlData::get(this);"_qs; - current.endInit.body << u"auto thisContext = ddata->outerContext;"_qs; - current.endInit.body << u"Q_ASSERT(thisContext);"_qs; - current.endInit.body << QStringLiteral("ddata->deferData(%1, " - "QQmlEnginePrivate::get(engine)->" - "compilationUnitFromUrl(%2()), thisContext);") - .arg(QString::number( - m_prototypeCodegen->codegenObjectIndex(type)), - QmltcCodeGenerator::urlMethodName); - current.endInit.body << u"}"_qs; - } - // compile components of a type: // - enums // - properties diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h index 5874665af1..ca4e36d2a3 100644 --- a/tools/qmltc/qmltccompilerpieces.h +++ b/tools/qmltc/qmltccompilerpieces.h @@ -330,6 +330,19 @@ QmltcCodeGenerator::generate_endInitCode(QmltcType ¤t, current.endInit.body << u"}"_qs; } + if (visitor->hasDeferredBindings(type)) { + current.endInit.body << u"{ // defer bindings"_qs; + current.endInit.body << u"auto ddata = QQmlData::get(this);"_qs; + current.endInit.body << u"auto thisContext = ddata->outerContext;"_qs; + current.endInit.body << u"Q_ASSERT(thisContext);"_qs; + current.endInit.body << QStringLiteral("ddata->deferData(%1, " + "QQmlEnginePrivate::get(engine)->" + "compilationUnitFromUrl(%2()), thisContext);") + .arg(QString::number(visitor->qmlIrObjectIndex(type)), + QmltcCodeGenerator::urlMethodName); + current.endInit.body << u"}"_qs; + } + // TODO: QScopeGuard here is redundant. we should call endInit of children // directly const auto generateFinalLines = [¤t, isDocumentRoot, this]() { diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index 5d263fdd93..eb3fb7155a 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -146,6 +146,30 @@ void QmltcVisitor::findCppIncludes() } } +void QmltcVisitor::findTypeIndicesInQmlDocument() +{ + qsizetype count = 0; + + // Perform DFS to align with the logic of discovering new QmlIR::Objects + // during IR building: we should align with it here to get correct object + // indices within the QmlIR::Document. + QList<QQmlJSScope::Ptr> stack; + stack.append(m_exportedRootScope); + + while (!stack.isEmpty()) { + QQmlJSScope::Ptr current = stack.takeLast(); + + if (current->scopeType() == QQmlJSScope::QMLScope) { + Q_ASSERT(!m_qmlIrObjectIndices.contains(current)); + m_qmlIrObjectIndices[current] = count; + ++count; + } + + const auto &children = current->childScopes(); + std::copy(children.rbegin(), children.rend(), std::back_inserter(stack)); + } +} + bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object) { if (!QQmlJSImportVisitor::visit(object)) @@ -281,6 +305,7 @@ void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program) postVisitResolve(bindings); findCppIncludes(); + findTypeIndicesInQmlDocument(); } QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding) @@ -372,12 +397,14 @@ void QmltcVisitor::postVisitResolve( // 1. find types that are part of the deferred bindings (we care about // *types* exclusively here) QSet<QQmlJSScope::ConstPtr> deferredTypes; - const auto findDeferred = [&deferredTypes](const QQmlJSScope::ConstPtr &type, - const QQmlJSMetaPropertyBinding &binding) { - if (binding.hasObject() || binding.hasInterceptor() || binding.hasValueSource()) { - const QString propertyName = binding.propertyName(); - Q_ASSERT(!propertyName.isEmpty()); - if (type->isNameDeferred(propertyName)) { + const auto findDeferred = [&](const QQmlJSScope::ConstPtr &type, + const QQmlJSMetaPropertyBinding &binding) { + const QString propertyName = binding.propertyName(); + Q_ASSERT(!propertyName.isEmpty()); + if (type->isNameDeferred(propertyName)) { + m_typesWithDeferredBindings.insert(type); + + if (binding.hasObject() || binding.hasInterceptor() || binding.hasValueSource()) { deferredTypes.insert(fetchType(binding)); return true; } diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h index b3ba6e91aa..af239e9f7b 100644 --- a/tools/qmltc/qmltcvisitor.h +++ b/tools/qmltc/qmltcvisitor.h @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE class QmltcVisitor : public QQmlJSImportVisitor { void findCppIncludes(); + void findTypeIndicesInQmlDocument(); void postVisitResolve(const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings); @@ -82,6 +83,13 @@ public: return m_syntheticTypeIndices[type] + qmlTypes().size(); } + qsizetype qmlIrObjectIndex(const QQmlJSScope::ConstPtr &type) const + { + Q_ASSERT(m_qmlIrObjectIndices.contains(type)); + Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope); + return m_qmlIrObjectIndices[type]; + } + /*! \internal Returns a runtime index counterpart of `id: foo` for \a type. Returns -1 if \a type does not have an id. @@ -106,6 +114,15 @@ public: */ QList<QQmlJSScope::ConstPtr> pureQmlTypes() const { return m_pureQmlTypes; } + /*! \internal + Returns \c true when \a type has deferred bindings. Returns \c false + otherwise. + */ + bool hasDeferredBindings(const QQmlJSScope::ConstPtr &type) const + { + return m_typesWithDeferredBindings.contains(type); + } + protected: QStringList m_qmlTypeNames; // names of QML types arranged as a stack QHash<QString, int> m_qmlTypeNameCounts; @@ -115,6 +132,9 @@ protected: QHash<QQmlJSScope::ConstPtr, qsizetype> m_pureTypeIndices; QHash<QQmlJSScope::ConstPtr, qsizetype> m_syntheticTypeIndices; + QHash<QQmlJSScope::ConstPtr, qsizetype> m_qmlIrObjectIndices; + + QSet<QQmlJSScope::ConstPtr> m_typesWithDeferredBindings; // prefer allQmlTypes or pureQmlTypes. this function is misleading in qmltc QList<QQmlJSScope::ConstPtr> qmlTypes() const { return QQmlJSImportVisitor::qmlTypes(); } |