From 684f9df7849bc79f1f02a60844fb43c7a3927d2f Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 15 Jan 2020 14:47:35 +0100 Subject: Long live QML inline components [ChangeLog][QtQml] It is now possible to declare new QML components in a QML file via the component keyword. They can be used just as if they were declared in another file, with the only difference that the type name needs to be prefixed with the name of the containing type outside of the file were the inline component has been declared. Notably, inline components are not closures: In the following example, the output would be 42 // MyItem.qml Item { property int i: 33 component IC: Item { Component.onCompleted: console.log(i) } } // user.qml Item { property int i: 42 MyItem.IC {} } Fixes: QTBUG-79382 Change-Id: I6a5ffc43f093a76323f435cfee9bab217781b8f5 Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlpropertycachecreator_p.h | 98 +++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 14 deletions(-) (limited to 'src/qml/qml/qqmlpropertycachecreator_p.h') 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 #include #include +#include + +#include +#include 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 propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const; QQmlJS::DiagnosticMessage createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer &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 @@ -151,12 +160,59 @@ inline QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlP template inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator::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().inlineComponentsBegin()))>::type; + std::vector 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 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 nameChange {typeClassName, icTypeName}; + QScopedValueRollback 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 -inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator::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::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::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 QQmlPropertyCacheCreatorinheritedTypeNameIndex != 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::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::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 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::metaTypeForParameter(const return qmltype.typeId(); if (selfReference) - return objectContainer->typeIds().id; + return objectContainer->typeIdsForComponent().id; QQmlRefPointer tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); -- cgit v1.2.3