aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsapi/qjslist.h63
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks.cpp6
-rw-r--r--src/qmlcompiler/qqmljsbasicblocks_p.h1
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp113
-rw-r--r--src/qmlcompiler/qqmljsregistercontent_p.h1
-rw-r--r--src/qmlcompiler/qqmljstypepropagator.cpp73
-rw-r--r--src/qmlcompiler/qqmljstyperesolver.cpp53
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h8
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/iteration.qml20
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp12
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 &reg, 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 &reg,
const QQmlJSScope::ConstPtr &type) const;
bool registerContains(const QQmlJSRegisterContent &reg,
@@ -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;