aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2022-09-09 13:47:28 +0200
committerSami Shalayel <sami.shalayel@qt.io>2022-09-26 10:59:15 +0200
commitb89a92053e1a4565d37f75831db1c6c90c21bce6 (patch)
tree56f19730877d4da6c5b0ea1ebf4f7a1d4ac7a3ee /tools
parent60ca8dae85de1bd0df4d549f237534c68d75091c (diff)
qmltc: support basic inline components
This patch adds basic inline component support to qmltc. Implementation details: * added tests: ** (for all tests: see if QQmlComponent would do the same thing) ** try simple inline components ** try inline components extending other inline components ** try recursive inline components (inline component A contain a B that itself contains a A) ** make sure ids in inline components are in their own context, and ** make sure alias inside of inline components work ** remove qmltc_qprocess that tests the error message when inline components are encountered ** test that inline components get their own context (by aliasing a property that they should not be able to see) ** test that also empty components work ** other tests inspired from those at tst_qqmllanguage * separate types depending on the inline component they belong in qmltcvisitor and qmltccompiler ** mostly by replacing T with QHash<QString, T>, where the belonging inline component name serves as hash key ** added a list of inline component names in qmltc * generate correct qmltc-code for inline components ** as they sometimes have mimic a document root and sometimes not ** generate their own "typeCount"-method ** fixed naming used in qmltc generated code by using the inline component names from the qqmljsscope. There is one missing feature (using inline components from other files, using QmlFileName.MyInlineComponent) that will be added in a separate commit. Fixes: QTBUG-106882 Change-Id: Iabe1a8b787027367d73d34bcd93c5f7b5480d217 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tools')
-rw-r--r--tools/qmltc/qmltccompiler.cpp120
-rw-r--r--tools/qmltc/qmltccompiler.h4
-rw-r--r--tools/qmltc/qmltccompilerpieces.cpp2
-rw-r--r--tools/qmltc/qmltccompilerpieces.h103
-rw-r--r--tools/qmltc/qmltcvisitor.cpp111
-rw-r--r--tools/qmltc/qmltcvisitor.h79
6 files changed, 300 insertions, 119 deletions
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 30cad53255..141bf7e83b 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -62,8 +62,6 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
// Note: we only compile "pure" QML types. any component-wrapped type is
// expected to appear through a binding
- auto pureTypes = m_visitor->pureQmlTypes();
- Q_ASSERT(m_visitor->result() == pureTypes.at(0));
const auto isComponent = [](const QQmlJSScope::ConstPtr &type) {
auto base = type->baseType();
@@ -76,28 +74,64 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
compileUrlMethod(urlMethod, generator.urlMethodName());
m_urlMethodName = urlMethod.name;
- QQmlJSScope::ConstPtr root = pureTypes.at(0);
+ // sort inline components to compile them in the right order
+ // a inherits b => b needs to be defined in the cpp file before a!
+ // r is the root => r needs to be compiled at the end!
+ // otherwise => sort them by inline component names to have consistent output
+ auto sortedInlineComponentNames = m_visitor->inlineComponentNames();
+ std::sort(sortedInlineComponentNames.begin(), sortedInlineComponentNames.end(),
+ [&](const InlineComponentOrDocumentRootName &a,
+ const InlineComponentOrDocumentRootName &b) {
+ const auto *inlineComponentAName = std::get_if<InlineComponentNameType>(&a);
+ const auto *inlineComponentBName = std::get_if<InlineComponentNameType>(&b);
+
+ // the root comes at last, so (a < b) == true when b is the root and a is not
+ if (inlineComponentAName && !inlineComponentBName)
+ return true;
+
+ // b requires a to be declared before b when b inherits from a, therefore (a < b)
+ // == true
+ if (inlineComponentAName && inlineComponentBName) {
+ QQmlJSScope::ConstPtr inlineComponentA = m_visitor->inlineComponent(a);
+ QQmlJSScope::ConstPtr inlineComponentB = m_visitor->inlineComponent(b);
+ if (inlineComponentB->inherits(inlineComponentA)) {
+ return true;
+ } else if (inlineComponentA->inherits(inlineComponentB)) {
+ return false;
+ } else {
+ // fallback to default sorting based on names
+ return *inlineComponentAName < *inlineComponentBName;
+ }
+ }
+ Q_ASSERT(!inlineComponentAName || !inlineComponentBName);
+ // a is the root or both a and b are the root
+ return false;
+ });
QList<QmltcType> compiledTypes;
- if (isComponent(root)) {
- compiledTypes.reserve(1);
- compiledTypes.emplaceBack(); // create empty type
- const auto compile = [&](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
- generator.generate_initCodeForTopLevelComponent(current, type);
- };
- compileType(compiledTypes.back(), root, compile);
- } else {
- const auto compile = [this](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
- compileTypeElements(current, type);
- };
-
- compiledTypes.reserve(pureTypes.size());
- for (const auto &type : pureTypes) {
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ for (const auto &inlineComponent : sortedInlineComponentNames) {
+ const QList<QQmlJSScope::ConstPtr> &pureTypes = m_visitor->pureQmlTypes(inlineComponent);
+ Q_ASSERT(!pureTypes.empty());
+ const QQmlJSScope::ConstPtr &root = pureTypes.front();
+ if (isComponent(root)) {
compiledTypes.emplaceBack(); // create empty type
- compileType(compiledTypes.back(), type, compile);
+ const auto compile = [&](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ generator.generate_initCodeForTopLevelComponent(current, type);
+ };
+ compileType(compiledTypes.back(), root, compile);
+ } else {
+ const auto compile = [this](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ compileTypeElements(current, type);
+ };
+
+ for (const auto &type : pureTypes) {
+ Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ compiledTypes.emplaceBack(); // create empty type
+ compileType(compiledTypes.back(), type, compile);
+ }
}
}
+
if (hasErrors())
return;
@@ -140,7 +174,11 @@ void QmltcCompiler::compileType(
const QString baseClass = type->baseType()->internalName();
const auto rootType = m_visitor->result();
+ const InlineComponentOrDocumentRootName name = type->enclosingInlineComponentName();
+ QQmlJSScope::ConstPtr inlineComponentType = m_visitor->inlineComponent(name);
+ Q_ASSERT(inlineComponentType);
const bool documentRoot = (type == rootType);
+ const bool inlineComponent = type->isInlineComponent();
const bool isAnonymous = !documentRoot || type->internalName().at(0).isLower();
QmltcCodeGenerator generator { m_url, m_visitor };
@@ -148,9 +186,26 @@ void QmltcCompiler::compileType(
current.baseClasses = { baseClass };
if (!documentRoot) {
// make document root a friend to allow it to access init and endInit
- current.otherCode << u"friend class %1;"_s.arg(rootType->internalName());
-
- // additionally make an immediate parent a friend since that parent
+ const QString rootInternalName =
+ m_visitor->inlineComponent(type->enclosingInlineComponentName())->internalName();
+ current.otherCode << u"friend class %1;"_s.arg(rootInternalName);
+ }
+ if (documentRoot || inlineComponent) {
+ auto name = type->inlineComponentName()
+ ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
+ : InlineComponentOrDocumentRootName(RootDocumentNameType());
+ // make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to
+ // be created for the root object
+ current.otherCode << u"friend class QQmltcObjectCreationBase<%1>;"_s.arg(
+ inlineComponentType->internalName());
+ // generate typeCount for all components (root + inlineComponents)
+ QmltcMethod typeCountMethod;
+ typeCountMethod.name = QmltcCodeGenerator::typeCountName;
+ typeCountMethod.returnType = u"uint"_s;
+ typeCountMethod.body << u"return " + generator.generate_typeCount(name) + u";";
+ current.typeCount = typeCountMethod;
+ } else {
+ // make an immediate parent a friend since that parent
// would create the object through a non-public constructor
const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) {
if (scope->isArrayScope())
@@ -159,17 +214,6 @@ void QmltcCompiler::compileType(
};
current.otherCode << u"friend class %1;"_s.arg(
realQmlScope(type->parentScope())->internalName());
- } else {
- // make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to
- // be created for the root object
- current.otherCode << u"friend class QQmltcObjectCreationBase<%1>;"_s.arg(
- rootType->internalName());
-
- QmltcMethod typeCountMethod;
- typeCountMethod.name = QmltcCodeGenerator::typeCountName;
- typeCountMethod.returnType = u"uint"_s;
- typeCountMethod.body << u"return " + generator.generate_typeCount() + u";";
- current.typeCount = typeCountMethod;
}
// make QQmltcObjectCreationHelper a friend of every type since it provides
@@ -179,12 +223,13 @@ void QmltcCompiler::compileType(
current.mocCode = {
u"Q_OBJECT"_s,
// Note: isAnonymous holds for non-root types in the document as well
- isAnonymous ? u"QML_ANONYMOUS"_s : u"QML_ELEMENT"_s,
+ type->isInlineComponent() ? (u"QML_NAMED_ELEMENT(%1)"_s.arg(*type->inlineComponentName()))
+ : (isAnonymous ? u"QML_ANONYMOUS"_s : u"QML_ELEMENT"_s),
};
// add special member functions
current.baselineCtor.access = QQmlJSMetaMethod::Protected;
- if (documentRoot) {
+ if (documentRoot || inlineComponent) {
current.externalCtor.access = QQmlJSMetaMethod::Public;
} else {
current.externalCtor.access = QQmlJSMetaMethod::Protected;
@@ -222,7 +267,8 @@ void QmltcCompiler::compileType(
current.endInit.parameterList = { creator, engine };
current.setComplexBindings.parameterList = { creator, engine };
current.handleOnCompleted.parameterList = { creator };
- if (documentRoot) {
+
+ if (documentRoot || inlineComponent) {
current.externalCtor.parameterList = { engine, parent };
current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
current.beginClass.parameterList = { creator, finalizeFlag };
@@ -250,7 +296,7 @@ void QmltcCompiler::compileType(
// compilation stub:
current.externalCtor.body << u"Q_UNUSED(engine);"_s;
- if (documentRoot) {
+ if (documentRoot || inlineComponent) {
current.externalCtor.body << u"// document root:"_s;
// if it's document root, we want to create our QQmltcObjectCreationBase
// that would store all the created objects
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index 436e54d852..a44c251e82 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -29,6 +29,10 @@ struct QmltcCompilerInfo
class QmltcCompiler
{
+ using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
+ using InlineComponentNameType = QQmlJSScope::InlineComponentNameType;
+ using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
+
public:
QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
QQmlJSLogger *logger);
diff --git a/tools/qmltc/qmltccompilerpieces.cpp b/tools/qmltc/qmltccompilerpieces.cpp
index a53bb89796..8d6f938aeb 100644
--- a/tools/qmltc/qmltccompilerpieces.cpp
+++ b/tools/qmltc/qmltccompilerpieces.cpp
@@ -168,6 +168,8 @@ void QmltcCodeGenerator::generate_setIdValue(QStringList *block, const QString &
const QString &idString)
{
Q_ASSERT(index >= 0);
+ *block << u"Q_ASSERT(%1 < %2->numIdValues()); // make sure Id is in bounds"_s.arg(index).arg(
+ context);
*block << u"%1->setIdValue(%2 /* id: %3 */, %4);"_s.arg(context, QString::number(index),
idString, accessor);
}
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
index 7e1df2637d..eb2a94a33f 100644
--- a/tools/qmltc/qmltccompilerpieces.h
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -32,6 +32,9 @@ struct QmltcCodeGenerator
QString documentUrl;
QmltcVisitor *visitor = nullptr;
+ using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
+ using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
+
[[nodiscard]] inline decltype(auto) generate_initCode(QmltcType &current,
const QQmlJSScope::ConstPtr &type) const;
inline void generate_initCodeForTopLevelComponent(QmltcType &current,
@@ -69,12 +72,22 @@ struct QmltcCodeGenerator
static void generate_setIdValue(QStringList *block, const QString &context, qsizetype index,
const QString &accessor, const QString &idString);
- inline QString generate_typeCount() const
+ inline QString
+ generate_typeCount(const InlineComponentOrDocumentRootName &inlinedComponent) const
{
- return generate_typeCount([](const QQmlJSScope::ConstPtr &) { return false; });
+ return generate_typeCount([](const QQmlJSScope::ConstPtr &) { return false; },
+ inlinedComponent);
}
+
+ /*!
+ * \internal
+ * Generate the constexpr typeCount expression for given inlinedComponent. Leave
+ * inlinedComponent empty to generate the expression for the main component.
+ */
template<typename Predicate>
- inline QString generate_typeCount(Predicate p) const;
+ inline QString
+ generate_typeCount(Predicate p,
+ const InlineComponentOrDocumentRootName &inlinedComponent) const;
static void generate_callExecuteRuntimeFunction(QStringList *block, const QString &url,
QQmlJSMetaMethod::AbsoluteFunctionIndex index,
@@ -164,6 +177,7 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
// * const QQmlRefPointer<QQmlContextData>& parentContext
// * bool canFinalize [optional, when document root]
const bool isDocumentRoot = type == visitor->result();
+ const bool isInlineComponent = type->isInlineComponent();
current.init.body << u"Q_UNUSED(creator);"_s; // can happen sometimes
@@ -190,15 +204,15 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
QString lhs;
// init creates new context. for document root, it's going to be a real
// parent context, so store it temporarily in `context` variable
- if (isDocumentRoot)
+ if (isDocumentRoot || isInlineComponent)
lhs = u"context = "_s;
current.init.body << u"// 0. call base's init method"_s;
- Q_ASSERT(!isDocumentRoot || visitor->qmlTypesWithQmlBases()[0] == visitor->result());
const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
return qmlType == type;
};
- const QString creationOffset = generate_typeCount(isCurrentType);
+ const QString creationOffset =
+ generate_typeCount(isCurrentType, type->enclosingInlineComponentName());
current.init.body << u"{"_s;
current.init.body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
@@ -213,31 +227,29 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
<< QStringLiteral("auto %1 = QQmlEnginePrivate::get(engine);").arg(privateEngineName);
current.init.body << QStringLiteral("Q_UNUSED(%1);").arg(privateEngineName); // precaution
- // when generating root, we need to create a new (document-level) context.
+ // when generating root or inlineComponents, we need to create a new (document-level) context.
// otherwise, just use existing context as is
- if (isDocumentRoot) {
+ if (isDocumentRoot || isInlineComponent) {
current.init.body << u"// 1. create new QML context for this document"_s;
- // TODO: the last 2 parameters are {0, true} because we deal with
- // document root only here. in reality, there are inline components
- // which need { index, true } instead
current.init.body
<< QStringLiteral(
"context = %1->createInternalContext(%1->compilationUnitFromUrl(%2()), "
- "context, 0, true);")
- .arg(privateEngineName, urlMethodName());
+ "context, %3, true);")
+ .arg(privateEngineName, urlMethodName())
+ .arg(this->visitor->creationIndex(type));
} else {
current.init.body << u"// 1. use current context as this object's context"_s;
current.init.body << u"// context = context;"_s;
}
- if (!type->baseType()->isComposite() || isDocumentRoot) {
+ if (!type->baseType()->isComposite() || isDocumentRoot || isInlineComponent) {
current.init.body << u"// 2. set context for this object"_s;
current.init.body << QStringLiteral(
"%1->setInternalContext(this, context, QQmlContextData::%2);")
.arg(privateEngineName,
(isDocumentRoot ? u"DocumentRoot"_s
: u"OrdinaryObject"_s));
- if (isDocumentRoot)
+ if (isDocumentRoot || isInlineComponent)
current.init.body << u"context->setContextObject(this);"_s;
}
@@ -274,8 +286,8 @@ inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
current.init.body << u"}"_s;
}
- const auto generateFinalLines = [&current, isDocumentRoot]() {
- if (isDocumentRoot) {
+ const auto generateFinalLines = [&current, isDocumentRoot, isInlineComponent]() {
+ if (isDocumentRoot || isInlineComponent) {
current.init.body << u"// 4. finish the document root creation"_s;
current.init.body << u"if (canFinalize) {"_s;
current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
@@ -358,14 +370,13 @@ inline void QmltcCodeGenerator::generate_qmltcInstructionCallCode(
{
using namespace Qt::StringLiterals;
- const bool isDocumentRoot = type == visitor->result();
if (auto base = type->baseType(); base->isComposite()) {
function->body << u"// call base's method"_s;
- Q_ASSERT(!isDocumentRoot || visitor->qmlTypesWithQmlBases()[0] == visitor->result());
const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
return qmlType == type;
};
- const QString creationOffset = generate_typeCount(isCurrentType);
+ const QString creationOffset =
+ generate_typeCount(isCurrentType, type->enclosingInlineComponentName());
function->body << u"{"_s;
function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
creationOffset);
@@ -378,10 +389,16 @@ inline void QmltcCodeGenerator::generate_qmltcInstructionCallCode(
function->body << u"}"_s;
}
- if (!isDocumentRoot) // document root does all the work here
- return;
+ const bool isDocumentRoot = type == visitor->result();
+ const bool isInlineComponent = type->isInlineComponent();
- const auto types = visitor->pureQmlTypes();
+ if (!(isDocumentRoot
+ || isInlineComponent)) // document/inline component root does all the work here
+ return;
+ auto name = isInlineComponent
+ ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
+ : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
+ const auto types = visitor->pureQmlTypes(name);
function->body << u"// call children's methods"_s;
for (qsizetype i = 1; i < types.size(); ++i) {
const auto &type = types[i];
@@ -467,19 +484,20 @@ inline void QmltcCodeGenerator::generate_interfaceCallCode(QmltcMethod *function
// function's parameters:
// * QQmltcObjectCreationHelper* creator
- // * bool canFinalize [optional, when document root]
+ // * bool canFinalize [optional, when document root or inline component root]
const bool isDocumentRoot = type == visitor->result();
+ const bool isInlineComponent = type->isInlineComponent();
function->body << u"Q_UNUSED(creator);"_s;
- if (isDocumentRoot)
+ if (isDocumentRoot || isInlineComponent)
function->body << u"Q_UNUSED(canFinalize);"_s;
if (auto base = type->baseType(); base->isComposite()) {
function->body << u"// call base's method"_s;
- Q_ASSERT(!isDocumentRoot || visitor->qmlTypesWithQmlBases()[0] == visitor->result());
const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
return qmlType == type;
};
- const QString creationOffset = generate_typeCount(isCurrentType);
+ const QString creationOffset =
+ generate_typeCount(isCurrentType, type->enclosingInlineComponentName());
function->body << u"{"_s;
function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
creationOffset);
@@ -488,10 +506,14 @@ inline void QmltcCodeGenerator::generate_interfaceCallCode(QmltcMethod *function
function->body << u"}"_s;
}
- if (!isDocumentRoot)
+ if (!(isDocumentRoot || isInlineComponent))
return;
- const auto types = visitor->pureQmlTypes();
+ auto name = isInlineComponent
+ ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
+ : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
+
+ const auto types = visitor->pureQmlTypes(name);
function->body << u"// call children's methods"_s;
for (qsizetype i = 1; i < types.size(); ++i) {
const auto &type = types[i];
@@ -639,18 +661,25 @@ QmltcCodeGenerator::generate_handleOnCompletedCode(QmltcType &current,
For the object lookup logic itself, see QQmltcObjectCreationHelper
*/
template<typename Predicate>
-inline QString QmltcCodeGenerator::generate_typeCount(Predicate p) const
+inline QString QmltcCodeGenerator::generate_typeCount(
+ Predicate p, const InlineComponentOrDocumentRootName &inlinedComponent) const
{
using namespace Qt::StringLiterals;
- const QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount = visitor->qmlTypesWithQmlBases();
+ const QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount =
+ visitor->qmlTypesWithQmlBases(inlinedComponent);
QStringList components;
components.reserve(1 + typesWithBaseTypeCount.size());
- // add this document's type counts minus document root
- Q_ASSERT(visitor->pureQmlTypes().size() > 0);
- Q_ASSERT(visitor->typeCount() >= visitor->pureQmlTypes().size());
- components << QString::number(visitor->typeCount() - 1);
+ Q_ASSERT(visitor->pureQmlTypes(inlinedComponent).size() > 0);
+ Q_ASSERT(visitor->typeCount(inlinedComponent)
+ >= visitor->pureQmlTypes(inlinedComponent).size());
+ qsizetype typeCount = visitor->typeCount(inlinedComponent);
+
+ // add this document's type counts minus document root (if not an inline component)
+ if (std::holds_alternative<RootDocumentNameType>(inlinedComponent))
+ typeCount--;
+ components << QString::number(typeCount);
// traverse types with QML base classes
for (const QQmlJSScope::ConstPtr &t : typesWithBaseTypeCount) {
@@ -659,6 +688,10 @@ inline QString QmltcCodeGenerator::generate_typeCount(Predicate p) const
QString typeCountTemplate = u"QQmltcObjectCreationHelper::typeCount<%1>()"_s;
if (t == visitor->result()) { // t is this document's root
components << typeCountTemplate.arg(t->baseTypeName());
+ } else if (t->isInlineComponent()) {
+ // inline components always have a base class, by definition
+ Q_ASSERT(t->baseType());
+ components << typeCountTemplate.arg(t->baseType()->internalName());
} else {
components << typeCountTemplate.arg(t->internalName());
}
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index 017b5d5440..a95ee1cac3 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -49,8 +49,9 @@ static bool isOrUnderComponent(QQmlJSScope::ConstPtr type)
{
Q_ASSERT(type->isComposite()); // we're dealing with composite types here
for (; type; type = type->parentScope()) {
- if (isExplicitComponent(type) || isImplicitComponent(type))
+ if (isExplicitComponent(type) || isImplicitComponent(type)) {
return true;
+ }
}
return false;
}
@@ -163,7 +164,9 @@ static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr
Q_ASSERT(!scope->baseTypeName().isEmpty());
// the scope is guaranteed to be a new QML type, so any prefixes (separated
// by dots) should be import namespaces
- names->append(scope->baseTypeName().replace(u'.', u'_'));
+ const std::optional<QString> &inlineComponentName = scope->inlineComponentName();
+ QString name = inlineComponentName ? *inlineComponentName : scope->baseTypeName();
+ names->append(name.replace(u'.', u'_'));
}
bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
@@ -182,14 +185,18 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
if (m_currentScope->scopeType() != QQmlJSScope::QMLScope)
return true;
+ if (m_currentScope->isInlineComponent()) {
+ m_inlineComponentNames.append(m_currentInlineComponentName);
+ m_inlineComponents[m_currentInlineComponentName] = m_currentScope;
+ }
+
if (m_currentScope != m_exportedRootScope) // not document root
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);
+ const QQmlJSScope::ConstPtr base = m_currentScope->baseType();
+ if (base && base->isComposite() && !isOrUnderComponent(m_currentScope)) {
+ m_qmlTypesWithQmlBases[m_currentInlineComponentName].append(m_currentScope);
}
return true;
@@ -212,9 +219,9 @@ 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);
+ const QQmlJSScope::ConstPtr base = m_currentScope->baseType();
+ if (base && base->isComposite() && !isOrUnderComponent(m_currentScope)) {
+ m_qmlTypesWithQmlBases[m_currentInlineComponentName].append(m_currentScope);
}
return true;
@@ -295,10 +302,6 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiInlineComponent *component)
{
if (!QQmlJSImportVisitor::visit(component))
return false;
- m_logger->log(u"Inline components are not supported"_s, qmlCompiler,
- component->firstSourceLocation());
- // despite the failure, return true here so that we do not assert in
- // QQmlJSImportVisitor::endVisit(UiInlineComponent)
return true;
}
@@ -323,8 +326,9 @@ void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
findCppIncludes();
- for (const QQmlJSScope::ConstPtr &type : m_pureQmlTypes)
- checkForNamingCollisionsWithCpp(type);
+ for (const QList<QQmlJSScope::ConstPtr> &qmlTypes : m_pureQmlTypes)
+ for (const QQmlJSScope::ConstPtr &type : qmlTypes)
+ checkForNamingCollisionsWithCpp(type);
}
QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
@@ -416,6 +420,11 @@ void QmltcVisitor::postVisitResolve(
// resolve things that couldn't be resolved during the AST traversal, such
// as anything that is dependent on implicit or explicit components
+ // add the document root (that is not an inline component), as we usually
+ // want to iterate on all inline components, followed by the document root
+ m_inlineComponentNames.append(RootDocumentNameType());
+ m_inlineComponents[RootDocumentNameType()] = m_exportedRootScope;
+
// match scopes to indices of QmlIR::Object from QmlIR::Document
qsizetype count = 0;
const auto setIndex = [&](const QQmlJSScope::Ptr &current) {
@@ -444,7 +453,10 @@ void QmltcVisitor::postVisitResolve(
}
return false;
};
- iterateBindings(m_exportedRootScope, qmlIrOrderedBindings, findDeferred);
+ for (const auto &inlineComponentName : m_inlineComponentNames) {
+ iterateBindings(m_inlineComponents[inlineComponentName], qmlIrOrderedBindings,
+ findDeferred);
+ }
const auto isOrUnderDeferred = [&deferredTypes](QQmlJSScope::ConstPtr type) {
for (; type; type = type->parentScope()) {
@@ -455,7 +467,6 @@ void QmltcVisitor::postVisitResolve(
};
// find all "pure" QML types
- m_pureQmlTypes.reserve(m_qmlTypes.size());
QList<QQmlJSScope::ConstPtr> explicitComponents;
for (qsizetype i = 0; i < m_qmlTypes.size(); ++i) {
const QQmlJSScope::ConstPtr &type = m_qmlTypes.at(i);
@@ -473,30 +484,45 @@ void QmltcVisitor::postVisitResolve(
}
}
- m_creationIndices[type] = m_pureQmlTypes.size();
- m_pureQmlTypes.append(type);
+ const InlineComponentOrDocumentRootName inlineComponent =
+ type->enclosingInlineComponentName();
+ QList<QQmlJSScope::ConstPtr> &pureQmlTypes = m_pureQmlTypes[inlineComponent];
+ m_creationIndices[type] = pureQmlTypes.size();
+ pureQmlTypes.append(type);
+ }
+
+ // update the typeCounts
+ for (const auto &inlineComponent : m_inlineComponentNames) {
+ m_inlineComponentTypeCount[inlineComponent] = m_pureQmlTypes[inlineComponent].size();
}
// add explicit components to the object creation indices
{
- qsizetype index = 0;
- for (const QQmlJSScope::ConstPtr &c : qAsConst(explicitComponents))
- m_creationIndices[c] = m_pureQmlTypes.size() + index++;
+ QHash<InlineComponentOrDocumentRootName, qsizetype> index;
+ for (const QQmlJSScope::ConstPtr &c : qAsConst(explicitComponents)) {
+ const InlineComponentOrDocumentRootName inlineComponent =
+ c->enclosingInlineComponentName();
+ m_creationIndices[c] =
+ m_pureQmlTypes[inlineComponent].size() + index[inlineComponent]++;
+ m_inlineComponentTypeCount[inlineComponent]++;
+ }
}
// filter out deferred types
- {
+ for (const auto &inlineComponent : m_inlineComponentNames) {
QList<QQmlJSScope::ConstPtr> filteredQmlTypesWithQmlBases;
- filteredQmlTypesWithQmlBases.reserve(m_qmlTypesWithQmlBases.size());
- std::copy_if(m_qmlTypesWithQmlBases.cbegin(), m_qmlTypesWithQmlBases.cend(),
+ QList<QQmlJSScope::ConstPtr> &unfilteredQmlTypesWithQmlBases =
+ m_qmlTypesWithQmlBases[inlineComponent];
+ filteredQmlTypesWithQmlBases.reserve(unfilteredQmlTypesWithQmlBases.size());
+ std::copy_if(unfilteredQmlTypesWithQmlBases.cbegin(), unfilteredQmlTypesWithQmlBases.cend(),
std::back_inserter(filteredQmlTypesWithQmlBases),
[&](const QQmlJSScope::ConstPtr &type) { return !isOrUnderDeferred(type); });
- qSwap(m_qmlTypesWithQmlBases, filteredQmlTypesWithQmlBases);
+ qSwap(unfilteredQmlTypesWithQmlBases, filteredQmlTypesWithQmlBases);
}
// count QmlIR::Objects in the document - the amount is used to calculate
// object indices of implicit components
- int qmlScopeCount = 0;
+ QHash<InlineComponentOrDocumentRootName, qsizetype> qmlScopeCount;
const auto countQmlScopes = [&](const QQmlJSScope::ConstPtr &scope) {
if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope
return;
@@ -504,7 +530,7 @@ void QmltcVisitor::postVisitResolve(
case QQmlJSScope::QMLScope:
case QQmlJSScope::GroupedPropertyScope:
case QQmlJSScope::AttachedPropertyScope: {
- ++qmlScopeCount;
+ ++qmlScopeCount[scope->enclosingInlineComponentName()];
break;
}
default:
@@ -514,10 +540,9 @@ void QmltcVisitor::postVisitResolve(
};
// order doesn't matter (so re-use QQmlJSUtils)
QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, countQmlScopes);
- Q_ASSERT(qmlScopeCount >= int(m_qmlTypes.size()));
// figure synthetic indices of QQmlComponent-wrapped types
- int syntheticCreationIndex = 0;
+ int syntheticCreationIndex;
const auto addSyntheticIndex = [&](const QQmlJSScope::ConstPtr &type) {
// explicit component
if (isExplicitComponent(type)) {
@@ -526,30 +551,40 @@ void QmltcVisitor::postVisitResolve(
}
// implicit component
if (isImplicitComponent(type)) {
- const int index = int(qmlScopeCount) + syntheticCreationIndex++;
+ const int index =
+ qmlScopeCount[type->enclosingInlineComponentName()] + syntheticCreationIndex++;
m_syntheticTypeIndices[type] = index;
return true;
}
return false;
};
- iterateTypes(m_exportedRootScope, qmlIrOrderedBindings, addSyntheticIndex);
+
+ for (const auto &inlineComponentName : m_inlineComponentNames) {
+ syntheticCreationIndex = 0; // reset for each inline component
+ iterateTypes(m_inlineComponents[inlineComponentName], qmlIrOrderedBindings,
+ addSyntheticIndex);
+ }
// figure runtime object ids for non-component wrapped types
- int currentId = 0;
+ int currentId;
const auto setRuntimeId = [&](const QQmlJSScope::ConstPtr &type) {
// any type wrapped in an implicit component shouldn't be processed
// here. even if it has id, it doesn't need to be set by qmltc
- if (type->isComponentRootElement())
+ if (type->isComponentRootElement()) {
return true;
+ }
- if (m_typesWithId.contains(type))
+ if (m_typesWithId.contains(type)) {
m_typesWithId[type] = currentId++;
+ }
- // otherwise we need to `return true` here
- Q_ASSERT(!type->isInlineComponent());
return false;
};
- iterateTypes(m_exportedRootScope, qmlIrOrderedBindings, setRuntimeId);
+
+ for (const auto &inlineComponentName : m_inlineComponentNames) {
+ currentId = 0; // reset for each inline component
+ iterateTypes(m_inlineComponents[inlineComponentName], qmlIrOrderedBindings, setRuntimeId);
+ }
}
static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedAlias &origin)
diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h
index 04f18b96d5..0ec9349527 100644
--- a/tools/qmltc/qmltcvisitor.h
+++ b/tools/qmltc/qmltcvisitor.h
@@ -14,6 +14,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QmltcVisitor : public QQmlJSImportVisitor
{
void findCppIncludes();
@@ -25,6 +27,8 @@ class QmltcVisitor : public QQmlJSImportVisitor
QString sourceDirectoryPath(const QString &path);
+ using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
+
public:
QmltcVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory,
@@ -44,7 +48,11 @@ public:
void endVisit(QQmlJS::AST::UiProgram *) override;
- QList<QQmlJSScope::ConstPtr> qmlTypesWithQmlBases() const { return m_qmlTypesWithQmlBases; }
+ QList<QQmlJSScope::ConstPtr>
+ qmlTypesWithQmlBases(const InlineComponentOrDocumentRootName &inlinedComponentName) const
+ {
+ return m_qmlTypesWithQmlBases.value(inlinedComponentName);
+ }
QSet<QString> cppIncludeFiles() const { return m_cppIncludes; }
qsizetype creationIndex(const QQmlJSScope::ConstPtr &type) const
@@ -53,7 +61,10 @@ public:
return m_creationIndices.value(type, -1);
}
- qsizetype typeCount() const { return m_creationIndices.size(); }
+ qsizetype typeCount(const InlineComponentOrDocumentRootName &inlineComponent) const
+ {
+ return m_inlineComponentTypeCount.value(inlineComponent);
+ }
qsizetype qmlComponentIndex(const QQmlJSScope::ConstPtr &type) const
{
@@ -65,7 +76,7 @@ public:
{
Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
Q_ASSERT(m_qmlIrObjectIndices.contains(type));
- return m_qmlIrObjectIndices[type];
+ return m_qmlIrObjectIndices.value(type, -1);
}
/*! \internal
@@ -85,12 +96,32 @@ public:
QList<QQmlJSScope::ConstPtr> allQmlTypes() const { return qmlTypes(); }
/*! \internal
- Returns encountered QML types which return \c false in
- \c{isComponentRootElement()}. Called "pure", because these are the ones
+ Returns QML types which return \c false in
+ \c{isComponentRootElement()}. The QHash key are the enclosing inline component
+ or the root document name when not beloning to any inline component.
+ Called "pure", because these are the ones
that are not wrapped into QQmlComponent. Pure QML types can be created
through direct constructor invocation.
*/
- QList<QQmlJSScope::ConstPtr> pureQmlTypes() const { return m_pureQmlTypes; }
+ QList<QQmlJSScope::ConstPtr>
+ pureQmlTypes(const InlineComponentOrDocumentRootName &inlineComponent) const
+ {
+ return m_pureQmlTypes[inlineComponent];
+ }
+
+ /*!
+ * \internal
+ * Returns a list of the inline components. This list ends with the document root.
+ */
+ QList<InlineComponentOrDocumentRootName> inlineComponentNames() const
+ {
+ return m_inlineComponentNames;
+ }
+ QQmlJSScope::ConstPtr
+ inlineComponent(const InlineComponentOrDocumentRootName &inlineComponentName) const
+ {
+ return m_inlineComponents.value(inlineComponentName);
+ }
/*! \internal
Returns \c true when \a type has deferred bindings. Returns \c false
@@ -107,11 +138,41 @@ public:
protected:
QStringList m_qmlTypeNames; // names of QML types arranged as a stack
QHash<QString, int> m_qmlTypeNameCounts;
- QList<QQmlJSScope::ConstPtr> m_qmlTypesWithQmlBases; // QML types with composite/QML base types
+ /*!
+ * \internal
+ * QML types with composite/QML base types, mapped from inline component name to types
+ */
+ QHash<InlineComponentOrDocumentRootName, QList<QQmlJSScope::ConstPtr>> m_qmlTypesWithQmlBases;
QSet<QString> m_cppIncludes; // all C++ includes found from QQmlJSScope hierarchy
- QList<QQmlJSScope::ConstPtr> m_pureQmlTypes; // the ones not under QQmlComponent
-
+ QHash<InlineComponentOrDocumentRootName, QList<QQmlJSScope::ConstPtr>>
+ m_pureQmlTypes; // the ones not under QQmlComponent
+ /*!
+ * \internal
+ * List of the names of the inline components, useful when iterating over QHash that
+ * uses those names as keys. Ends with the the document root.
+ */
+ QList<InlineComponentOrDocumentRootName> m_inlineComponentNames;
+ /*!
+ * \internal
+ * Map inline component names to the corresponding type, and the document root
+ * name to all types not belonging to any inline component.
+ */
+ QHash<InlineComponentOrDocumentRootName, QQmlJSScope::Ptr> m_inlineComponents;
+ /*!
+ * \internal
+ * Map types to their creation indices. Childrens are stored at their creation index in
+ * a QObject* array either in the document root or in the inline component they belong to.
+ * Therefore two types in the same file might have the same creation index, if they belong
+ * to different inline components.
+ */
QHash<QQmlJSScope::ConstPtr, qsizetype> m_creationIndices;
+ /*!
+ * \internal
+ * Counts the types (pure qml types and explicit/implicit components) per inline component.
+ * Needed to set the size of the QObject* array in the document root or the inline component
+ * they belong to.
+ */
+ QHash<InlineComponentOrDocumentRootName, qsizetype> m_inlineComponentTypeCount;
QHash<QQmlJSScope::ConstPtr, qsizetype> m_syntheticTypeIndices;
QHash<QQmlJSScope::ConstPtr, qsizetype> m_qmlIrObjectIndices;