diff options
59 files changed, 1529 insertions, 160 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index 453ade09a7..42c476763e 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -75,7 +75,7 @@ QT_BEGIN_NAMESPACE // Also change the comment behind the number to describe the latest change. This has the added // benefit that if another patch changes the version too, it will result in a merge conflict, and // not get removed silently. -#define QV4_DATA_STRUCTURE_VERSION 0x27 // resolved merge +#define QV4_DATA_STRUCTURE_VERSION 0x28// support inline components class QIODevice; class QQmlTypeNameCache; @@ -112,6 +112,7 @@ struct TableIterator int index; const ItemType *operator->() { return (container->*IndexedGetter)(index); } + ItemType operator*() {return *operator->();} void operator++() { ++index; } bool operator==(const TableIterator &rhs) const { return index == rhs.index; } bool operator!=(const TableIterator &rhs) const { return index != rhs.index; } @@ -597,6 +598,15 @@ struct Binding static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +struct InlineComponent +{ + quint32_le objectIndex; + quint32_le nameIndex; + Location location; +}; + +static_assert(sizeof(InlineComponent) == 12, "InlineComponent structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + struct EnumValue { quint32_le nameIndex; @@ -721,7 +731,9 @@ struct Object NoFlag = 0x0, IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary HasDeferredBindings = 0x2, // any of the bindings are deferred - HasCustomParserBindings = 0x4 + HasCustomParserBindings = 0x4, + IsInlineComponentRoot = 0x8, + InPartOfInlineComponent = 0x10 }; // Depending on the use, this may be the type name to instantiate before instantiating this @@ -751,12 +763,15 @@ struct Object quint32_le offsetToNamedObjectsInComponent; Location location; Location locationOfIdProperty; + quint32_le offsetToInlineComponents; + quint16_le nInlineComponents; // Function[] // Property[] // Signal[] // Binding[] +// InlineComponent[] - static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent) + static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent, int nInlineComponents) { return ( sizeof(Object) + nFunctions * sizeof(quint32) @@ -766,6 +781,7 @@ struct Object + nSignals * sizeof(quint32) + nBindings * sizeof(Binding) + nNamedObjectsInComponent * sizeof(int) + + nInlineComponents * sizeof(InlineComponent) + 0x7 ) & ~0x7; } @@ -804,11 +820,21 @@ struct Object return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset); } + const InlineComponent *inlineComponentAt(int idx) const + { + return inlineComponentTable() + idx; + } + const quint32_le *namedObjectsInComponentTable() const { return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent); } + const InlineComponent *inlineComponentTable() const + { + return reinterpret_cast<const InlineComponent*>(reinterpret_cast<const char *>(this) + offsetToInlineComponents); + } + // --- QQmlPropertyCacheCreator interface int propertyCount() const { return nProperties; } int aliasCount() const { return nAliases; } @@ -833,17 +859,22 @@ struct Object SignalIterator signalsBegin() const { return SignalIterator(this, 0); } SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } + typedef TableIterator<InlineComponent, Object, &Object::inlineComponentAt> InlineComponentIterator; + InlineComponentIterator inlineComponentsBegin() const {return InlineComponentIterator(this, 0);} + InlineComponentIterator inlineComponentsEnd() const {return InlineComponentIterator(this, nInlineComponents);} + int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } // --- }; -static_assert(sizeof(Object) == 68, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Object) == 76, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Import { enum ImportType : unsigned int { ImportLibrary = 0x1, ImportFile = 0x2, - ImportScript = 0x3 + ImportScript = 0x3, + ImportInlineComponent = 0x4 }; quint32_le type; @@ -1072,7 +1103,7 @@ struct TypeReferenceMap : QHash<int, TypeReference> } auto prop = obj->propertiesBegin(); - auto propEnd = obj->propertiesEnd(); + auto const propEnd = obj->propertiesEnd(); for ( ; prop != propEnd; ++prop) { if (!prop->isBuiltinType) { TypeReference &r = this->add(prop->builtinTypeOrTypeNameIndex, prop->location); @@ -1081,11 +1112,17 @@ struct TypeReferenceMap : QHash<int, TypeReference> } auto binding = obj->bindingsBegin(); - auto bindingEnd = obj->bindingsEnd(); + auto const bindingEnd = obj->bindingsEnd(); for ( ; binding != bindingEnd; ++binding) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) this->add(binding->propertyNameIndex, binding->location); } + + auto ic = obj->inlineComponentsBegin(); + auto const icEnd = obj->inlineComponentsEnd(); + for (; ic != icEnd; ++ic) { + this->add(ic->nameIndex, ic->location); + } } template <typename Iterator> diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 9623d2ed58..811f88cb73 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -161,6 +161,7 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons bindings = pool->New<PoolList<Binding> >(); functions = pool->New<PoolList<Function> >(); functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >(); + inlineComponents = pool->New<PoolList<InlineComponent>>(); declarationsOverride = nullptr; } @@ -281,6 +282,11 @@ void Object::appendFunction(QmlIR::Function *f) target->functions->append(f); } +void Object::appendInlineComponent(InlineComponent *ic) +{ + inlineComponents->append(ic); +} + QString Object::appendBinding(Binding *b, bool isListBinding) { const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0)); @@ -503,6 +509,33 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectDefinition *node) return false; } +bool IRBuilder::visit(QQmlJS::AST::UiInlineComponent *ast) +{ + int idx = -1; + if (insideInlineComponent) { + recordError(ast->firstSourceLocation(), QLatin1String("Nested inline components are not supported")); + return false; + } + { + QScopedValueRollback<bool> rollBack {insideInlineComponent, true}; + if (!defineQMLObject(&idx, ast->component)) + return false; + } + Q_ASSERT(idx > 0); + Object* definedObject = _objects.at(idx); + definedObject->flags |= QV4::CompiledData::Object::IsInlineComponentRoot; + definedObject->flags |= QV4::CompiledData::Object::InPartOfInlineComponent; + definedObject->isInlineComponent = true; + auto inlineComponent = New<InlineComponent>(); + inlineComponent->nameIndex = registerString(ast->name.toString()); + inlineComponent->objectIndex = idx; + auto location = ast->firstSourceLocation(); + inlineComponent->location.line = location.startLine; + inlineComponent->location.column = location.startColumn; + _object->appendInlineComponent(inlineComponent); + return false; +} + bool IRBuilder::visit(QQmlJS::AST::UiObjectBinding *node) { int idx = 0; @@ -597,12 +630,16 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu } Object *obj = New<Object>(); + _objects.append(obj); *objectIndex = _objects.size() - 1; qSwap(_object, obj); _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, location); _object->declarationsOverride = declarationsOverride; + if (insideInlineComponent) { + _object->flags |= QV4::CompiledData::Object::InPartOfInlineComponent; + } // A new object is also a boundary for property declarations. Property *declaration = nullptr; @@ -1553,7 +1590,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen uint nextOffset = objectOffset + objectOffsetTableSize; for (Object *o : qAsConst(output.objects)) { objectOffsets.insert(o, nextOffset); - nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size()); + nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size(), o->inlineComponentCount()); int signalTableSize = 0; for (const Signal *s = o->firstSignal(); s; s = s->next) @@ -1632,6 +1669,10 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen objectToWrite->offsetToNamedObjectsInComponent = nextOffset; nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32); + objectToWrite->nInlineComponents = o->inlineComponentCount(); + objectToWrite->offsetToInlineComponents = nextOffset; + nextOffset += objectToWrite->nInlineComponents * sizeof (QV4::CompiledData::InlineComponent); + quint32_le *functionsTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToFunctions); for (const Function *f = o->firstFunction(); f; f = f->next) *functionsTable++ = o->runtimeFunctionIndices.at(f->index); @@ -1703,6 +1744,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen for (int i = 0; i < o->namedObjectsInComponent.size(); ++i) { *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); } + + char *inlineComponentPtr = objectPtr + objectToWrite->offsetToInlineComponents; + for (auto it = o->inlineComponentsBegin(); it != o->inlineComponentsEnd(); ++it) { + const InlineComponent *ic = it.ptr; + QV4::CompiledData::InlineComponent *icToWrite = reinterpret_cast<QV4::CompiledData::InlineComponent*>(inlineComponentPtr); + *icToWrite = *ic; + inlineComponentPtr += sizeof(QV4::CompiledData::InlineComponent); + } } if (!output.javaScriptCompilationUnit.data) { @@ -1850,12 +1899,14 @@ bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots bool JSCodeGen::compileComponent(int contextObject) { const QmlIR::Object *obj = document->objects.at(contextObject); - if (obj->flags & QV4::CompiledData::Object::IsComponent) { + if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) { Q_ASSERT(obj->bindingCount() == 1); const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); contextObject = componentBinding->value.objectIndex; } + for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) + compileComponent(it->objectIndex); return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject); } @@ -1863,7 +1914,7 @@ bool JSCodeGen::compileComponent(int contextObject) bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) { QmlIR::Object *object = document->objects.at(objectIndex); - if (object->flags & QV4::CompiledData::Object::IsComponent) + if (object->flags & QV4::CompiledData::Object::IsComponent && !object->isInlineComponent) return true; if (object->functionsAndExpressions->count > 0) { diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index ab0ddf6ef8..d4f2eb8dd4 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -275,6 +275,11 @@ struct Binding : public QV4::CompiledData::Binding Binding *next; }; +struct InlineComponent : public QV4::CompiledData::InlineComponent +{ + InlineComponent *next; +}; + struct Alias : public QV4::CompiledData::Alias { Alias *next; @@ -316,6 +321,7 @@ public: int id; int indexOfDefaultPropertyOrAlias; bool defaultPropertyIsAlias; + bool isInlineComponent = false; quint32 flags; QV4::CompiledData::Location location; @@ -333,6 +339,8 @@ public: int bindingCount() const { return bindings->count; } const Function *firstFunction() const { return functions->first; } int functionCount() const { return functions->count; } + const InlineComponent *inlineComponent() const { return inlineComponents->first; } + int inlineComponentCount() const { return inlineComponents->count; } PoolList<Binding>::Iterator bindingsBegin() const { return bindings->begin(); } PoolList<Binding>::Iterator bindingsEnd() const { return bindings->end(); } @@ -346,6 +354,8 @@ public: PoolList<Signal>::Iterator signalsEnd() const { return qmlSignals->end(); } PoolList<Function>::Iterator functionsBegin() const { return functions->begin(); } PoolList<Function>::Iterator functionsEnd() const { return functions->end(); } + PoolList<InlineComponent>::Iterator inlineComponentsBegin() const { return inlineComponents->begin(); } + PoolList<InlineComponent>::Iterator inlineComponentsEnd() const { return inlineComponents->end(); } // If set, then declarations for this object (and init bindings for these) should go into the // specified object. Used for declarations inside group properties. @@ -358,6 +368,7 @@ public: QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); void appendFunction(QmlIR::Function *f); + void appendInlineComponent(InlineComponent *ic); QString appendBinding(Binding *b, bool isListBinding); Binding *findBinding(quint32 nameIndex) const; @@ -381,6 +392,7 @@ private: PoolList<Signal> *qmlSignals; PoolList<Binding> *bindings; PoolList<Function> *functions; + PoolList<InlineComponent> *inlineComponents; }; struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma @@ -409,6 +421,9 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT Document int registerString(const QString &str) { return jsGenerator.registerString(str); } QString stringAt(int index) const { return jsGenerator.stringForIndex(index); } + + int objectCount() const {return objects.size();} + Object* objectAt(int i) const {return objects.at(i);} }; class Q_QMLCOMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives @@ -449,6 +464,7 @@ public: bool visit(QQmlJS::AST::UiArrayBinding *ast) override; bool visit(QQmlJS::AST::UiObjectBinding *ast) override; bool visit(QQmlJS::AST::UiObjectDefinition *ast) override; + bool visit(QQmlJS::AST::UiInlineComponent *ast) override; bool visit(QQmlJS::AST::UiEnumDeclaration *ast) override; bool visit(QQmlJS::AST::UiPublicMember *ast) override; bool visit(QQmlJS::AST::UiScriptBinding *ast) override; @@ -527,6 +543,8 @@ public: QQmlJS::MemoryPool *pool; QString sourceCode; QV4::Compiler::JSUnitGenerator *jsGenerator; + + bool insideInlineComponent = false; }; struct Q_QMLCOMPILER_PRIVATE_EXPORT QmlUnitGenerator diff --git a/src/qml/inlinecomponentutils_p.h b/src/qml/inlinecomponentutils_p.h new file mode 100644 index 0000000000..99b28349cd --- /dev/null +++ b/src/qml/inlinecomponentutils_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef INLINECOMPONENTUTILS_P_H +#define INLINECOMPONENTUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4compileddata_p.h> +#include <private/qv4executablecompilationunit_p.h> + +namespace icutils { +struct Node { + Node() = default; + Node(const Node &) = default; + Node(Node &&) = default; + Node& operator=(Node const &) = default; + Node& operator=(Node &&) = default; + bool operator==(Node const &other) const {return index == other.index;} + + Node(std::vector<QV4::CompiledData::InlineComponent>::size_type s) { + index = quint32(s); + temporaryMark = 0; + permanentMark = 0; + } + + union { + quint32_le_bitfield<0, 30> index; + quint32_le_bitfield<30, 1> temporaryMark; + quint32_le_bitfield<31, 1> permanentMark; + }; +}; + +using AdjacencyList = std::vector<std::vector<Node*>>; + +template<typename ObjectContainer, typename InlineComponent> +void fillAdjacencyListForInlineComponents(ObjectContainer *objectContainer, AdjacencyList &adjacencyList, std::vector<Node> &nodes, const std::vector<InlineComponent> &allICs) { + using CompiledObject = typename ObjectContainer::CompiledObject; + // add an edge from A to B if A and B are inline components with the same containing type + // and A inherits from B (ignore indirect chains through external types for now) + // or if A instantiates B + for (typename std::vector<InlineComponent>::size_type i = 0; i < allICs.size(); ++i) { + const auto& ic = allICs[i]; + const CompiledObject *obj = objectContainer->objectAt(ic.objectIndex); + QV4::ResolvedTypeReference *currentICTypeRef = objectContainer->resolvedType(ic.nameIndex); + auto createEdgeFromTypeRef = [&](QV4::ResolvedTypeReference *targetTypeRef) { + if (targetTypeRef && targetTypeRef->type.isInlineComponentType()) { + if (targetTypeRef->type.containingType() == currentICTypeRef->type.containingType()) { + auto icIt = std::find_if(allICs.cbegin(), allICs.cend(), [&](const QV4::CompiledData::InlineComponent &icSearched){ + return int(icSearched.objectIndex) == targetTypeRef->type.inlineComponentObjectId(); + }); + Q_ASSERT(icIt != allICs.cend()); + Node& target = nodes[i]; + adjacencyList[std::distance(allICs.cbegin(), icIt)].push_back(&target); + } + } + }; + if (obj->inheritedTypeNameIndex != 0) { + QV4::ResolvedTypeReference *parentTypeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); + createEdgeFromTypeRef(parentTypeRef); + + } + auto referencedInICObjectIndex = ic.objectIndex + 1; + while (int(referencedInICObjectIndex) < objectContainer->objectCount()) { + auto potentiallyReferencedInICObject = objectContainer->objectAt(referencedInICObjectIndex); + bool stillInIC = !(potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::IsInlineComponentRoot) + && (potentiallyReferencedInICObject-> flags & QV4::CompiledData::Object::InPartOfInlineComponent); + if (!stillInIC) + break; + createEdgeFromTypeRef(objectContainer->resolvedType(potentiallyReferencedInICObject->inheritedTypeNameIndex)); + ++referencedInICObjectIndex; + } + } +}; + +inline void topoVisit(Node *node, AdjacencyList &adjacencyList, bool &hasCycle, std::vector<Node> &nodesSorted) { + if (node->permanentMark) + return; + if (node->temporaryMark) { + hasCycle = true; + return; + } + node->temporaryMark = 1; + + auto const &edges = adjacencyList[node->index]; + for (auto edgeTarget =edges.begin(); edgeTarget != edges.end(); ++edgeTarget) { + topoVisit(*edgeTarget, adjacencyList, hasCycle, nodesSorted); + } + + node->temporaryMark = 0; + node->permanentMark = 1; + nodesSorted.push_back(*node); +}; + +// Use DFS based topological sorting (https://en.wikipedia.org/wiki/Topological_sorting) +inline std::vector<Node> topoSort(std::vector<Node> &nodes, AdjacencyList &adjacencyList, bool &hasCycle) { + std::vector<Node> nodesSorted; + nodesSorted.reserve(nodes.size()); + + hasCycle = false; + auto currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) { + return node.permanentMark == 0; + }); + // Do a topological sort of all inline components + // afterwards, nodesSorted contains the nodes for the inline components in reverse topological order + while (currentNodeIt != nodes.end() && !hasCycle) { + Node& currentNode = *currentNodeIt; + topoVisit(¤tNode, adjacencyList, hasCycle, nodesSorted); + currentNodeIt = std::find_if(nodes.begin(), nodes.end(), [](const Node& node) { + return node.permanentMark == 0; + }); + } + return nodesSorted; +} +} + +#endif // INLINECOMPONENTUTILS_P_H diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index b1e2879bba..fa4a1f1ce4 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -53,6 +53,7 @@ #include <private/qv4compilationunitmapper_p.h> #include <private/qml_compile_hash_p.h> #include <private/qqmltypewrapper_p.h> +#include <private/inlinecomponentutils_p.h> #include <QtQml/qqmlfile.h> #include <QtQml/qqmlpropertymap.h> @@ -62,6 +63,7 @@ #include <QtCore/qfileinfo.h> #include <QtCore/qscopeguard.h> #include <QtCore/qcryptographichash.h> +#include <QtCore/QScopedValueRollback> #if defined(QML_COMPILE_HASH) # ifdef Q_OS_LINUX @@ -387,7 +389,7 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); } -void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine, QQmlMetaType::CompositeMetaTypeIds typeIds) +void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds typeIds) { this->qmlEngine = qmlEngine; @@ -399,6 +401,7 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi metaTypeId = typeIds.id; listMetaTypeId = typeIds.listId; qmlEngine->registerInternalCompositeType(this); + } else { const QV4::CompiledData::Object *obj = objectAt(/*root object*/0); auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); @@ -413,29 +416,105 @@ void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngi } // Collect some data for instantiation later. + using namespace icutils; + std::vector<QV4::CompiledData::InlineComponent> allICs {}; + for (int i=0; i != objectCount(); ++i) { + const CompiledObject *obj = objectAt(i); + for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) { + allICs.push_back(*it); + } + } + std::vector<Node> nodes; + nodes.resize(allICs.size()); + std::iota(nodes.begin(), nodes.end(), 0); + AdjacencyList adjacencyList; + adjacencyList.resize(nodes.size()); + fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs); + bool hasCycle = false; + auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle); + Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator + + // We need to first iterate over all inline components, as the containing component might create instances of them + // and in that case we need to add its object count + for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) { + const auto &ic = allICs.at(nodeIt->index); + int lastICRoot = ic.objectIndex; + for (int i = ic.objectIndex; i<objectCount(); ++i) { + const QV4::CompiledData::Object *obj = objectAt(i); + bool leftCurrentInlineComponent = + (i != lastICRoot && obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot) + || !(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent); + if (leftCurrentInlineComponent) + break; + inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings; + + if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1) + ++inlineComponentData[lastICRoot].totalParserStatusCount; + + ++inlineComponentData[lastICRoot].totalObjectCount; + if (typeRef->compilationUnit) { + // if the type is an inline component type, we have to extract the information from it + // This requires that inline components are visited in the correct order + auto icRoot = typeRef->compilationUnit->icRoot; + if (typeRef->type.isInlineComponentType()) { + icRoot = typeRef->type.inlineComponendId(); + } + QScopedValueRollback<int> rollback {typeRef->compilationUnit->icRoot, icRoot}; + inlineComponentData[lastICRoot].totalBindingCount += typeRef->compilationUnit->totalBindingsCount(); + inlineComponentData[lastICRoot].totalParserStatusCount += typeRef->compilationUnit->totalParserStatusCount(); + inlineComponentData[lastICRoot].totalObjectCount += typeRef->compilationUnit->totalObjectCount(); + } + } + } + } int bindingCount = 0; int parserStatusCount = 0; int objectCount = 0; for (quint32 i = 0, count = this->objectCount(); i < count; ++i) { const QV4::CompiledData::Object *obj = objectAt(i); + if (obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) { + continue; + } bindingCount += obj->nBindings; if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) { - if (typeRef->type.parserStatusCast() != -1) - ++parserStatusCount; - } + if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1) + ++parserStatusCount; ++objectCount; if (typeRef->compilationUnit) { - bindingCount += typeRef->compilationUnit->totalBindingsCount; - parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; - objectCount += typeRef->compilationUnit->totalObjectCount; + auto icRoot = typeRef->compilationUnit->icRoot; + if (typeRef->type.isInlineComponentType()) { + icRoot = typeRef->type.inlineComponendId(); + } + QScopedValueRollback<int> rollback {typeRef->compilationUnit->icRoot, icRoot}; + bindingCount += typeRef->compilationUnit->totalBindingsCount(); + parserStatusCount += typeRef->compilationUnit->totalParserStatusCount(); + objectCount += typeRef->compilationUnit->totalObjectCount(); } } } - totalBindingsCount = bindingCount; - totalParserStatusCount = parserStatusCount; - totalObjectCount = objectCount; + m_totalBindingsCount = bindingCount; + m_totalParserStatusCount = parserStatusCount; + m_totalObjectCount = objectCount; +} + +int ExecutableCompilationUnit::totalBindingsCount() const { + if (icRoot == -1) + return m_totalBindingsCount; + return inlineComponentData[icRoot].totalBindingCount; +} + +int ExecutableCompilationUnit::totalObjectCount() const { + if (icRoot == -1) + return m_totalObjectCount; + return inlineComponentData[icRoot].totalObjectCount; +} + +int ExecutableCompilationUnit::totalParserStatusCount() const { + if (icRoot == -1) + return m_totalParserStatusCount; + return inlineComponentData[icRoot].totalParserStatusCount; } bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const @@ -453,6 +532,13 @@ bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentType sizeof(data->dependencyMD5Checksum)) == 0; } +CompositeMetaTypeIds ExecutableCompilationUnit::typeIdsForComponent(int objectid) const +{ + if (objectid == 0) + return {metaTypeId, listMetaTypeId}; + return inlineComponentData[objectid].typeIds; +} + QStringList ExecutableCompilationUnit::moduleRequests() const { QStringList requests; @@ -734,13 +820,14 @@ QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQm typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject(), minorVersion); return typePropertyCache; } else { + Q_ASSERT(compilationUnit); return compilationUnit->rootPropertyCache(); } } bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine) { - if (type.isValid()) { + if (type.isValid() && !type.isInlineComponentType()) { bool ok = false; hash->addData(createPropertyCache(engine)->checksum(&ok)); return ok; diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index 1272e7a2c3..8cad18a3dc 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -64,6 +64,26 @@ QT_BEGIN_NAMESPACE class QQmlScriptData; class QQmlEnginePrivate; + +struct InlineComponentData { + + InlineComponentData() = default; + InlineComponentData(const CompositeMetaTypeIds &typeIds, int objectIndex, int nameIndex, int totalObjectCount, int totalBindingCount, int totalParserStatusCount) + : typeIds(typeIds) + , objectIndex(objectIndex) + , nameIndex(nameIndex) + , totalObjectCount(totalObjectCount) + , totalBindingCount(totalBindingCount) + , totalParserStatusCount(totalParserStatusCount) {} + + CompositeMetaTypeIds typeIds; + int objectIndex = -1; + int nameIndex = -1; + int totalObjectCount = 0; + int totalBindingCount = 0; + int totalParserStatusCount = 0; +}; + namespace QV4 { // index is per-object binding index @@ -143,11 +163,16 @@ public: QHash<int, IdentifierHash> namedObjectsPerComponentCache; inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine, QQmlMetaType::CompositeMetaTypeIds typeIds); + void finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds typeIdsForComponent); - int totalBindingsCount = 0; // Number of bindings used in this type - int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount = 0; // Number of objects explicitly instantiated + int m_totalBindingsCount = 0; // Number of bindings used in this type + int m_totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses + int m_totalObjectCount = 0; // Number of objects explicitly instantiated + int icRoot = -1; + + int totalBindingsCount() const; + int totalParserStatusCount() const; + int totalObjectCount() const; QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; ResolvedTypeReferenceMap resolvedTypes; @@ -155,12 +180,14 @@ public: bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const; - QQmlMetaType::CompositeMetaTypeIds typeIds() const { return {metaTypeId, listMetaTypeId}; } + CompositeMetaTypeIds typeIdsForComponent(int objectid = 0) const; int metaTypeId = -1; int listMetaTypeId = -1; bool isRegisteredWithEngine = false; + QHash<int, InlineComponentData> inlineComponentData; + QScopedPointer<CompilationUnitMapper> backingFile; // --- interface for QQmlPropertyCacheCreator diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 6fbb9df164..397c0b51a5 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -91,6 +91,7 @@ %token T_EXPORT "export" %token T_FROM "from" %token T_REQUIRED "required" +%token T_COMPONENT "component" --- template strings %token T_NO_SUBSTITUTION_TEMPLATE"(no subst template)" @@ -123,7 +124,7 @@ %token T_FOR_LOOKAHEAD_OK "(for lookahead ok)" --%left T_PLUS T_MINUS -%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET T_OF T_STATIC T_FROM T_AS T_REQUIRED +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET T_OF T_STATIC T_FROM T_AS T_REQUIRED T_COMPONENT %nonassoc REDUCE_HERE %right T_THEN T_ELSE %right T_WITHOUTAS T_AS @@ -1426,6 +1427,19 @@ UiObjectMember: T_ENUM T_IDENTIFIER T_LBRACE EnumMemberList T_RBRACE; } ./ +UiObjectMember: T_COMPONENT T_IDENTIFIER T_COLON UiObjectDefinition; +/. + case $rule_number: { + if (!stringRef(2).front().isUpper()) { + diagnostic_messages.append(compileError(loc(2), + QLatin1String("Type name must be upper case"), QtWarningMsg)); + } + auto inlineComponent = new (pool) AST::UiInlineComponent(stringRef(2), sym(4).UiObjectDefinition); + inlineComponent->componentToken = loc(1); + sym(1).Node = inlineComponent; + } break; +./ + EnumMemberList: T_IDENTIFIER; /. case $rule_number: { @@ -1468,29 +1482,31 @@ EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_NUMERIC_LITERAL; } ./ -QmlIdentifier: T_IDENTIFIER; -QmlIdentifier: T_PROPERTY; -QmlIdentifier: T_SIGNAL; -QmlIdentifier: T_READONLY; -QmlIdentifier: T_ON; -QmlIdentifier: T_GET; -QmlIdentifier: T_SET; -QmlIdentifier: T_FROM; -QmlIdentifier: T_OF; -QmlIdentifier: T_REQUIRED; - -JsIdentifier: T_IDENTIFIER; -JsIdentifier: T_PROPERTY; -JsIdentifier: T_SIGNAL; -JsIdentifier: T_READONLY; -JsIdentifier: T_ON; -JsIdentifier: T_GET; -JsIdentifier: T_SET; -JsIdentifier: T_FROM; -JsIdentifier: T_STATIC; -JsIdentifier: T_OF; -JsIdentifier: T_AS; -JsIdentifier: T_REQUIRED; +QmlIdentifier: T_IDENTIFIER + | T_PROPERTY + | T_SIGNAL + | T_READONLY + | T_ON + | T_GET + | T_SET + | T_FROM + | T_OF + | T_REQUIRED + | T_COMPONENT; + +JsIdentifier: T_IDENTIFIER + | T_PROPERTY + | T_SIGNAL + | T_READONLY + | T_ON + | T_GET + | T_SET + | T_FROM + | T_STATIC + | T_OF + | T_AS + | T_REQUIRED + | T_COMPONENT; IdentifierReference: JsIdentifier; BindingIdentifier: IdentifierReference; diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 03355b3e38..aa3e8ab5e3 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -1545,6 +1545,15 @@ void Type::toString(QString *out) const }; } +void UiInlineComponent::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(component, visitor); + } + + visitor->endVisit(this); +} + } } // namespace QQmlJS::AST QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index aa355fed85..48a994cd33 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -244,6 +244,7 @@ public: Kind_UiImport, Kind_UiObjectBinding, Kind_UiObjectDefinition, + Kind_UiInlineComponent, Kind_UiObjectInitializer, Kind_UiObjectMemberList, Kind_UiArrayMemberList, @@ -3377,6 +3378,28 @@ public: UiObjectInitializer *initializer; }; +class QML_PARSER_EXPORT UiInlineComponent: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiInlineComponent) + + UiInlineComponent(const QStringRef& inlineComponentName, UiObjectDefinition* inlineComponent) + : name(inlineComponentName), component(inlineComponent) + { kind = K; } + + QStringRef name; + UiObjectDefinition* component; + SourceLocation componentToken; + + SourceLocation lastSourceLocation() const override + {return component->lastSourceLocation();} + + SourceLocation firstSourceLocation() const override + {return componentToken;} + + void accept0(Visitor *visitor) override; +}; + class QML_PARSER_EXPORT UiSourceElement: public UiObjectMember { public: diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index 05226fd043..8a8ee2dfae 100644 --- a/src/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/parser/qqmljsastfwd_p.h @@ -169,6 +169,7 @@ class UiImport; class UiPublicMember; class UiParameterList; class UiObjectDefinition; +class UiInlineComponent; class UiObjectInitializer; class UiObjectBinding; class UiScriptBinding; diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index 7146cd00ac..d6b92990ad 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -112,6 +112,7 @@ public: virtual bool visit(UiEnumDeclaration *) { return true; } virtual bool visit(UiEnumMemberList *) { return true; } virtual bool visit(UiVersionSpecifier *) { return true; } + virtual bool visit(UiInlineComponent *) { return true; } virtual void endVisit(UiProgram *) {} virtual void endVisit(UiImport *) {} @@ -131,6 +132,7 @@ public: virtual void endVisit(UiEnumDeclaration *) {} virtual void endVisit(UiEnumMemberList *) { } virtual void endVisit(UiVersionSpecifier *) {} + virtual void endVisit(UiInlineComponent *) {} // QQmlJS virtual bool visit(ThisExpression *) { return true; } diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index 3eb054341f..5f08cc4353 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -836,6 +836,25 @@ static inline int classify9(const QChar *s, int parseModeFlags) { } } } + else if (s[0].unicode() == 'c') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'm') { + if (s[3].unicode() == 'p') { + if (s[4].unicode() == 'o') { + if (s[5].unicode() == 'n') { + if (s[6].unicode() == 'e') { + if (s[7].unicode() == 'n') { + if (s[8].unicode() == 't') { + return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_COMPONENT) : int(Lexer::T_IDENTIFIER); + } + } + } + } + } + } + } + } + } return Lexer::T_IDENTIFIER; } diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 6d4b962d39..e39a8319b6 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -39,6 +39,7 @@ greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 3)| \ WERROR += -Wno-error=unused-const-variable HEADERS += qtqmlglobal.h \ + inlinecomponentutils_p.h \ qtqmlglobal_p.h \ qtqmlcompilerglobal.h \ qtqmlcompilerglobal_p.h diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 3345fcb19e..07d5429423 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2383,12 +2383,23 @@ int QQmlEnginePrivate::listType(int t) const return QQmlMetaType::listType(t); } + +static QQmlPropertyCache *propertyCacheForPotentialInlineComponentType(int t, const QHash<int, QV4::ExecutableCompilationUnit *>::const_iterator &iter) { + if (t != (*iter)->metaTypeId) { + // this is an inline component, and what we have in the iterator is currently the parent compilation unit + for (auto &&icDatum: (*iter)->inlineComponentData) + if (icDatum.typeIds.id == t) + return (*iter)->propertyCaches.at(icDatum.objectIndex); + } + return (*iter)->rootPropertyCache().data(); +} + QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const { Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache().data()); + return propertyCacheForPotentialInlineComponentType(t, iter); } else { QQmlType type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type.baseMetaObject()); @@ -2400,7 +2411,7 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache().data()); + return propertyCacheForPotentialInlineComponentType(t, iter); } else { QQmlType type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type.metaObject()); @@ -2412,7 +2423,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache().data(); + return propertyCacheForPotentialInlineComponentType(t, iter); } else { QQmlType type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2425,7 +2436,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe Locker locker(this); auto iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache().data(); + return propertyCacheForPotentialInlineComponentType(t, iter); } else { QQmlType type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2445,6 +2456,9 @@ void QQmlEnginePrivate::registerInternalCompositeType(QV4::ExecutableCompilation // The QQmlCompiledData is not referenced here, but it is removed from this // hash in the QQmlCompiledData destructor m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit); + for (auto &&data: compilationUnit->inlineComponentData) { + m_compositeTypes.insert(data.typeIds.id, compilationUnit); + } } void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) @@ -2453,6 +2467,14 @@ void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilati Locker locker(this); m_compositeTypes.remove(compilationUnit->metaTypeId); + for (auto&& icDatum: compilationUnit->inlineComponentData) + m_compositeTypes.remove(icDatum.typeIds.id); +} + +QV4::ExecutableCompilationUnit *QQmlEnginePrivate::obtainExecutableCompilationUnit(int typeId) +{ + Locker locker(this); + return m_compositeTypes.value(typeId, nullptr); } template<> diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 5b1b676c89..ed81e055e5 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -231,6 +231,7 @@ public: QQmlPropertyCache *rawPropertyCacheForType(int, int minorVersion = -1); void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); + QV4::ExecutableCompilationUnit *obtainExecutableCompilationUnit(int typeId); bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 7da0685872..80eafdf146 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -58,6 +58,7 @@ #include <private/qqmltypeloaderqmldircontent_p.h> #include <QtCore/qjsonobject.h> #include <QtCore/qjsonarray.h> +#include <QtQml/private/qqmltype_p_p.h> #include <algorithm> #include <functional> @@ -613,6 +614,8 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL-SINGLETON"; else if (type_return->isComposite()) RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL"; + else if (type_return->isInlineComponentType()) + RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE(INLINECOMPONENT)"; else RESOLVE_TYPE_DEBUG << type_return->typeName() << " TYPE"; } @@ -709,6 +712,37 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt } const QString typeStr = type.toString(); + if (isInlineComponent) { + Q_ASSERT(type_return); + bool ret = uri == typeStr; + if (ret) { + Q_ASSERT(!type_return->isValid()); + auto createICType = [&]() { + auto typePriv = new QQmlTypePrivate {QQmlType::RegistrationType::InlineComponentType}; + bool ok = false; + typePriv->extraData.id->objectId = QUrl(this->url).fragment().toInt(&ok); + Q_ASSERT(ok); + typePriv->extraData.id->url = QUrl(this->url); + auto icType = QQmlType(typePriv); + return icType; + }; + if (containingType.isValid()) { + // we currently cannot reference a Singleton inside itself + // in that case, containingType is still invalid + if (int icID = containingType.lookupInlineComponentIdByName(typeStr) != -1) { + *type_return = containingType.lookupInlineComponentById(icID); + } else { + auto icType = createICType(); + int placeholderId = containingType.generatePlaceHolderICId(); + const_cast<QQmlImportInstance*>(this)->containingType.associateInlineComponent(typeStr, placeholderId, CompositeMetaTypeIds {}, icType); + *type_return = QQmlType(icType); + } + } else { + *type_return = createICType(); + } + } + return ret; + } QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end(); if (it != end) { QString componentUrl; @@ -826,47 +860,107 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, QQmlType::RegistrationType registrationType, bool *typeRecursionDetected) { - QQmlImportNamespace *s = nullptr; - int dot = type.indexOf(Dot); - if (dot >= 0) { - QHashedStringRef namespaceName(type.constData(), dot); - s = findQualifiedNamespace(namespaceName); - if (!s) { - if (errors) { - QQmlError error; - error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName.toString())); - errors->prepend(error); - } - return false; - } - int ndot = type.indexOf(Dot,dot+1); - if (ndot > 0) { - if (errors) { - QQmlError error; - error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed")); - errors->prepend(error); - } - return false; - } - } else { - s = &unqualifiedset; - } - QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1); - if (s) { - if (s->resolveType(typeLoader, unqualifiedtype, vmajor, vminor, type_return, &base, errors, + const QVector<QHashedStringRef> splitName = type.split(Dot); + auto resolveTypeInNamespace = [&](QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) -> bool { + if (nameSpace->resolveType(typeLoader, unqualifiedtype, vmajor, vminor, type_return, &base, errors, registrationType, typeRecursionDetected)) return true; - if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { + if (nameSpace->imports.count() == 1 && !nameSpace->imports.at(0)->isLibrary && type_return && nameSpace != &unqualifiedset) { // qualified, and only 1 url *type_return = QQmlMetaType::typeForUrl( - resolveLocalUrl(s->imports.at(0)->url, + resolveLocalUrl(nameSpace->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); return type_return->isValid(); } + return false; + }; + switch (splitName.size()) { + case 1: { + // must be a simple type + return resolveTypeInNamespace(type, &unqualifiedset, errors); } - - return false; + case 2: { + // either namespace + simple type OR simple type + inline component + QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0)); + if (s) { + // namespace + simple type + return resolveTypeInNamespace(splitName.at(1), s, errors); + } else { + if (resolveTypeInNamespace(splitName.at(0), &unqualifiedset, nullptr)) { + // either simple type + inline component + auto const icName = splitName.at(1).toString(); + auto objectIndex = type_return->lookupInlineComponentIdByName(icName); + if (objectIndex != -1) { + *type_return = type_return->lookupInlineComponentById(objectIndex); + } else { + auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType); + icTypePriv->setContainingType(type_return); + icTypePriv->extraData.id->url = type_return->sourceUrl(); + int placeholderId = type_return->generatePlaceHolderICId(); + icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId)); + auto icType = QQmlType(icTypePriv); + type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType); + *type_return = icType; + } + Q_ASSERT(type_return->containingType().isValid()); + type_return->setPendingResolutionName(icName); + return true; + } else { + // or a failure + if (errors) { + QQmlError error; + error.setDescription(QQmlImportDatabase::tr("- %1 is neither a type nor a namespace").arg(splitName.at(0).toString())); + errors->prepend(error); + } + return false; + } + } + } + case 3: { + // must be namespace + simple type + inline component + QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0)); + QQmlError error; + if (!s) { + error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(splitName.at(0).toString())); + } else { + if (resolveTypeInNamespace(splitName.at(1), s, nullptr)) { + auto const icName = splitName.at(2).toString(); + auto objectIndex = type_return->lookupInlineComponentIdByName(icName); + if (objectIndex != -1) + *type_return = type_return->lookupInlineComponentById(objectIndex); + else { + auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType); + icTypePriv->setContainingType(type_return); + icTypePriv->extraData.id->url = type_return->sourceUrl(); + int placeholderId = type_return->generatePlaceHolderICId(); + icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId)); + auto icType = QQmlType(icTypePriv); + type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType); + *type_return = icType; + } + type_return->setPendingResolutionName(icName); + return true; + } else { + error.setDescription(QQmlImportDatabase::tr("- %1 is not a type").arg(splitName.at(1).toString())); + } + } + if (errors) { + errors->prepend(error); + } + return false; + } + default: { + // all other numbers suggest a user error + if (errors) { + QQmlError error; + error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed")); + errors->prepend(error); + } + return false; + } + } + Q_UNREACHABLE(); } QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const @@ -1652,6 +1746,21 @@ bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlErro } /*! + \internal + */ +bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType) +{ + importInstance->url = importUrl.toString(); + importInstance->uri = name; + importInstance->isInlineComponent = true; + importInstance->majversion = 0; + importInstance->minversion = 0; + importInstance->containingType = containingType; + d->unqualifiedset.imports.push_back(importInstance); + return true; +} + +/*! \internal Adds information to \a imports such that subsequent calls to resolveType() diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 1f44b22deb..2e994fd27f 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -80,10 +80,12 @@ struct QQmlImportInstance QString uri; // e.g. QtQuick QString url; // the base path of the import QString localDirectoryPath; // the base path of the import if it's a local file + QQmlType containingType; // points to the containing type for inline components int majversion; // the major version imported int minversion; // the minor version imported bool isLibrary; // true means that this is not a file import bool implicitlyImported = false; + bool isInlineComponent = false; QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir @@ -151,6 +153,8 @@ public: bool addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors); + bool addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType); + bool addFileImport(QQmlImportDatabase *, const QString& uri, const QString& prefix, int vmaj, int vmin, bool incomplete, QList<QQmlError> *errors); diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp index 82cad8eba8..b284e44fdf 100644 --- a/src/qml/qml/qqmlirloader.cpp +++ b/src/qml/qml/qqmlirloader.cpp @@ -200,6 +200,13 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali object->runtimeFunctionIndices.allocate(pool, functionIndices); + const QV4::CompiledData::InlineComponent *serializedInlineComponent = serializedObject->inlineComponentTable(); + for (uint i = 0; i < serializedObject->nInlineComponents; ++i, ++serializedInlineComponent) { + QmlIR::InlineComponent *ic = pool->New<QmlIR::InlineComponent>(); + *static_cast<QV4::CompiledData::InlineComponent*>(ic) = *serializedInlineComponent; + object->inlineComponents->append(ic); + } + return object; } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index f506cdece3..76816618ac 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -531,7 +531,7 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit return QQmlType(priv); } -QQmlMetaType::CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className) +CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className) { QByteArray ptr = className + '*'; QByteArray lst = "QQmlListProperty<" + className + '>'; @@ -555,7 +555,7 @@ QQmlMetaType::CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(c return {ptr_type, lst_type}; } -void QQmlMetaType::unregisterInternalCompositeType(const QQmlMetaType::CompositeMetaTypeIds &typeIds) +void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds) { QQmlMetaTypeDataPtr data; data->qmlLists.remove(typeIds.listId); diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 80126cbffb..037cf89beb 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -63,6 +63,15 @@ class QQmlError; namespace QV4 { class ExecutableCompilationUnit; } +struct CompositeMetaTypeIds +{ + int id = -1; + int listId = -1; + CompositeMetaTypeIds() = default; + CompositeMetaTypeIds(int id, int listId) : id(id), listId(listId) {} + bool isValid() const { return id != -1 && listId != -1; } +}; + class Q_QML_PRIVATE_EXPORT QQmlMetaType { public: @@ -80,18 +89,8 @@ public: static void unregisterType(int type); - struct CompositeMetaTypeIds - { - int id = -1; - int listId = -1; - CompositeMetaTypeIds() = default; - CompositeMetaTypeIds(int id, int listId) : id(id), listId(listId) {} - bool isValid() const { return id != -1 && listId != -1; } - }; - static CompositeMetaTypeIds registerInternalCompositeType(const QByteArray &className); - static void unregisterInternalCompositeType(const QQmlMetaType::CompositeMetaTypeIds &typeIds); - + static void unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds); static void registerModule(const char *uri, int versionMajor, int versionMinor); static bool protectModule(const QString &uri, int majVersion); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 78df02554c..198ce98f2d 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -62,6 +62,7 @@ #include <QScopedValueRollback> #include <qtqml_tracepoints_p.h> +#include <QScopedValueRollback> QT_USE_NAMESPACE @@ -77,9 +78,9 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR init(parentContext); sharedState->componentAttached = nullptr; - sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount); - sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount); - sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount); + sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount()); + sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount()); + sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount()); sharedState->allJavaScriptObjects = nullptr; sharedState->creationContext = creationContext; sharedState->rootContext = nullptr; @@ -87,7 +88,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR if (auto profiler = QQmlEnginePrivate::get(engine)->profiler) { Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, - sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount)); + sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount())); } else { Q_UNUSED(profiler); } @@ -145,7 +146,7 @@ QQmlObjectCreator::~QQmlObjectCreator() } } -QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt) +QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt, int flags) { if (phase == CreatingObjectsPhase2) { phase = ObjectsCreated; @@ -159,14 +160,20 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (subComponentIndex == -1) { objectToCreate = /*root object*/0; } else { - const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex); - objectToCreate = compObj->bindingTable()->value.objectIndex; + Q_ASSERT(subComponentIndex >= 0); + if (flags & CreationFlags::InlineComponent) { + objectToCreate = subComponentIndex; + } else { + Q_ASSERT(flags & CreationFlags::NormalObject); + const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex); + objectToCreate = compObj->bindingTable()->value.objectIndex; + } } context = new QQmlContextData; context->isInternal = true; context->imports = compilationUnit->typeNameCache; - context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); + context->initFromTypeCompilationUnit(compilationUnit, flags & CreationFlags::NormalObject ? subComponentIndex : -1); context->setParent(parentContext); if (!sharedState->rootContext) { @@ -179,7 +186,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator); if (topLevelCreator) - sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount); + sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount()); if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) { QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.count())); @@ -231,6 +238,9 @@ void QQmlObjectCreator::beginPopulateDeferred(QQmlContextData *newContext) Q_ASSERT(topLevelCreator); Q_ASSERT(!sharedState->allJavaScriptObjects); + + QV4::Scope valueScope(v4); + sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount()); } void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex, @@ -248,7 +258,7 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex, QV4::Scope valueScope(v4); QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects, - valueScope.alloc(compilationUnit->totalObjectCount)); + valueScope.alloc(compilationUnit->totalObjectCount())); Q_ASSERT(topLevelCreator); QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc()); @@ -263,7 +273,6 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex, const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex); qSwap(_compiledObject, obj); - qSwap(_ddata, declarativeData); qSwap(_bindingTarget, bindingTarget); qSwap(_vmeMetaObject, vmeMetaObject); @@ -1172,7 +1181,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo Q_ASSERT(typeRef); installPropertyCache = !typeRef->isFullyDynamicType; QQmlType type = typeRef->type; - if (type.isValid()) { + if (type.isValid() && !type.isInlineComponentType()) { typeName = type.qmlTypeName(); void *ddataMemory = nullptr; @@ -1206,17 +1215,31 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo } else { Q_ASSERT(typeRef->compilationUnit); typeName = typeRef->compilationUnit->fileName(); - if (typeRef->compilationUnit->unitData()->isSingleton()) + // compilation unit is shared between root type and its inline component types + // so isSingleton errorneously returns true for inline components + if (typeRef->compilationUnit->unitData()->isSingleton() && !type.isInlineComponentType()) { recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex))); return nullptr; } - QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data()); - instance = subCreator.create(); - if (!instance) { - errors += subCreator.errors; - return nullptr; + + if (!type.isInlineComponentType()) { + QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data()); + instance = subCreator.create(); + if (!instance) { + errors += subCreator.errors; + return nullptr; + } + } else { + int subObjectId = type.inlineComponendId(); + QScopedValueRollback<int> rollback {typeRef->compilationUnit->icRoot, subObjectId}; + QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data()); + instance = subCreator.create(subObjectId, nullptr, nullptr, CreationFlags::InlineComponent); + if (!instance) { + errors += subCreator.errors; + return nullptr; + } } } if (instance->isWidgetType()) { diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 8b6cb67341..f8ad90be15 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -111,7 +111,8 @@ public: QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); ~QQmlObjectCreator(); - QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr); + enum CreationFlags { NormalObject = 1, InlineComponent = 2 }; + QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr, int flags = NormalObject); bool populateDeferredProperties(QObject *instance, const QQmlData::DeferredData *deferredData); diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index e8178603cf..a050a0bf0a 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -55,6 +55,10 @@ #include <private/qqmlmetaobject_p.h> #include <private/qqmlpropertyresolver_p.h> #include <private/qqmltypedata_p.h> +#include <private/inlinecomponentutils_p.h> + +#include <QScopedValueRollback> +#include <vector> QT_BEGIN_NAMESPACE @@ -115,8 +119,12 @@ public: QQmlJS::DiagnosticMessage buildMetaObjects(); + enum class VMEMetaObjectIsRequired { + Maybe, + Always + }; protected: - QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); + QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired); QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const; QQmlJS::DiagnosticMessage createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); @@ -129,7 +137,8 @@ protected: const QQmlImports * const imports; QQmlPropertyCacheVector *propertyCaches; QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings; - const QByteArray typeClassName; + QByteArray typeClassName; // not const as we temporarily chang it for inline components + unsigned int currentRoot; // set to objectID of inline component root when handling inline components }; template <typename ObjectContainer> @@ -151,12 +160,59 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP template <typename ObjectContainer> inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() { + using namespace icutils; QQmlBindingInstantiationContext context; - return buildMetaObjectRecursively(/*root object*/0, context); + + // get a list of all inline components + using InlineComponent = typename std::remove_reference<decltype (*(std::declval<CompiledObject>().inlineComponentsBegin()))>::type; + std::vector<InlineComponent> allICs {}; + for (int i=0; i != objectContainer->objectCount(); ++i) { + const CompiledObject *obj = objectContainer->objectAt(i); + for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) { + allICs.push_back(*it); + } + } + + // create a graph on inline components referencing inline components + std::vector<Node> nodes; + nodes.resize(allICs.size()); + std::iota(nodes.begin(), nodes.end(), 0); + AdjacencyList adjacencyList; + adjacencyList.resize(nodes.size()); + fillAdjacencyListForInlineComponents(objectContainer, adjacencyList, nodes, allICs); + + bool hasCycle = false; + auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle); + + if (hasCycle) { + QQmlJS::DiagnosticMessage diag; + diag.message = QLatin1String("Inline components form a cycle!"); + return diag; + } + + // create meta objects for inline components before compiling actual root component + for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) { + const auto &ic = allICs[nodeIt->index]; + QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex); + Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr); + Q_ASSERT(typeRef->typePropertyCache.isNull()); // not set yet + + QByteArray icTypeName { objectContainer->stringAt(ic.nameIndex).toUtf8() }; + QScopedValueRollback<QByteArray> nameChange {typeClassName, icTypeName}; + QScopedValueRollback<unsigned int> rootChange {currentRoot, ic.objectIndex}; + QQmlJS::DiagnosticMessage diag = buildMetaObjectRecursively(ic.objectIndex, context, VMEMetaObjectIsRequired::Always); + if (diag.isValid()) { + return diag; + } + typeRef->typePropertyCache = propertyCaches->at(ic.objectIndex); + Q_ASSERT(!typeRef->typePropertyCache.isNull()); + } + + return buildMetaObjectRecursively(/*root object*/0, context, VMEMetaObjectIsRequired::Maybe); } template <typename ObjectContainer> -inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired) { auto isAddressable = [](const QUrl &url) { const QString fileName = url.fileName(); @@ -164,7 +220,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil }; const CompiledObject *obj = objectContainer->objectAt(objectIndex); - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 + bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0 || (((obj->flags & QV4::CompiledData::Object::IsComponent) || (objectIndex == 0 && isAddressable(objectContainer->url()))) @@ -230,7 +286,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil if (!context.resolveInstantiatingProperty()) pendingGroupPropertyBindings->append(context); - QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context); + QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe); if (error.isValid()) return error; } @@ -247,6 +303,7 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine return context.instantiatingPropertyCache(enginePrivate); } else if (obj->inheritedTypeNameIndex != 0) { auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); + QQmlType qmltype = typeRef->type; Q_ASSERT(typeRef); if (typeRef->isFullyDynamicType) { @@ -298,7 +355,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea QByteArray newClassName; - if (objectIndex == /*root object*/0) { + if (objectIndex == /*root object*/0 || int(currentRoot) == objectIndex) { newClassName = typeClassName; } if (newClassName.isEmpty()) { @@ -504,18 +561,31 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); } - Q_ASSERT(qmltype.isValid()); - if (qmltype.isComposite()) { - QQmlMetaType::CompositeMetaTypeIds typeIds; - if (selfReference) { - typeIds = objectContainer->typeIds(); + // inline components are not necessarily valid yet + Q_ASSERT(qmltype.isValid() || qmltype.isInlineComponentType()); + if (qmltype.isComposite() || qmltype.isInlineComponentType()) { + CompositeMetaTypeIds typeIds; + if (qmltype.isInlineComponentType()) { + auto objectId = qmltype.inlineComponendId(); + auto containingType = qmltype.containingType(); + if (containingType.isValid()) { + auto icType = containingType.lookupInlineComponentById(objectId); + typeIds = {icType.typeId(), icType.qListTypeId()}; + } else { + typeIds = {}; + } + if (!typeIds.isValid()) // type has not been registered yet, we must be in containing type + typeIds = objectContainer->typeIdsForComponent(objectId); + Q_ASSERT(typeIds.isValid()); + } else if (selfReference) { + typeIds = objectContainer->typeIdsForComponent(); } else { QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); auto compilationUnit = tdata->compilationUnit(); - typeIds = compilationUnit->typeIds(); + typeIds = compilationUnit->typeIdsForComponent(); } if (p->isList) { @@ -578,7 +648,7 @@ inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const return qmltype.typeId(); if (selfReference) - return objectContainer->typeIds().id; + return objectContainer->typeIdsForComponent().id; QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index 8f6e2737cc..8762dc328d 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -100,6 +100,9 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const { const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex); + for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) { + validateObject(it->objectIndex, /* instantiatingBinding*/ nullptr); + } if (obj->flags & QV4::CompiledData::Object::IsComponent) { Q_ASSERT(obj->nBindings == 1); diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index 4a211ffa53..28fefca239 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -84,6 +84,9 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) case QQmlType::CompositeType: extraData.fd = new QQmlCompositeTypeData; break; + case QQmlType::InlineComponentType: + extraData.id = new QQmlInlineTypeData; + break; default: qFatal("QQmlTypePrivate Internal Error."); } } @@ -106,6 +109,9 @@ QQmlTypePrivate::~QQmlTypePrivate() case QQmlType::CompositeType: delete extraData.fd; break; + case QQmlType::InlineComponentType: + delete extraData.id; + break; default: //Also InterfaceType, because it has no extra data break; } @@ -426,6 +432,12 @@ void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cach insertEnums(cppMetaObject); } +void QQmlTypePrivate::setContainingType(QQmlType *containingType) +{ + Q_ASSERT(regType == QQmlType::InlineComponentType); + extraData.id->containingType = containingType->d.data(); +} + void QQmlTypePrivate::setName(const QString &uri, const QString &element) { module = uri; @@ -440,6 +452,8 @@ QByteArray QQmlType::typeName() const return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); else if (d->baseMetaObject) return d->baseMetaObject->className(); + else if (d->regType == InlineComponentType) + return d->extraData.id->inlineComponentName.toUtf8(); } return QByteArray(); } @@ -561,7 +575,11 @@ bool QQmlType::isComposite() const bool QQmlType::isCompositeSingleton() const { - return d && d->regType == CompositeSingletonType; + // if the outer type is a composite singleton, d->regType will indicate that even for + // the inline component type + // however, inline components can -at least for now- never be singletons + // so we just do one additional check + return d && d->regType == CompositeSingletonType && !isInlineComponentType(); } bool QQmlType::isQObjectSingleton() const @@ -677,9 +695,28 @@ int QQmlType::index() const return d ? d->index : -1; } +bool QQmlType::isInlineComponentType() const { + return d ? d->regType == QQmlType::InlineComponentType : false; +} + +int QQmlType::inlineComponendId() const { + bool ok = false; + if (d->regType == QQmlType::RegistrationType::InlineComponentType) { + Q_ASSERT(d->extraData.id->objectId != -1); + return d->extraData.id->objectId; + } + int subObjectId = sourceUrl().fragment().toInt(&ok); + return ok ? subObjectId : -1; +} + QUrl QQmlType::sourceUrl() const { - return d ? d->sourceUrl() : QUrl(); + auto url = d ? d->sourceUrl() : QUrl(); + if (url.isValid() && d->regType == QQmlType::RegistrationType::InlineComponentType && d->extraData.id->objectId) { + Q_ASSERT(url.hasFragment()); + url.setFragment(QString::number(inlineComponendId())); + } + return url; } int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const @@ -845,6 +882,19 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scope return -1; } +int QQmlType::inlineComponentObjectId() +{ + if (!isInlineComponentType()) + return -1; + return d->extraData.id->objectId; +} + +void QQmlType::setInlineComponentObjectId(int id) const +{ + Q_ASSERT(d && d->regType == QQmlType::InlineComponentType); + d->extraData.id->objectId = id; +} + void QQmlType::refHandle(const QQmlTypePrivate *priv) { if (priv) @@ -864,4 +914,63 @@ int QQmlType::refCount(const QQmlTypePrivate *priv) return -1; } +int QQmlType::lookupInlineComponentIdByName(const QString &name) const +{ + Q_ASSERT(d); + return d->namesToInlineComponentObjectIndex.value(name, -1); +} + +QQmlType QQmlType::containingType() const +{ + Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType); + auto ret = QQmlType {d->extraData.id->containingType}; + Q_ASSERT(!ret.isInlineComponentType()); + return ret; +} + +QQmlType QQmlType::lookupInlineComponentById(int objectid) const +{ + Q_ASSERT(d); + return d->objectIdToICType.value(objectid, QQmlType(nullptr)); +} + +int QQmlType::generatePlaceHolderICId() const +{ + Q_ASSERT(d); + int id = -2; + for (auto it = d->objectIdToICType.keyBegin(); it != d->objectIdToICType.keyEnd(); ++it) + if (*it < id) + id = *it; + return id; +} + +void QQmlType::associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType) +{ + auto priv = existingType.isValid() ? const_cast<QQmlTypePrivate *>(existingType.d.data()) : new QQmlTypePrivate { RegistrationType::InlineComponentType } ; + priv->setName( QString::fromUtf8(typeName()), name); + auto icUrl = QUrl(sourceUrl()); + icUrl.setFragment(QString::number(objectID)); + priv->extraData.id->url = icUrl; + priv->extraData.id->containingType = d.data(); + priv->extraData.id->objectId = objectID; + priv->typeId = metaTypeIds.id; + priv->listId = metaTypeIds.listId; + d->namesToInlineComponentObjectIndex.insert(name, objectID); + QQmlType icType(priv); + d->objectIdToICType.insert(objectID, icType); +} + +void QQmlType::setPendingResolutionName(const QString &name) +{ + Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType); + Q_ASSERT(d->extraData.id->inlineComponentName == name|| d->extraData.id->inlineComponentName.isEmpty()); + d->extraData.id->inlineComponentName = name; +} + +QString QQmlType::pendingResolutionName() const +{ + Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType); + return d->extraData.id->inlineComponentName; +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h index af134b21f1..387baa74bb 100644 --- a/src/qml/qml/qqmltype_p.h +++ b/src/qml/qml/qqmltype_p.h @@ -74,6 +74,7 @@ class QQmlPropertyCache; namespace QV4 { struct String; } +struct CompositeMetaTypeIds; class Q_QML_PRIVATE_EXPORT QQmlType { @@ -144,6 +145,9 @@ public: int index() const; + bool isInlineComponentType() const; + int inlineComponendId() const; + struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo { QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *) = nullptr; @@ -166,6 +170,8 @@ public: int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const; int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const; int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const; + int inlineComponentObjectId(); + void setInlineComponentObjectId(int id) const; // TODO: const setters are BAD const QQmlTypePrivate *priv() const { return d.data(); } static void refHandle(const QQmlTypePrivate *priv); @@ -178,9 +184,19 @@ public: InterfaceType = 2, CompositeType = 3, CompositeSingletonType = 4, + InlineComponentType = 5, AnyRegistrationType = 255 }; + QQmlType containingType() const; + int lookupInlineComponentIdByName(const QString &name) const; + QQmlType lookupInlineComponentById(int objectid) const; + int generatePlaceHolderICId() const; + + void associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType); + void setPendingResolutionName(const QString &name); + QString pendingResolutionName() const; + private: friend class QQmlTypePrivate; friend uint qHash(const QQmlType &t, uint seed); diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h index 51f776178c..43344827db 100644 --- a/src/qml/qml/qqmltype_p_p.h +++ b/src/qml/qml/qqmltype_p_p.h @@ -56,6 +56,7 @@ #include <private/qqmlproxymetaobject_p.h> #include <private/qqmlrefcount_p.h> #include <private/qqmlpropertycache_p.h> +#include <private/qqmlmetatype_p.h> QT_BEGIN_NAMESPACE @@ -69,6 +70,7 @@ public: void initEnums(QQmlEnginePrivate *engine) const; void insertEnums(const QMetaObject *metaObject) const; void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; + void setContainingType(QQmlType *containingType); QUrl sourceUrl() const { @@ -77,6 +79,8 @@ public: return extraData.fd->url; case QQmlType::CompositeSingletonType: return extraData.sd->singletonInstanceInfo->url; + case QQmlType::InlineComponentType: + return extraData.id->url; default: return QUrl(); } @@ -130,10 +134,23 @@ public: QUrl url; }; + struct QQmlInlineTypeData + { + QUrl url = QUrl(); + // The containing type stores a pointer to the inline component type + // Using QQmlType here would create a reference cycle + // As the inline component type cannot outlive the containing type + // this should still be fine + QQmlTypePrivate const * containingType = nullptr; + QString inlineComponentName = QString(); + int objectId = -1; + }; + union extraData { QQmlCppTypeData* cd; QQmlSingletonTypeData* sd; QQmlCompositeTypeData* fd; + QQmlInlineTypeData* id; } extraData; const char *iid; @@ -160,6 +177,8 @@ public: mutable QList<QStringHash<int>*> scopedEnums; void setName(const QString &uri, const QString &element); + mutable QHash<QString, int> namesToInlineComponentObjectIndex; + mutable QHash<int, QQmlType> objectIdToICType; private: ~QQmlTypePrivate() override; diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index 7b4cf1a580..8d499bfe6a 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -58,10 +58,10 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) : resolvedTypes(resolvedTypeCache) , engine(engine) - , typeData(typeData) , dependencyHasher(dependencyHasher) - , typeNameCache(typeNameCache) , document(parsedQML) + , typeNameCache(typeNameCache) + , typeData(typeData) { } @@ -279,9 +279,9 @@ void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier document->imports.append(import); } -QQmlMetaType::CompositeMetaTypeIds QQmlTypeCompiler::typeIds() const +CompositeMetaTypeIds QQmlTypeCompiler::typeIdsForComponent(int objectId) const { - return typeData->typeIds(); + return typeData->typeIds(objectId); } QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) @@ -1221,7 +1221,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) if (obj->idNameIndex != 0) _seenObjectWithId = true; - if (obj->flags & QV4::CompiledData::Object::IsComponent) { + if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) { Q_ASSERT(obj->bindingCount() == 1); const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h index b43089dc06..319df8673b 100644 --- a/src/qml/qml/qqmltypecompiler_p.h +++ b/src/qml/qml/qqmltypecompiler_p.h @@ -79,7 +79,9 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, + QQmlTypeCompiler(QQmlEnginePrivate *engine, + QQmlTypeData *typeData, + QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher); @@ -129,14 +131,12 @@ public: return resolvedTypes->value(id); } - QQmlMetaType::CompositeMetaTypeIds typeIds() const; + CompositeMetaTypeIds typeIdsForComponent(int objectId = 0) const; private: QList<QQmlError> errors; QQmlEnginePrivate *engine; - QQmlTypeData *typeData; const QV4::CompiledData::DependentTypesHasher &dependencyHasher; - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash<int, QQmlCustomParser*> customParsers; @@ -144,6 +144,9 @@ private: // index in first hash is component index, vector inside contains object indices of objects with id property QVector<quint32> m_componentRoots; QQmlPropertyCacheVector m_propertyCaches; + + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + QQmlTypeData *typeData; }; struct QQmlCompilePass diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index 22587c226a..f7abe67921 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -92,6 +92,25 @@ QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const return m_compiledData.data(); } +QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnitForInlineComponent(unsigned int icObjectId) const +{ + Q_ASSERT(m_document || m_compiledData); + if (m_compiledData) + return m_compiledData.data(); + for (auto it = m_document->objects.begin(); it != m_document->objects.end(); ++it) { + auto object = *it; + auto icIt = std::find_if(object->inlineComponentsBegin(), object->inlineComponentsEnd(), [&](const QV4::CompiledData::InlineComponent &ic) { + return ic.objectIndex == icObjectId; + }); + if (icIt != object->inlineComponentsEnd()) { + Q_ASSERT(m_inlineComponentToCompiledData.contains(icIt->nameIndex)); + return m_inlineComponentToCompiledData[icIt->nameIndex].data(); + } + } + Q_UNREACHABLE(); + return nullptr; // make integrity happy +} + void QQmlTypeData::registerCallback(TypeDataCallback *callback) { Q_ASSERT(!m_callbacks.contains(callback)); @@ -105,8 +124,10 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) Q_ASSERT(!m_callbacks.contains(callback)); } -QQmlMetaType::CompositeMetaTypeIds QQmlTypeData::typeIds() const +CompositeMetaTypeIds QQmlTypeData::typeIds(int objectId) const { + if (objectId != 0) + return m_inlineComponentData[objectId].typeIds; return m_typeIds; } @@ -135,8 +156,15 @@ bool QQmlTypeData::tryLoadFromDiskCache() m_compiledData = unit; - for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) - m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); + QVector<QV4::CompiledData::InlineComponent> ics; + for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) { + auto object = m_compiledData->objectAt(i); + m_typeReferences.collectFromObject(object); + const auto inlineComponentTable = object->inlineComponentTable(); + for (auto i = 0; i != object->nInlineComponents; ++i) { + ics.push_back(inlineComponentTable[i]); + } + } m_importCache.setBaseUrl(finalUrl(), finalUrlString()); @@ -183,6 +211,20 @@ bool QQmlTypeData::tryLoadFromDiskCache() } } + QQmlType containingType; + auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first(); + int major = -1, minor = -1; + QQmlImportNamespace *ns = nullptr; + m_importCache.resolveType(containingTypeName, &containingType, &major, &minor, &ns); + for (auto&& ic: ics) { + QString const nameString = m_compiledData->stringAt(ic.nameIndex); + QByteArray const name = nameString.toUtf8(); + auto importUrl = finalUrl(); + importUrl.setFragment(QString::number(ic.objectIndex)); + auto import = new QQmlImportInstance(); + m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType); + } + return true; } @@ -233,6 +275,36 @@ static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeRefere return true; } +// local helper function for inline components +namespace { +template<typename ObjectContainer> +void setupICs(const ObjectContainer &container, QHash<int, InlineComponentData> *icData, const QUrl &finalUrl) { + Q_ASSERT(icData->empty()); + for (int i = 0; i != container->objectCount(); ++i) { + auto root = container->objectAt(i); + for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) { + auto url = finalUrl; + url.setFragment(QString::number(it->objectIndex)); + const QByteArray &className = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url); + InlineComponentData icDatum(QQmlMetaType::registerInternalCompositeType(className), int(it->objectIndex), int(it->nameIndex), 0, 0, 0); + icData->insert(it->objectIndex, icDatum); + } + } +}; +} + +template<typename Container> +void QQmlTypeData::setCompileUnit(const Container &container) +{ + for (int i = 0; i != container->objectCount(); ++i) { + auto const root = container->objectAt(i); + for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) { + auto *typeRef = m_compiledData->resolvedType(it->nameIndex); + typeRef->compilationUnit = m_compiledData; // share compilation unit + } + } +} + void QQmlTypeData::done() { auto cleanup = qScopeGuard([this]{ @@ -263,10 +335,30 @@ void QQmlTypeData::done() } // Check all type dependencies for errors - for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; + for (auto it = qAsConst(m_resolvedTypes).begin(), end = qAsConst(m_resolvedTypes).end(); it != end; ++it) { const TypeReference &type = *it; - Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError() || type.type.isInlineComponentType()); + if (type.type.isInlineComponentType() && !type.type.pendingResolutionName().isEmpty()) { + auto containingType = type.type.containingType(); + auto objectId = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName()); + if (objectId < 0) { // can be any negative number if we tentatively resolved it in QQmlImport but it actually was not an inline component + const QString typeName = stringAt(it.key()); + int lastDot = typeName.lastIndexOf('.'); + + QList<QQmlError> errors = type.typeData ? type.typeData->errors() : QList<QQmlError>{}; + QQmlError error; + error.setUrl(url()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(typeName.leftRef(lastDot), type.type.pendingResolutionName())); + errors.prepend(error); + setError(errors); + return; + } else { + type.type.setInlineComponentObjectId(objectId); + } + } if (type.typeData && type.typeData->isError()) { const QString typeName = stringAt(it.key()); @@ -304,13 +396,23 @@ void QQmlTypeData::done() m_typeClassName = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(finalUrl()); if (!m_typeClassName.isEmpty()) m_typeIds = QQmlMetaType::registerInternalCompositeType(m_typeClassName); + + if (m_document) { + setupICs(m_document, &m_inlineComponentData, finalUrl()); + } else { + setupICs(m_compiledData, &m_inlineComponentData, finalUrl()); + } auto typeCleanupGuard = qScopeGuard([&]() { - if (isError() && m_typeIds.isValid()) + if (isError() && m_typeIds.isValid()) { QQmlMetaType::unregisterInternalCompositeType(m_typeIds); + for (auto&& icData: qAsConst(m_inlineComponentData)) { + QQmlMetaType::unregisterInternalCompositeType(icData.typeIds); + } + } }); - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QV4::ResolvedTypeReferenceMap resolvedTypeCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; { QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); if (error.isValid()) { @@ -342,8 +444,12 @@ void QQmlTypeData::done() if (!m_document.isNull()) { // Compile component compile(typeNameCache, &resolvedTypeCache, dependencyHasher); + if (!isError()) + setCompileUnit(m_document); } else { createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); + if (!isError()) + setCompileUnit(m_compiledData); } if (isError()) @@ -351,6 +457,7 @@ void QQmlTypeData::done() { QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); + m_compiledData->inlineComponentData = m_inlineComponentData; { // Sanity check property bindings QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); @@ -389,6 +496,26 @@ void QQmlTypeData::done() } } + // associate inline components to root component + { + auto typeName = finalUrlString().splitRef('/').last().split('.').first().toString(); + // typeName can be empty if a QQmlComponent was constructed with an empty QUrl parameter + if (!typeName.isEmpty() && typeName.at(0).isUpper() && !m_inlineComponentData.isEmpty()) { + QHashedStringRef const hashedStringRef { typeName }; + QList<QQmlError> errors; + auto type = QQmlMetaType::typeForUrl(finalUrlString(), hashedStringRef, false, &errors); + Q_ASSERT(errors.empty()); + if (type.isValid()) { + for (auto const &icDatum : m_inlineComponentData) { + Q_ASSERT(icDatum.typeIds.isValid()); + QQmlType existingType = type.lookupInlineComponentById(type.lookupInlineComponentIdByName(m_compiledData->stringAt(icDatum.nameIndex))); + type.associateInlineComponent(m_compiledData->stringAt(icDatum.nameIndex), + icDatum.objectIndex, icDatum.typeIds, existingType); + } + } + } + } + { // Collect imported scripts m_compiledData->dependentScripts.reserve(m_scripts.count()); @@ -522,6 +649,22 @@ void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit) void QQmlTypeData::continueLoadFromIR() { + QQmlType containingType; + auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first(); + int major = -1, minor = -1; + QQmlImportNamespace *ns = nullptr; + m_importCache.resolveType(containingTypeName, &containingType, &major, &minor, &ns); + for (auto const& object: m_document->objects) { + for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it) { + QString const nameString = m_document->stringAt(it->nameIndex); + QByteArray const name = nameString.toUtf8(); + auto importUrl = finalUrl(); + importUrl.setFragment(QString::number(it->objectIndex)); + auto import = new QQmlImportInstance(); // Note: The cache takes ownership of the QQmlImportInstance + m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType); + } + } + m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); m_importCache.setBaseUrl(finalUrl(), finalUrlString()); @@ -724,6 +867,17 @@ void QQmlTypeData::resolveTypes() ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); addDependency(ref.typeData.data()); } + if (ref.type.isInlineComponentType()) { + auto containingType = ref.type.containingType(); + if (containingType.isValid()) { + auto const url = containingType.sourceUrl(); + if (url.isValid()) { + auto typeData = typeLoader()->getType(url); + ref.typeData = typeData; + addDependency(typeData.data()); + } + } + } ref.majorVersion = majorVersion; ref.minorVersion = minorVersion; @@ -731,7 +885,6 @@ void QQmlTypeData::resolveTypes() ref.location.column = unresolvedRef->location.column; ref.needsCreation = unresolvedRef->needsCreation; - m_resolvedTypes.insert(unresolvedRef.key(), ref); } @@ -766,6 +919,38 @@ QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName())); } ref->compilationUnit = resolvedType->typeData->compilationUnit(); + if (resolvedType->type.isInlineComponentType()) { + // Inline component which is part of an already resolved type + int objectId = -1; + if (qmlType.containingType().isValid()) { + objectId = qmlType.containingType().lookupInlineComponentIdByName(QString::fromUtf8(qmlType.typeName())); + qmlType.setInlineComponentObjectId(objectId); + } else { + objectId = resolvedType->type.inlineComponendId(); + } + Q_ASSERT(objectId != -1); + ref->typePropertyCache = resolvedType->typeData->compilationUnit()->propertyCaches.at(objectId); + ref->type = qmlType; + Q_ASSERT(ref->type.isInlineComponentType()); + } + } else if (resolvedType->type.isInlineComponentType()) { + // Inline component, defined in the file we are currently compiling + if (!m_inlineComponentToCompiledData.contains(resolvedType.key())) { + ref->type = qmlType; + if (qmlType.isValid()) { + // this is required for inline components in singletons + auto typeID = qmlType.lookupInlineComponentById(qmlType.inlineComponendId()).typeId(); + auto exUnit = engine->obtainExecutableCompilationUnit(typeID); + if (exUnit) { + ref->compilationUnit = exUnit; + ref->typePropertyCache = engine->propertyCacheForType(typeID); + } + } + } else { + ref->compilationUnit = m_inlineComponentToCompiledData[resolvedType.key()]; + ref->typePropertyCache = m_inlineComponentToCompiledData[resolvedType.key()]->rootPropertyCache(); + } + } else if (qmlType.isValid() && !resolvedType->selfReference) { ref->type = qmlType; Q_ASSERT(ref->type.isValid()); diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h index 53e78e06d7..d894090b36 100644 --- a/src/qml/qml/qqmltypedata_p.h +++ b/src/qml/qml/qqmltypedata_p.h @@ -86,6 +86,8 @@ private: friend class QQmlTypeLoader; QQmlTypeData(const QUrl &, QQmlTypeLoader *); + template<typename Container> + void setCompileUnit(const Container &container); public: ~QQmlTypeData() override; @@ -93,6 +95,7 @@ public: const QList<ScriptReference> &resolvedScripts() const; QV4::ExecutableCompilationUnit *compilationUnit() const; + QV4::ExecutableCompilationUnit *compilationUnitForInlineComponent(unsigned int icObjectId) const; // Used by QQmlComponent to get notifications struct TypeDataCallback { @@ -103,7 +106,7 @@ public: void registerCallback(TypeDataCallback *); void unregisterCallback(TypeDataCallback *); - QQmlMetaType::CompositeMetaTypeIds typeIds() const; + CompositeMetaTypeIds typeIds(int objectId = 0) const; QByteArray typeClassName() const { return m_typeClassName; } protected: @@ -155,10 +158,15 @@ private: bool m_typesResolved:1; // Used for self-referencing types, otherwise -1. - QQmlMetaType::CompositeMetaTypeIds m_typeIds; + CompositeMetaTypeIds m_typeIds; QByteArray m_typeClassName; // used for meta-object later - QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compiledData; + using ExecutableCompilationUnitPtr = QQmlRefPointer<QV4::ExecutableCompilationUnit>; + + QHash<int, InlineComponentData> m_inlineComponentData; + + ExecutableCompilationUnitPtr m_compiledData; + QHash<int, ExecutableCompilationUnitPtr> m_inlineComponentToCompiledData; QList<TypeDataCallback *> m_callbacks; diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml new file mode 100644 index 0000000000..0b297a7779 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentBase.qml @@ -0,0 +1,9 @@ +import QtQml 2.15 + +QtObject { + property alias i: icInstance.i + component IC : QtObject { + property int i: 42 + } + property QtObject ic: IC {id: icInstance} +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml new file mode 100644 index 0000000000..49a90ab7da --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentChild.qml @@ -0,0 +1,7 @@ +import QtQml 2.15 + +InlineComponentBase { + component IC : QtObject { + property int i: 13 + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml new file mode 100644 index 0000000000..6058e32b2f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider.qml @@ -0,0 +1,9 @@ +import QtQuick 2.15 + +Item { + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "red" + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml new file mode 100644 index 0000000000..b5a0efaccf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.15 + +Item { + property color myColor: "red" + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: myColor + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml new file mode 100644 index 0000000000..9a4f1fd272 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProvider3.qml @@ -0,0 +1,11 @@ +import QtQuick 2.15 + +Item { + Item { + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "red" + } + } +} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml new file mode 100644 index 0000000000..ddb55e55e6 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentProviderChild.qml @@ -0,0 +1 @@ +InlineComponentProvider {} diff --git a/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml b/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml new file mode 100644 index 0000000000..24bf6f771e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/InlineComponentReexporter.qml @@ -0,0 +1,8 @@ +import QtQuick 2.14 + +QtObject { + component StyledRectangle: InlineComponentProvider.StyledRectangle { + color: "green" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml b/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml new file mode 100644 index 0000000000..c5aa4cfdf5 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/icCycleViaProperty.qml @@ -0,0 +1,9 @@ +import QtQuick 2.15 + +Item { + component A : Item { + property var test: B {} + } + component B: A {} + A {} +} diff --git a/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml b/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml new file mode 100644 index 0000000000..69e74f7c96 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/icSimpleCycle.qml @@ -0,0 +1,6 @@ +import QtQuick 2.15 + +Item { + component A : B {} + component B: A {} +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml new file mode 100644 index 0000000000..a96f68e56a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentOrder.qml @@ -0,0 +1,20 @@ +import QtQuick 2.15 + +Item { + width: 600 + height: 480 + IC2 { + objectName: "icInstance" + anchors.centerIn: parent + } + + component IC2: IC1 {} + component IC0: Rectangle { + height: 200 + width: 200 + color: "blue" + } + component IC1: IC0 {} + + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml new file mode 100644 index 0000000000..8968a20112 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser1.qml @@ -0,0 +1,13 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property InlineComponentProvider.StyledRectangle myProp: InlineComponentProvider.StyledRectangle {} + InlineComponentProvider.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + color: "blue" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml new file mode 100644 index 0000000000..dc6e3850db --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property InlineComponentProvider.StyledRectangle myProp: InlineComponentReexporter.StyledRectangle { + objectName: "icInstance" + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml new file mode 100644 index 0000000000..c57c4cad01 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser3.qml @@ -0,0 +1,16 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + component StyledRectangle: Rectangle { + width: 24 + height: 24 + color: "blue" + } + StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml new file mode 100644 index 0000000000..9f3903c8df --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser4.qml @@ -0,0 +1,12 @@ +import QtQuick 2.14 + +Item { + width: 600 + height: 480 + property color myColor: "blue" + InlineComponentProvider2.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml new file mode 100644 index 0000000000..150d0c2ded --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentUser5.qml @@ -0,0 +1,11 @@ +import QtQuick 2.15 + +Item { + width: 600 + height: 480 + property var test: InlineComponentProvider3.StyledRectangle { + objectName: "icInstance" + anchors.centerIn: parent + } + +} diff --git a/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt index 3b90f573a2..41cb0eaac1 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidRoot.4.errors.txt @@ -1 +1 @@ -3:1:Bar.Item - Bar is not a namespace +3:1:Bar.Item - Bar is neither a type nor a namespace diff --git a/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt index 3b90f573a2..41cb0eaac1 100644 --- a/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/invalidTypeName.4.errors.txt @@ -1 +1 @@ -3:1:Bar.Item - Bar is not a namespace +3:1:Bar.Item - Bar is neither a type nor a namespace diff --git a/tests/auto/qml/qqmllanguage/data/nestedIC.qml b/tests/auto/qml/qqmllanguage/data/nestedIC.qml new file mode 100644 index 0000000000..04cef64d54 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nestedIC.qml @@ -0,0 +1,7 @@ +import QtQuick 2.15 + +Item { + component Outer : Item { + component Inner : Item {} + } +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml new file mode 100644 index 0000000000..a07a6a9838 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser1.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property InlineComponentProvider.NonExisting myProp +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml new file mode 100644 index 0000000000..5c24962def --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser2.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + InlineComponentProvider.NotExisting {} +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml new file mode 100644 index 0000000000..34595707fc --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser3.qml @@ -0,0 +1,3 @@ +import QtQuick 2.15 + +InlineComponentProvider.NotExisting {} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml new file mode 100644 index 0000000000..2be01ccd96 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser4.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "singleton" as MySingleton + +Item { + property MySingleton.SingletonTypeWithIC.NonExisting singletonIC +} diff --git a/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml b/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml new file mode 100644 index 0000000000..a2ca5db6de --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/nonExistingICUser5.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Item { + property InlineComponentProviderChild.StyledRectangle myProp +} diff --git a/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml b/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml new file mode 100644 index 0000000000..bd724c4aeb --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singleton/SingletonTypeWithIC.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 +pragma Singleton + +Item { + id: singletonId + component IC1: Item { + property int iProp: 42 + property string sProp: "Hello, world" + property Rectangle myRect: Rectangle {color: "green"} + } + component IC2: Item { + property int iProp: 13 + property string sProp: "Goodbye, world" + property Rectangle myRect: Rectangle {color: "red"} + } +} diff --git a/tests/auto/qml/qqmllanguage/data/singleton/qmldir b/tests/auto/qml/qqmllanguage/data/singleton/qmldir index 533fb6999a..727b09b4e8 100644 --- a/tests/auto/qml/qqmllanguage/data/singleton/qmldir +++ b/tests/auto/qml/qqmllanguage/data/singleton/qmldir @@ -1,3 +1,4 @@ singleton SingletonType SingletonType.qml +singleton SingletonTypeWithIC SingletonTypeWithIC.qml diff --git a/tests/auto/qml/qqmllanguage/data/singletonICTest.qml b/tests/auto/qml/qqmllanguage/data/singletonICTest.qml new file mode 100644 index 0000000000..d0d3b079be --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/singletonICTest.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import "singleton" as MySingleton + +Item { + property MySingleton.SingletonTypeWithIC.IC1 singleton1: MySingleton.SingletonTypeWithIC.IC1 {}; +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 089daf3ed5..4d2f773dbf 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -306,6 +306,16 @@ private slots: void extendedForeignTypes(); + void inlineComponent(); + void inlineComponent_data(); + void inlineComponentReferenceCycle_data(); + void inlineComponentReferenceCycle(); + void nestedInlineComponentNotAllowed(); + void inlineComponentStaticTypeResolution(); + void inlineComponentInSingleton(); + void nonExistingInlineComponent_data(); + void nonExistingInlineComponent(); + void selfReference(); void selfReferencingSingleton(); @@ -2780,12 +2790,12 @@ void tst_qqmllanguage::importsLocal_data() QTest::newRow("local import QTBUG-7721 A") << "subdir.Test {}" // no longer allowed (QTBUG-7721) << "" - << "subdir.Test - subdir is not a namespace"; + << "subdir.Test - subdir is neither a type nor a namespace"; QTest::newRow("local import QTBUG-7721 B") << "import \"subdir\" as X\n" "X.subsubdir.SubTest {}" // no longer allowed (QTBUG-7721) << "" - << "X.subsubdir.SubTest - nested namespaces not allowed"; + << "X.subsubdir.SubTest - subsubdir is not a type"; QTest::newRow("local import as") << "import \"subdir\" as T\n" "T.Test {}" @@ -5408,6 +5418,128 @@ void tst_qqmllanguage::overrideSingleton() check("uncreatable", "UncreatableSingleton"); } +void tst_qqmllanguage::inlineComponent() +{ + QFETCH(QUrl, componentUrl); + QFETCH(QColor, color); + QFETCH(int, width); + QQmlEngine engine; + QQmlComponent component(&engine, componentUrl); + QScopedPointer<QObject> o(component.create()); + if (component.isError()) { + qDebug() << component.errorString(); + } + QVERIFY(!o.isNull()); + auto icInstance = o->findChild<QObject *>("icInstance"); + QVERIFY(icInstance); + QCOMPARE(icInstance->property("color").value<QColor>(),color); + QCOMPARE(icInstance->property("width").value<qreal>(), width); +} + +void tst_qqmllanguage::inlineComponent_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + QTest::addColumn<QColor>("color"); + QTest::addColumn<int>("width"); + + QTest::newRow("Usage from other component") << testFileUrl("inlineComponentUser1.qml") << QColorConstants::Blue << 24; + QTest::newRow("Reexport") << testFileUrl("inlineComponentUser2.qml") << QColorConstants::Svg::green << 24; + QTest::newRow("Usage in same component") << testFileUrl("inlineComponentUser3.qml") << QColorConstants::Blue << 24; + + QTest::newRow("Resolution happens at instantiation") << testFileUrl("inlineComponentUser4.qml") << QColorConstants::Blue << 24; + QTest::newRow("Non-toplevel IC is found") << testFileUrl("inlineComponentUser5.qml") << QColorConstants::Svg::red << 24; + + QTest::newRow("Resolved in correct order") << testFileUrl("inlineComponentOrder.qml") << QColorConstants::Blue << 200; +} + +void tst_qqmllanguage::inlineComponentReferenceCycle_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + + QTest::newRow("Simple cycle") << testFileUrl("icSimpleCycle.qml"); + QTest::newRow("Via property") << testFileUrl("icCycleViaProperty.qml"); +} + +void tst_qqmllanguage::inlineComponentReferenceCycle() +{ + QFETCH(QUrl, componentUrl); + QQmlEngine engine; + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); + QQmlComponent component(&engine, componentUrl); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o.isNull()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), componentUrl.toString() + QLatin1String(":-1 Inline components form a cycle!\n")); +} + +void tst_qqmllanguage::nestedInlineComponentNotAllowed() +{ + QQmlEngine engine; + auto url = testFileUrl("nestedIC.qml"); + QQmlComponent component(&engine, url); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QQmlComponent: Component is not ready"); + QScopedPointer<QObject> o(component.create()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), QLatin1String("%1:%2").arg(url.toString(), QLatin1String("5 Nested inline components are not supported\n"))); +} + +void tst_qqmllanguage::inlineComponentStaticTypeResolution() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("InlineComponentChild.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(o); + QCOMPARE(o->property("i").toInt(), 42); +} + +void tst_qqmllanguage::inlineComponentInSingleton() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("singletonICTest.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + auto untyped = o->property("singleton1"); + QVERIFY(untyped.isValid()); + auto singleton1 = untyped.value<QObject*>(); + QVERIFY(singleton1); + QCOMPARE(singleton1->property("iProp").value<int>(), 42); + QCOMPARE(singleton1->property("sProp").value<QString>(), QString::fromLatin1("Hello, world")); + QVERIFY(!o.isNull()); +} + +void tst_qqmllanguage::nonExistingInlineComponent_data() +{ + QTest::addColumn<QUrl>("componentUrl"); + QTest::addColumn<QString>("errorMessage"); + QTest::addColumn<int>("line"); + QTest::addColumn<int>("column"); + + QTest::newRow("Property type") << testFileUrl("nonExistingICUser1.qml") << QString("Type InlineComponentProvider has no inline component type called NonExisting") << 4 << 5; + QTest::newRow("Instantiation") << testFileUrl("nonExistingICUser2.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 4 << 5; + QTest::newRow("Inheritance") << testFileUrl("nonExistingICUser3.qml") << QString("Type InlineComponentProvider has no inline component type called NotExisting") << 3 << 1; + QTest::newRow("From singleton") << testFileUrl("nonExistingICUser4.qml") << QString("Type MySingleton.SingletonTypeWithIC has no inline component type called NonExisting") << 5 << 5; + + QTest::newRow("Cannot access parent inline components from child") << testFileUrl("nonExistingICUser5.qml") << QString("Type InlineComponentProviderChild has no inline component type called StyledRectangle") << 4 << 5; +} + +void tst_qqmllanguage::nonExistingInlineComponent() +{ + QFETCH(QUrl, componentUrl); + QFETCH(QString, errorMessage); + QFETCH(int, line); + QFETCH(int, column); + QQmlEngine engine; + QQmlComponent component(&engine, componentUrl); + auto errors = component.errors(); + QCOMPARE(errors.size(), 1); + const auto &error = errors.first(); + QCOMPARE(error.description(), errorMessage); + QCOMPARE(error.line(), line); + QCOMPARE(error.column(), column); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 75a932b6f4..d54e3467b7 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -700,7 +700,7 @@ void tst_qqmllistmodel::error_data() QTest::newRow("unknown qualified ListElement not allowed") << "import QtQuick 2.0\nListModel { Foo.ListElement { a: 123 } }" - << "Foo.ListElement - Foo is not a namespace"; + << "Foo.ListElement - Foo is neither a type nor a namespace"; } void tst_qqmllistmodel::error() |