aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2022-09-30 12:34:52 +0200
committerSami Shalayel <sami.shalayel@qt.io>2022-10-11 16:42:34 +0200
commitf60ed66cb5c45ff5c638af12ae92f8274cde9175 (patch)
tree222937766f50171890a298c0a88fae5520a3f299
parentdc6d4f243b7be616a832510439c1b84067a5865f (diff)
qmltc: fix code generation for implicit components
Do not check if a type is implictly wrapped during visitation, as this information is only made available by QQmlJSImportVisitor after(!) the visit. Move this into the postvisit step of qmltc, and write some internal documentation so this error will hopefully not be done again by some qmltc contributor (e.g., me). Now that implicit components are correctly recognized as such, they can be compiled correctly and the c++-code generated by qmltc can again be compiled. Added also a test to see if all childrens of the repeater are correctly instantiated. Fixes: QTBUG-107091 Change-Id: I8fce018b83316f8786ae5ca15e5af27c30bb1d37 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit be104adfb5748e801e03f9d1b5fcc91ab74c5c4d)
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp3
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml22
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.cpp33
-rw-r--r--tests/auto/qml/qmltc/tst_qmltc.h1
-rw-r--r--tools/qmltc/qmltcvisitor.cpp63
6 files changed, 100 insertions, 23 deletions
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index 3e0cff0e52..c0513d1554 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -267,6 +267,9 @@ bool QQmlJSScope::causesImplicitComponentWrapping(const QQmlJSMetaProperty &prop
Either because it has been implicitly wrapped, e.g. due to an assignment to
a Component property, or because it is the first (and only) child of a
Component.
+ For visitors: This method should only be called after implicit components
+ are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
+ was called.
*/
bool QQmlJSScope::isComponentRootElement() const {
if (m_flags.testFlag(WrappedInImplicitComponent))
diff --git a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt
index 45fc2492dd..fbb5de6815 100644
--- a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt
+++ b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt
@@ -90,6 +90,7 @@ set(qml_sources
deferredProperties_group.qml
deferredProperties_attached.qml
deferredProperties_complex.qml
+ repeaterCrash.qml
# support types:
DefaultPropertySingleChild.qml
diff --git a/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml b/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml
new file mode 100644
index 0000000000..7c4dad5a4e
--- /dev/null
+++ b/tests/auto/qml/qmltc/QmltcTests/repeaterCrash.qml
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+
+Item {
+ Item {
+ property string objName: "Child1"
+ }
+
+ Repeater {
+ model: 4
+
+ Item {
+ property string objName: "Child" + (index + 1)
+ }
+ }
+
+ Item {
+ property string objName: "Child6"
+ }
+}
diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp
index 5a0d7eea9d..e085ce9f24 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.cpp
+++ b/tests/auto/qml/qmltc/tst_qmltc.cpp
@@ -69,6 +69,7 @@
#include "privatepropertysubclass.h"
#include "calqlatrbits.h"
#include "propertychangeandsignalhandlers.h"
+#include "repeatercrash.h"
#include "testprivateproperty.h"
@@ -2161,4 +2162,36 @@ void tst_qmltc::trickyPropertyChangeAndSignalHandlers()
QCOMPARE(created.cChangedCount3(), 22);
}
+void tst_qmltc::repeaterCrash()
+{
+ QQmlEngine e;
+ PREPEND_NAMESPACE(repeaterCrash) fromQmltc(&e);
+
+ QQmlComponent component(&e, "qrc:/QmltcTests/repeaterCrash.qml");
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QQuickItem> fromEngine(qobject_cast<QQuickItem *>(component.create()));
+ QVERIFY(fromEngine);
+
+ const int size = 7;
+
+ const auto listFromEngine = fromEngine->childItems();
+ const auto listFromQmltc = fromQmltc.childItems();
+
+ QCOMPARE(listFromEngine.size(), size);
+ QCOMPARE(listFromQmltc.size(), size);
+
+ for (int i = 0; i < size; i++) {
+ // the repeater itself has no objName property
+ if (i == 5)
+ continue;
+
+ const QVariant nameFromEngine = listFromEngine.at(i)->property("objName");
+ const QVariant nameFromQmltc = listFromQmltc.at(i)->property("objName");
+
+ QVERIFY(nameFromEngine.isValid());
+ QVERIFY(nameFromQmltc.isValid());
+ QCOMPARE(nameFromQmltc.toString(), nameFromEngine.toString());
+ }
+}
+
QTEST_MAIN(tst_qmltc)
diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h
index 19f225a548..d0ead7ec03 100644
--- a/tests/auto/qml/qmltc/tst_qmltc.h
+++ b/tests/auto/qml/qmltc/tst_qmltc.h
@@ -82,4 +82,5 @@ private slots:
void privateProperties();
void calqlatrBits(); // corner cases from calqlatr demo
void trickyPropertyChangeAndSignalHandlers();
+ void repeaterCrash();
};
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index efe573d05d..a219e02e53 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -36,6 +36,12 @@ static bool isExplicitComponent(const QQmlJSScope::ConstPtr &type)
return base && base->internalName() == u"QQmlComponent";
}
+/*! \internal
+ Returns if type is an implicit component.
+ This method should only be called after implicit components
+ are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
+ was called.
+ */
static bool isImplicitComponent(const QQmlJSScope::ConstPtr &type)
{
if (!type->isComposite())
@@ -45,6 +51,12 @@ static bool isImplicitComponent(const QQmlJSScope::ConstPtr &type)
return type->isComponentRootElement() && !isComponentBased;
}
+/*! \internal
+ Checks if type is inside or a (implicit or explicit) component.
+ This method should only be called after implicit components
+ are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
+ was called.
+ */
static bool isOrUnderComponent(QQmlJSScope::ConstPtr type)
{
Q_ASSERT(type->isComposite()); // we're dealing with composite types here
@@ -186,11 +198,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
addCleanQmlTypeName(&m_qmlTypeNames, m_currentScope);
// give C++-relevant internal names to QMLScopes, we can use them later in compiler
m_currentScope->setInternalName(uniqueNameFromPieces(m_qmlTypeNames, m_qmlTypeNameCounts));
-
- if (auto base = m_currentScope->baseType();
- base && base->isComposite() && !isOrUnderComponent(m_currentScope)) {
- m_qmlTypesWithQmlBases.append(m_currentScope);
- }
+ m_qmlTypesWithQmlBases.append(m_currentScope);
return true;
}
@@ -212,11 +220,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
// give C++-relevant internal names to QMLScopes, we can use them later in compiler
m_currentScope->setInternalName(uniqueNameFromPieces(m_qmlTypeNames, m_qmlTypeNameCounts));
- if (auto base = m_currentScope->baseType();
- base && base->isComposite() && !isOrUnderComponent(m_currentScope)) {
- m_qmlTypesWithQmlBases.append(m_currentScope);
- }
-
+ m_qmlTypesWithQmlBases.append(m_currentScope);
return true;
}
@@ -408,13 +412,15 @@ void iterateBindings(
}
}
+/*! \internal
+ This is a special function that must be called after
+ QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiProgram *). It is used to
+ resolve things that couldn't be resolved during the AST traversal, such
+ as anything that is dependent on implicit or explicit components
+*/
void QmltcVisitor::postVisitResolve(
const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings)
{
- // This is a special function that must be called after
- // QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiProgram *). It is used to
- // resolve things that couldn't be resolved during the AST traversal, such
- // as anything that is dependent on implicit or explicit components
// match scopes to indices of QmlIR::Object from QmlIR::Document
qsizetype count = 0;
@@ -484,15 +490,26 @@ void QmltcVisitor::postVisitResolve(
m_creationIndices[c] = m_pureQmlTypes.size() + index++;
}
- // filter out deferred types
- {
- QList<QQmlJSScope::ConstPtr> filteredQmlTypesWithQmlBases;
- filteredQmlTypesWithQmlBases.reserve(m_qmlTypesWithQmlBases.size());
- std::copy_if(m_qmlTypesWithQmlBases.cbegin(), m_qmlTypesWithQmlBases.cend(),
- std::back_inserter(filteredQmlTypesWithQmlBases),
- [&](const QQmlJSScope::ConstPtr &type) { return !isOrUnderDeferred(type); });
- qSwap(m_qmlTypesWithQmlBases, filteredQmlTypesWithQmlBases);
- }
+ // m_qmlTypesWithQmlBases should contain the types to be compiled.
+ // Some types should not be compiled and are therefore filtered out:
+ // * deferred types
+ // * types inside of capital-c-Components (implicit and explicit)
+ // * non-composite types (that is, types not defined in qml)
+ //
+ // This can not be done earlier as implicitly wrapped Components are
+ // only known after visitation is over!
+
+ // filter step:
+ QList<QQmlJSScope::ConstPtr> filteredQmlTypesWithQmlBases;
+ filteredQmlTypesWithQmlBases.reserve(m_qmlTypesWithQmlBases.size());
+ std::copy_if(m_qmlTypesWithQmlBases.cbegin(), m_qmlTypesWithQmlBases.cend(),
+ std::back_inserter(filteredQmlTypesWithQmlBases),
+ [&](const QQmlJSScope::ConstPtr &type) {
+ auto base = type->baseType();
+ return base && base->isComposite() && !isOrUnderComponent(type)
+ && !isOrUnderDeferred(type);
+ });
+ qSwap(m_qmlTypesWithQmlBases, filteredQmlTypesWithQmlBases);
// count QmlIR::Objects in the document - the amount is used to calculate
// object indices of implicit components