aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2022-04-12 16:35:23 +0200
committerAndrei Golubev <andrei.golubev@qt.io>2022-04-28 09:43:36 +0200
commitf37c7888d001bc1f39edffbdd52b381c65fc0711 (patch)
tree130645ce3c442dbfd276ba57e6814b3b9d9e1898 /tools
parent432f651439465d2f9f56c61c2466a7b2b846169d (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.cpp29
-rw-r--r--tools/qmltc/prototype/qml2cppdefaultpasses.cpp38
-rw-r--r--tools/qmltc/prototype/qml2cppdefaultpasses.h2
-rw-r--r--tools/qmltc/qmltccompiler.cpp16
-rw-r--r--tools/qmltc/qmltccompilerpieces.h13
-rw-r--r--tools/qmltc/qmltcvisitor.cpp39
-rw-r--r--tools/qmltc/qmltcvisitor.h20
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 &current, 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 &current, 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 &current, 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 &current,
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 = [&current, 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(); }