diff options
-rw-r--r-- | src/qml/jsapi/qjslist.h | 63 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsbasicblocks.cpp | 6 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsbasicblocks_p.h | 1 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 113 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsregistercontent_p.h | 1 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 73 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 53 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 8 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/iteration.qml | 20 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 12 |
11 files changed, 311 insertions, 40 deletions
diff --git a/src/qml/jsapi/qjslist.h b/src/qml/jsapi/qjslist.h index aeba3ec4b2..f55d669a41 100644 --- a/src/qml/jsapi/qjslist.h +++ b/src/qml/jsapi/qjslist.h @@ -43,6 +43,17 @@ struct QJSList : private QJSListIndexClamp QJSList(List *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} + Value at(int index) const + { + Q_ASSERT(index >= 0 && index < size()); + return *(m_list->cbegin() + index); + } + + int size() const + { + return int(std::min(m_list->size(), qsizetype(std::numeric_limits<int>::max()))); + } + void resize(int size) { m_list->resize(size); @@ -157,6 +168,17 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam QJSList(QQmlListProperty<QObject> *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} + QObject *at(int index) const + { + Q_ASSERT(index >= 0 && index < size()); + return m_list->at(m_list, index); + } + + int size() const + { + return int(std::min(m_list->count(m_list), qsizetype(std::numeric_limits<int>::max()))); + } + void resize(int size) { qsizetype current = m_list->count(m_list); @@ -308,6 +330,47 @@ private: QJSEngine *m_engine = nullptr; }; +struct QJSListForInIterator +{ +public: + using Ptr = QJSListForInIterator *; + template<typename List, typename Value> + void init(const QJSList<List, Value> &list) + { + m_index = 0; + m_size = list.size(); + } + + bool hasNext() const { return m_index < m_size; } + int next() { return m_index++; } + +private: + int m_index; + int m_size; +}; + +// QJSListForInIterator must not require initialization so that we can jump over it with goto. +static_assert(std::is_trivial_v<QJSListForInIterator>); + +struct QJSListForOfIterator +{ +public: + using Ptr = QJSListForOfIterator *; + void init() { m_index = 0; } + + template<typename List, typename Value> + bool hasNext(const QJSList<List, Value> &list) const { return m_index < list.size(); } + + template<typename List, typename Value> + Value next(const QJSList<List, Value> &list) { return list.at(m_index++); } + +private: + int m_index; +}; + +// QJSListForOfIterator must not require initialization so that we can jump over it with goto. +static_assert(std::is_trivial_v<QJSListForOfIterator>); + QT_END_NAMESPACE #endif // QJSLIST_H diff --git a/src/qmlcompiler/qqmljsbasicblocks.cpp b/src/qmlcompiler/qqmljsbasicblocks.cpp index 6a535b9582..fc8f5b91fe 100644 --- a/src/qmlcompiler/qqmljsbasicblocks.cpp +++ b/src/qmlcompiler/qqmljsbasicblocks.cpp @@ -225,6 +225,12 @@ void QQmlJSBasicBlocks::generate_JumpNotUndefined(int offset) processJump(offset, Conditional); } +void QQmlJSBasicBlocks::generate_IteratorNext(int value, int offset) +{ + Q_UNUSED(value); + processJump(offset, Conditional); +} + void QQmlJSBasicBlocks::generate_Ret() { auto currentBlock = basicBlockForInstruction(m_basicBlocks, currentInstructionOffset()); diff --git a/src/qmlcompiler/qqmljsbasicblocks_p.h b/src/qmlcompiler/qqmljsbasicblocks_p.h index dd5397b753..71a47a3bd1 100644 --- a/src/qmlcompiler/qqmljsbasicblocks_p.h +++ b/src/qmlcompiler/qqmljsbasicblocks_p.h @@ -80,6 +80,7 @@ private: void generate_JumpFalse(int offset) override; void generate_JumpNoException(int offset) override; void generate_JumpNotUndefined(int offset) override; + void generate_IteratorNext(int value, int offset) override; void generate_Ret() override; void generate_ThrowException() override; diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index d62e899dbc..24a308d4b6 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -674,30 +674,54 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base) const QQmlJSRegisterContent baseType = registerType(base); - if (!m_typeResolver->isNumeric(m_state.accumulatorIn()) - || (!baseType.isList() - && !m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType()))) { - reject(u"LoadElement with non-list base type or non-numeric arguments"_s); + if (!baseType.isList() + && !m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->stringType())) { + reject(u"LoadElement with non-list base type "_s + baseType.descriptiveName()); return; } - AccumulatorConverter registers(this); - - const QString baseName = registerVariable(base); - const QString indexName = m_state.accumulatorVariableIn; - const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s + conversion(m_typeResolver->globalType(m_typeResolver->voidType()), m_state.accumulatorOut(), QString()) + u";\n"_s; - if (!m_typeResolver->isIntegral(m_state.accumulatorIn())) { + QString indexName = m_state.accumulatorVariableIn; + QQmlJSScope::ConstPtr indexType; + if (m_typeResolver->isNumeric(m_state.accumulatorIn())) { + indexType = m_typeResolver->containedType(m_state.accumulatorIn()); + } else if (m_state.accumulatorIn().isConversion()) { + const auto origins = m_state.accumulatorIn().conversionOrigins(); + if (origins.size() == 2) { + const auto target = m_typeResolver->equals(origins[0], m_typeResolver->voidType()) + ? origins[1] + : origins[0]; + + if (m_typeResolver->isNumeric(target)) { + indexType = target; + m_body += u"if (!" + indexName + u".metaType().isValid())\n" + + voidAssignment + + u"else "; + indexName = convertStored( + m_state.accumulatorIn().storedType(), indexType, indexName); + } else { + reject(u"LoadElement with non-numeric argument"_s); + return; + } + } else { + reject(u"LoadElement with non-numeric argument"_s); + return; + } + } + + AccumulatorConverter registers(this); + const QString baseName = registerVariable(base); + + if (!m_typeResolver->isIntegral(indexType)) { m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s + voidAssignment + u"else "_s; } - if (!m_typeResolver->isUnsignedInteger( - m_typeResolver->containedType(m_state.accumulatorIn()))) { + if (!m_typeResolver->isUnsignedInteger(indexType)) { m_body += u"if ("_s + indexName + u" < 0)\n"_s + voidAssignment + u"else "_s; @@ -2295,15 +2319,70 @@ void QQmlJSCodeGenerator::generate_PopContext() void QQmlJSCodeGenerator::generate_GetIterator(int iterator) { - Q_UNUSED(iterator) - BYTECODE_UNIMPLEMENTED(); + INJECT_TRACE_INFO(generate_GetIterator); + + addInclude(u"qjslist.h"_s); + const QQmlJSRegisterContent listType = m_state.accumulatorIn(); + if (!listType.isList()) + reject(u"iterator on non-list type"_s); + + const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut(); + + const QString identifier = QString::number(iteratorType.baseLookupIndex()); + const QString iteratorName = m_state.accumulatorVariableOut + u"Iterator" + identifier; + const QString listName = m_state.accumulatorVariableOut + u"List" + identifier; + + m_body += u"QJSListFor"_s + + (iterator == int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s) + + u"Iterator "_s + iteratorName + u";\n"; + m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n"; + + m_body += m_state.accumulatorVariableOut + u"->init("; + if (iterator == int(QQmlJS::AST::ForEachType::In)) { + if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forInIteratorPtr())) + reject(u"using non-iterator as iterator"_s); + m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)"; + } + m_body += u");\n"; + + if (iterator == int(QQmlJS::AST::ForEachType::Of)) { + if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forOfIteratorPtr())) + reject(u"using non-iterator as iterator"_s); + m_body += u"const auto &" // Rely on life time extension for const refs + + listName + u" = " + consumedAccumulatorVariableIn(); + } } void QQmlJSCodeGenerator::generate_IteratorNext(int value, int offset) { - Q_UNUSED(value) - Q_UNUSED(offset) - BYTECODE_UNIMPLEMENTED(); + INJECT_TRACE_INFO(generate_IteratorNext); + + Q_ASSERT(value == m_state.changedRegisterIndex()); + const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn(); + const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType(); + + const QString iteratorTypeName = iteratorType->internalName(); + const QString listName = m_state.accumulatorVariableIn + + u"List" + QString::number(iteratorContent.baseLookupIndex()); + QString qjsList; + if (m_typeResolver->equals(iteratorType, m_typeResolver->forOfIteratorPtr())) + qjsList = u"QJSList(&" + listName + u", aotContext->engine)"; + else if (!m_typeResolver->equals(iteratorType, m_typeResolver->forInIteratorPtr())) + reject(u"using non-iterator as iterator"_s); + + m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n "; + m_body += changedRegisterVariable() + u" = " + + conversion( + m_typeResolver->valueType(iteratorContent), + m_state.changedRegister(), + m_state.accumulatorVariableIn + u"->next(" + qjsList + u')') + + u";\n"; + m_body += u"} else {\n "; + m_body += changedRegisterVariable() + u" = " + + conversion(m_typeResolver->voidType(), m_state.changedRegister(), QString()); + m_body += u";\n "; + generateJumpCodeWithTypeConversions(offset); + m_body += u"\n}"_s; } void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(int iterator, int object, int offset) diff --git a/src/qmlcompiler/qqmljsregistercontent_p.h b/src/qmlcompiler/qqmljsregistercontent_p.h index 2374f89f36..29b7e7e010 100644 --- a/src/qmlcompiler/qqmljsregistercontent_p.h +++ b/src/qmlcompiler/qqmljsregistercontent_p.h @@ -56,6 +56,7 @@ public: JavaScriptReturnValue, ListValue, + ListIterator, Builtin, Unknown, }; diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index 21e87a9321..6f47498ea0 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -700,27 +700,47 @@ bool QQmlJSTypePropagator::checkForEnumProblems( void QQmlJSTypePropagator::generate_LoadElement(int base) { - const QQmlJSRegisterContent baseRegister = m_state.registers[base].content; - - if ((!baseRegister.isList() - && !m_typeResolver->registerContains(baseRegister, m_typeResolver->stringType())) - || !m_typeResolver->isNumeric(m_state.accumulatorIn())) { + const auto fallback = [&]() { const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType()); addReadAccumulator(jsValue); addReadRegister(base, jsValue); setAccumulator(jsValue); + }; + + const QQmlJSRegisterContent baseRegister = m_state.registers[base].content; + if (!baseRegister.isList() + && !m_typeResolver->registerContains(baseRegister, m_typeResolver->stringType())) { + fallback(); return; } + addReadRegister(base, baseRegister); - const auto contained = m_typeResolver->containedType(m_state.accumulatorIn()); - if (m_typeResolver->isSignedInteger(contained)) - addReadAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type())); - else if (m_typeResolver->isUnsignedInteger(contained)) - addReadAccumulator(m_typeResolver->globalType(m_typeResolver->uint32Type())); - else - addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType())); + if (m_typeResolver->isNumeric(m_state.accumulatorIn())) { + const auto contained = m_typeResolver->containedType(m_state.accumulatorIn()); + if (m_typeResolver->isSignedInteger(contained)) + addReadAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type())); + else if (m_typeResolver->isUnsignedInteger(contained)) + addReadAccumulator(m_typeResolver->globalType(m_typeResolver->uint32Type())); + else + addReadAccumulator(m_typeResolver->globalType(m_typeResolver->realType())); + } else if (m_state.accumulatorIn().isConversion()) { + const auto origins = m_state.accumulatorIn().conversionOrigins(); + const bool isOptionalNumber = origins.length() == 2 + && ((m_typeResolver->isNumeric(origins[0]) + && m_typeResolver->equals(origins[1], m_typeResolver->voidType())) + || (m_typeResolver->isNumeric(origins[1]) + && m_typeResolver->equals(origins[0], m_typeResolver->voidType()))); + if (isOptionalNumber) { + addReadAccumulator(m_state.accumulatorIn()); + } else { + fallback(); + return; + } + } else { + fallback(); + return; + } - addReadRegister(base, baseRegister); // We can end up with undefined. setAccumulator(m_typeResolver->merge( m_typeResolver->valueType(baseRegister), @@ -1893,15 +1913,28 @@ void QQmlJSTypePropagator::generate_PopContext() void QQmlJSTypePropagator::generate_GetIterator(int iterator) { - Q_UNUSED(iterator) - INSTR_PROLOGUE_NOT_IMPLEMENTED(); + const QQmlJSRegisterContent listType = m_state.accumulatorIn(); + if (!listType.isList()) { + const auto jsValue = m_typeResolver->globalType(m_typeResolver->jsValueType()); + addReadAccumulator(jsValue); + setAccumulator(jsValue); + return; + } + + addReadAccumulator(listType); + setAccumulator(m_typeResolver->iteratorPointer( + listType, QQmlJS::AST::ForEachType(iterator), currentInstructionOffset())); } void QQmlJSTypePropagator::generate_IteratorNext(int value, int offset) { - Q_UNUSED(value) - Q_UNUSED(offset) - INSTR_PROLOGUE_NOT_IMPLEMENTED(); + const QQmlJSRegisterContent iteratorType = m_state.accumulatorIn(); + addReadAccumulator(iteratorType); + setRegister(value, m_typeResolver->merge( + m_typeResolver->valueType(iteratorType), + m_typeResolver->globalType(m_typeResolver->voidType()))); + saveRegisterStateForJump(offset); + m_state.setHasSideEffects(true); } void QQmlJSTypePropagator::generate_IteratorNextForYieldStar(int iterator, int object, int offset) @@ -1914,7 +1947,7 @@ void QQmlJSTypePropagator::generate_IteratorNextForYieldStar(int iterator, int o void QQmlJSTypePropagator::generate_IteratorClose() { - INSTR_PROLOGUE_NOT_IMPLEMENTED(); + // Noop } void QQmlJSTypePropagator::generate_DestructureRestElement() @@ -2587,6 +2620,8 @@ void QQmlJSTypePropagator::endInstruction(QV4::Moth::Instr::Type instr) case QV4::Moth::Instr::Type::InitializeBlockDeadTemporalZone: case QV4::Moth::Instr::Type::ConvertThisToObject: case QV4::Moth::Instr::Type::DeadTemporalZoneCheck: + case QV4::Moth::Instr::Type::IteratorNext: + case QV4::Moth::Instr::Type::IteratorNextForYieldStar: if (m_state.changedRegisterIndex() == Accumulator && !m_error->isValid()) { setError(u"Instruction is not expected to populate the accumulator"_s); return; diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 9a34d76e7c..6790a94db2 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -102,6 +102,18 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer) m_metaObjectType = metaObjectType; m_jsGlobalObject = importer->jsGlobalObject(); + + QQmlJSScope::Ptr forInIteratorPtr = QQmlJSScope::create(); + forInIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value); + forInIteratorPtr->setFilePath(u"qjslist.h"_s); + forInIteratorPtr->setInternalName(u"QJSListForInIterator::Ptr"_s); + m_forInIteratorPtr = forInIteratorPtr; + + QQmlJSScope::Ptr forOfIteratorPtr = QQmlJSScope::create(); + forOfIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value); + forOfIteratorPtr->setFilePath(u"qjslist.h"_s); + forOfIteratorPtr->setInternalName(u"QJSListForOfIterator::Ptr"_s); + m_forOfIteratorPtr = forOfIteratorPtr; } /*! @@ -863,7 +875,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType( m_stringListType, m_byteArrayType, m_urlType, m_dateTimeType, m_dateType, m_timeType, m_variantListType, m_variantMapType, m_varType, m_jsValueType, m_jsPrimitiveType, m_listPropertyType, m_qObjectType, m_qObjectListType, - m_metaObjectType }) { + m_metaObjectType, m_forInIteratorPtr, m_forOfIteratorPtr }) { if (equals(type, builtin) || equals(type, builtin->listType())) return type; } @@ -1485,13 +1497,22 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent QQmlJSScope::ConstPtr scope; QQmlJSScope::ConstPtr value; - auto valueType = [this](const QQmlJSScope::ConstPtr &scope) { + auto valueType = [&](const QQmlJSScope::ConstPtr &scope) { if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) return scope->valueType(); - else if (equals(scope, m_jsValueType) || equals(scope, m_varType)) + + if (equals(scope, m_forInIteratorPtr)) + return m_int32Type; + + if (equals(scope, m_forOfIteratorPtr)) + return list.scopeType()->valueType(); + + if (equals(scope, m_jsValueType) || equals(scope, m_varType)) return m_jsValueType; - else if (equals(scope, m_stringType)) + + if (equals(scope, m_stringType)) return m_stringType; + return QQmlJSScope::ConstPtr(); }; @@ -1529,6 +1550,30 @@ QQmlJSRegisterContent QQmlJSTypeResolver::returnType( storedType(type), type, QQmlJSRegisterContent::InvalidLookupIndex, variant, scope); } +QQmlJSRegisterContent QQmlJSTypeResolver::iteratorPointer( + const QQmlJSRegisterContent &listType, QQmlJS::AST::ForEachType type, + int lookupIndex) const +{ + const QQmlJSScope::ConstPtr value = (type == QQmlJS::AST::ForEachType::In) + ? m_int32Type + : containedType(valueType(listType)); + + QQmlJSScope::ConstPtr iteratorPointer = type == QQmlJS::AST::ForEachType::In + ? m_forInIteratorPtr + : m_forOfIteratorPtr; + + const QQmlJSScope::ConstPtr listContained = containedType(listType); + + QQmlJSMetaProperty prop; + prop.setPropertyName(u"<>"_s); + prop.setTypeName(iteratorPointer->internalName()); + prop.setType(iteratorPointer); + return QQmlJSRegisterContent::create( + storedType(iteratorPointer), prop, lookupIndex, + QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::ListIterator, + listContained); +} + bool QQmlJSTypeResolver::registerIsStoredIn( const QQmlJSRegisterContent ®, const QQmlJSScope::ConstPtr &type) const { diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index 7b8f714289..d2f043f32d 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -72,6 +72,8 @@ public: QQmlJSScope::ConstPtr qObjectType() const { return m_qObjectType; } QQmlJSScope::ConstPtr qObjectListType() const { return m_qObjectListType; } QQmlJSScope::ConstPtr arrayPrototype() const { return m_arrayPrototype; } + QQmlJSScope::ConstPtr forInIteratorPtr() const { return m_forInIteratorPtr; } + QQmlJSScope::ConstPtr forOfIteratorPtr() const { return m_forOfIteratorPtr; } QQmlJSScope::ConstPtr scopeForLocation(const QV4::CompiledData::Location &location) const; QQmlJSScope::ConstPtr scopeForId( @@ -125,6 +127,10 @@ public: const QQmlJSScope::ConstPtr &type, QQmlJSRegisterContent::ContentVariant variant, const QQmlJSScope::ConstPtr &scope) const; + QQmlJSRegisterContent iteratorPointer( + const QQmlJSRegisterContent &listType, QQmlJS::AST::ForEachType type, + int lookupIndex) const; + bool registerIsStoredIn(const QQmlJSRegisterContent ®, const QQmlJSScope::ConstPtr &type) const; bool registerContains(const QQmlJSRegisterContent ®, @@ -254,6 +260,8 @@ protected: QQmlJSScope::ConstPtr m_metaObjectType; QQmlJSScope::ConstPtr m_functionType; QQmlJSScope::ConstPtr m_jsGlobalObject; + QQmlJSScope::ConstPtr m_forInIteratorPtr; + QQmlJSScope::ConstPtr m_forOfIteratorPtr; QQmlJSScopesById m_objectsById; QHash<QV4::CompiledData::Location, QQmlJSScope::ConstPtr> m_objectsByLocation; diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 021f56f60d..2ec9e33263 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -144,6 +144,7 @@ set(qml_files invisibleListElementType.qml invisibleTypes.qml isnan.qml + iteration.qml javaScriptArgument.qml jsArrayMethods.qml jsArrayMethodsUntyped.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/iteration.qml b/tests/auto/qml/qmlcppcodegen/data/iteration.qml new file mode 100644 index 0000000000..8632eefa1b --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/iteration.qml @@ -0,0 +1,20 @@ +pragma Strict + +import QtQml + +QtObject { + property list<int> ints: [3, 4, 5] + property list<QtObject> objects: [ + QtObject { objectName: "a" }, + QtObject { objectName: "b" }, + QtObject { objectName: "c" } + ] + + Component.onCompleted: { + for (var a in objects) { + objectName += objects[a].objectName; + for (var b in ints) + objectName += ints[b]; + } + } +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index 6070503028..5f79aa575d 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -119,6 +119,7 @@ private slots: void invisibleListElementType(); void invisibleSingleton(); void invisibleTypes(); + void iteration(); void javaScriptArgument(); void jsArrayMethods(); void jsArrayMethodsWithParams(); @@ -2270,6 +2271,17 @@ void tst_QmlCppCodegen::invisibleTypes() // QCOMPARE(meta->className(), "DerivedFromInvisible"); } +void tst_QmlCppCodegen::iteration() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/iteration.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + QCOMPARE(o->objectName(), "a345b345c345"_L1); +} + void tst_QmlCppCodegen::javaScriptArgument() { QQmlEngine engine; |