diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2022-02-24 08:57:44 +0100 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-03-14 20:53:52 +0100 |
| commit | 6eff3465b5192e40343f0486ef7076da2ed28bed (patch) | |
| tree | 5b1b4f65b2396ebd7152ab8f13300e5f5dd33421 /src | |
| parent | 9de2b11a7033a1969156277bafa848b5c178baa1 (diff) | |
QmlCompiler: Implement generate_DefineArray
Using the type cloning and adaption mechanism we can now determine what
kind of list we have to create in order to avoid a later conversion. We
can even propagate the type adjustment into the element types we read.
Fixes: QTBUG-100157
Change-Id: Ia2f160ebae56f39ee5946f49d2f8c5b4986a6b77
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src')
| -rw-r--r-- | src/qmlcompiler/qqmljsbasicblocks.cpp | 41 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljsbasicblocks_p.h | 3 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 38 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljsfunctioninitializer.cpp | 2 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 9 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 23 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 3 |
7 files changed, 101 insertions, 18 deletions
diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp index cce7e141eb..6c3a0d71ab 100644 --- a/src/qmlcompiler/qqmljsbasicblocks.cpp +++ b/src/qmlcompiler/qqmljsbasicblocks.cpp @@ -140,6 +140,14 @@ void QQmlJSBasicBlocks::generate_ThrowException() m_skipUntilNextLabel = true; } +void QQmlJSBasicBlocks::generate_DefineArray(int argc, int) +{ + if (argc == 0) + return; // empty array/list, nothing to do + + m_arrayDefinitions.append(currentInstructionOffset()); +} + void QQmlJSBasicBlocks::processJump(int offset, JumpMode mode) { if (offset < 0) @@ -377,6 +385,39 @@ void QQmlJSBasicBlocks::adjustTypes() using NewVirtualRegisters = NewFlatMap<int, QQmlJSRegisterContent>; QHash<int, QList<int>> liveConversions; + + // Handle the array definitions first. + // Changing the array type changes the expected element types. + for (int instructionOffset : m_arrayDefinitions) { + auto it = m_readerLocations.find(instructionOffset); + if (it == m_readerLocations.end()) + continue; + + const InstructionAnnotation &annotation = m_annotations[instructionOffset]; + + Q_ASSERT(it->trackedTypes.length() == 1); + Q_ASSERT(it->trackedTypes[0] == m_typeResolver->containedType(annotation.changedRegister)); + Q_ASSERT(!annotation.readRegisters.isEmpty()); + + m_typeResolver->adjustTrackedType(it->trackedTypes[0], it->typeReaders.values()); + + // Now we don't adjust the type we store, but rather the type we expect to read. We + // can do this because we've tracked the read type when we defined the array in + // QQmlJSTypePropagator. + if (QQmlJSScope::ConstPtr valueType = it->trackedTypes[0]->valueType()) { + m_typeResolver->adjustTrackedType( + m_typeResolver->containedType(annotation.readRegisters.begin().value()), + valueType); + } + + for (const QList<int> &conversions : qAsConst(it->registerReadersAndConversions)) { + for (int conversion : conversions) + liveConversions[conversion].append(it->trackedRegister); + } + + m_readerLocations.erase(it); + } + for (auto it = m_readerLocations.begin(), end = m_readerLocations.end(); it != end; ++it) { for (const QList<int> &conversions : qAsConst(it->registerReadersAndConversions)) { for (int conversion : conversions) diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h index e895dc7fe6..e5a39104db 100644 --- a/src/qmlcompiler/qqmljsbasicblocks_p.h +++ b/src/qmlcompiler/qqmljsbasicblocks_p.h @@ -88,6 +88,8 @@ private: void generate_Ret() override; void generate_ThrowException() override; + void generate_DefineArray(int argc, int argv) override; + enum JumpMode { Unconditional, Conditional }; void processJump(int offset, JumpMode mode); void populateBasicBlocks(); @@ -97,6 +99,7 @@ private: InstructionAnnotations m_annotations; QFlatMap<int, BasicBlock> m_basicBlocks; QHash<int, RegisterAccess> m_readerLocations; + QList<int> m_arrayDefinitions; bool m_skipUntilNextLabel = false; bool m_hadBackJumps = false; }; diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index 7867a244c2..dd564207d8 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -1561,15 +1561,37 @@ void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable) void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args) { - Q_UNUSED(args); - if (argc > 0) - reject(u"DefineArray"_qs); + INJECT_TRACE_INFO(generate_DefineArray); - m_body += m_state.accumulatorVariableOut + u" = "_qs; - m_body += conversion(m_typeResolver->emptyListType(), m_state.accumulatorOut().storedType(), - QString()); - m_body += u";\n"_qs; - generateOutputVariantConversion(m_typeResolver->emptyListType()); + const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType(); + + if (argc == 0) { + m_body += m_state.accumulatorVariableOut + u" = "_qs; + m_body += conversion(m_typeResolver->emptyListType(), stored, QString()); + m_body += u";\n"_qs; + generateOutputVariantConversion(m_typeResolver->emptyListType()); + return; + } + + if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) { + // This rejects any attempt to store the list into a QVariant. + // Therefore, we don't have to adjust the contained type below. + reject(u"storing an array in a non-sequence type"_qs); + return; + } + + const QQmlJSScope::ConstPtr value = stored->valueType(); + Q_ASSERT(value); + + QStringList initializer; + for (int i = 0; i < argc; ++i) { + initializer += conversion(registerType(args + i).storedType(), value, + registerVariable(args + i)); + } + + m_body += m_state.accumulatorVariableOut + u" = "_qs + stored->internalName() + u'{'; + m_body += initializer.join(u", "_qs); + m_body += u"};\n"; } void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args) diff --git a/src/qmlcompiler/qqmljsfunctioninitializer.cpp b/src/qmlcompiler/qqmljsfunctioninitializer.cpp index c993197399..ce8df3148f 100644 --- a/src/qmlcompiler/qqmljsfunctioninitializer.cpp +++ b/src/qmlcompiler/qqmljsfunctioninitializer.cpp @@ -188,7 +188,7 @@ QQmlJSCompilePass::Function QQmlJSFunctionInitializer::run( const auto property = m_objectType->property(propertyName); function.returnType = property.isList() - ? m_typeResolver->listType(property.type()) + ? m_typeResolver->listType(property.type(), QQmlJSTypeResolver::UseQObjectList) : QQmlJSScope::ConstPtr(property.type()); diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index 1e48a615f6..fbcdccafaf 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -1412,10 +1412,15 @@ void QQmlJSTypePropagator::generate_DeclareVar(int varName, int isDeletable) void QQmlJSTypePropagator::generate_DefineArray(int argc, int args) { - Q_UNUSED(args); setAccumulator(m_typeResolver->globalType(argc == 0 ? m_typeResolver->emptyListType() - : m_typeResolver->jsValueType())); + : m_typeResolver->variantListType())); + + // Track all arguments as the same type. + const QQmlJSRegisterContent elementType + = m_typeResolver->tracked(m_typeResolver->globalType(m_typeResolver->varType())); + for (int i = 0; i < argc; ++i) + addReadRegister(args + i, elementType); } void QQmlJSTypePropagator::generate_DefineObjectLiteral(int internalClassId, int argc, int args) diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 1b0520e70d..766e8513db 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -96,6 +96,8 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer) listPropertyType->setInternalName(u"QQmlListProperty<QObject>"_qs); listPropertyType->setFilePath(u"qqmllist.h"_qs); listPropertyType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence); + listPropertyType->setValueTypeName(u"QObject"_qs); + QQmlJSScope::resolveTypes(listPropertyType, builtinTypes); m_listPropertyType = listPropertyType; QQmlJSScope::Ptr metaObjectType = QQmlJSScope::create(); @@ -177,7 +179,8 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::scopeForId( return m_objectsById.scope(id, referrer); } -QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(const QQmlJSScope::ConstPtr &elementType) const +QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType( + const QQmlJSScope::ConstPtr &elementType, ListMode mode) const { auto it = m_typeTracker->listTypes.find(elementType); if (it != m_typeTracker->listTypes.end()) @@ -185,12 +188,16 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::listType(const QQmlJSScope::ConstPtr & switch (elementType->accessSemantics()) { case QQmlJSScope::AccessSemantics::Reference: - return m_listPropertyType; + if (mode == UseListReference) + return m_listPropertyType; + if (elementType->internalName() != u"QObject"_qs) + return listType(genericType(elementType), mode); + Q_FALLTHROUGH(); case QQmlJSScope::AccessSemantics::Value: { QQmlJSScope::Ptr listType = QQmlJSScope::create(); listType->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence); listType->setValueTypeName(elementType->internalName()); - listType->setInternalName(u"QList<%1>"_qs.arg(elementType->internalName())); + listType->setInternalName(u"QList<%1>"_qs.arg(elementType->augmentedInternalName())); listType->setFilePath(elementType->filePath()); const QQmlJSImportedScope element = {elementType, QTypeRevision()}; QQmlJSScope::resolveTypes(listType, {{elementType->internalName(), element}}); @@ -344,7 +351,9 @@ QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const return container.type(); if (container.isProperty()) { const QQmlJSMetaProperty prop = container.property(); - return prop.isList() ? listType(prop.type()) : QQmlJSScope::ConstPtr(prop.type()); + return prop.isList() + ? listType(prop.type(), UseListReference) + : QQmlJSScope::ConstPtr(prop.type()); } if (container.isEnumeration()) return container.enumeration().type(); @@ -709,7 +718,9 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(const QQmlJSScope::ConstPt return m_intType; if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) { - return listType(genericType(type->valueType())); + return equals(type, m_listPropertyType) + ? type + : listType(genericType(type->valueType()), UseQObjectList); } return m_varType; @@ -977,7 +988,7 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo( if (equals(to, m_jsPrimitiveType)) return isPrimitive(from); - if (equals(from, m_emptyListType)) + if (equals(from, m_emptyListType) || equals(from, m_variantListType)) return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence; const bool matchByName = !to->isComposite(); diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index 32c64b89f7..4426a31f86 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -89,7 +89,8 @@ public: return m_imports.contains(name) && !m_imports[name].scope; } - QQmlJSScope::ConstPtr listType(const QQmlJSScope::ConstPtr &elementType) const; + enum ListMode { UseListReference, UseQObjectList }; + QQmlJSScope::ConstPtr listType(const QQmlJSScope::ConstPtr &elementType, ListMode mode) const; QQmlJSScope::ConstPtr typeForName(const QString &name) const { return m_imports[name].scope; } QQmlJSScope::ConstPtr typeFromAST(QQmlJS::AST::Type *type) const; QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const; |
