diff options
author | Ulf Hermann <[email protected]> | 2023-03-27 18:00:46 +0200 |
---|---|---|
committer | Ulf Hermann <[email protected]> | 2023-03-30 15:46:55 +0200 |
commit | 03ff348b4942ae29dd5bc2bd563998d7ba82ecd7 (patch) | |
tree | 8077bce26455108b449bb7bb9a3974a2b424cf3b | |
parent | 407ed344dadec77ae8ddb976a8ab1acfedcdb86d (diff) |
QmlCompiler: Support more integer types
This adds support for 8- and 16-bit signed and unsigned integer types.
The test exposes that the engine fails to correctly convert out of range
values when assigning to a 32-bit int property. Fix that as drive-by.
Fixes: QTBUG-101634
Change-Id: I0a4177f49ffc062a1f444e30424e94c1f293e70c
Reviewed-by: Fabian Kosmale <[email protected]>
Reviewed-by: Qt CI Bot <[email protected]>
-rw-r--r-- | src/imports/builtins/builtins.qmltypes | 32 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 2 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 96 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 67 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 100 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 23 | ||||
-rw-r--r-- | src/qmltyperegistrar/qqmltypescreator.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml | 42 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 83 |
9 files changed, 320 insertions, 129 deletions
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes index 7a74279ee8..98f2d7ac75 100644 --- a/src/imports/builtins/builtins.qmltypes +++ b/src/imports/builtins/builtins.qmltypes @@ -168,6 +168,30 @@ Module { } Component { + name: "qint8" + extension: "Number" + accessSemantics: "value" + } + + Component { + name: "quint8" + extension: "Number" + accessSemantics: "value" + } + + Component { + name: "short" + extension: "Number" + accessSemantics: "value" + } + + Component { + name: "ushort" + extension: "Number" + accessSemantics: "value" + } + + Component { name: "int" extension: "Number" exports: ["QML/int 1.0"] @@ -182,13 +206,13 @@ Module { } Component { - name: "qlonglong" - accessSemantics: "value" + name: "qlonglong" + accessSemantics: "value" } Component { - name: "qulonglong" - accessSemantics: "value" + name: "qulonglong" + accessSemantics: "value" } Component { diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 694b7828b6..e2ebda7d0e 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -602,7 +602,7 @@ void QObjectWrapper::setProperty( scope.engine->throwError(error); return; } else if (propType == QMetaType::fromType<int>() && value.isNumber()) { - PROPERTY_STORE(int, value.asDouble()); + PROPERTY_STORE(int, value.toInt32()); } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) { PROPERTY_STORE(qreal, qreal(value.asDouble())); } else if (propType == QMetaType::fromType<float>() && value.isNumber()) { diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index c7a1940907..265347a577 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -336,7 +336,7 @@ void QQmlJSCodeGenerator::generate_LoadConst(int index) m_body += conversion( type, m_state.accumulatorOut(), toNumericString(value.doubleValue())); - } else if (type == m_typeResolver->intType()) { + } else if (type == m_typeResolver->int32Type()) { m_body += conversion( type, m_state.accumulatorOut(), QString::number(value.integerValue())); @@ -365,7 +365,7 @@ void QQmlJSCodeGenerator::generate_LoadZero() m_body += m_state.accumulatorVariableOut; m_body += u" = "_s + conversion( - m_typeResolver->intType(), m_state.accumulatorOut(), u"0"_s); + m_typeResolver->int32Type(), m_state.accumulatorOut(), u"0"_s); m_body += u";\n"_s; } @@ -415,7 +415,7 @@ void QQmlJSCodeGenerator::generate_LoadInt(int value) m_body += m_state.accumulatorVariableOut; m_body += u" = "_s; - m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut(), + m_body += conversion(m_typeResolver->int32Type(), m_state.accumulatorOut(), QString::number(value)); m_body += u";\n"_s; } @@ -446,7 +446,7 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp) contained = m_typeResolver->boolType(); input = v4Value.booleanValue() ? u"true"_s : u"false"_s; } else if (v4Value.isInteger()) { - contained = m_typeResolver->intType(); + contained = m_typeResolver->int32Type(); input = QString::number(v4Value.int_32()); } else if (v4Value.isDouble()) { contained = m_typeResolver->realType(); @@ -691,15 +691,21 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base) + u"else "_s; } + if (!m_typeResolver->isUnsignedInteger( + m_typeResolver->containedType(m_state.accumulatorIn()))) { + m_body += u"if ("_s + indexName + u" < 0)\n"_s + + voidAssignment + + u"else "_s; + } + if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) { // Our QQmlListProperty only keeps plain QObject*. const auto valueType = m_typeResolver->valueType(baseType); const auto elementType = m_typeResolver->globalType( m_typeResolver->genericType(m_typeResolver->containedType(valueType))); - m_body += u"if ("_s + indexName + u" >= 0 && "_s + indexName - + u" < "_s + baseName + u".count(&"_s + baseName - + u"))\n"_s; + m_body += u"if ("_s + indexName + u" < "_s + baseName + + u".count(&"_s + baseName + u"))\n"_s; m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s + conversion(elementType, m_state.accumulatorOut(), baseName + u".at(&"_s + baseName + u", "_s @@ -719,8 +725,7 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base) else if (!m_typeResolver->canUseValueTypes()) reject(u"LoadElement in sequence type reference"_s); - m_body += u"if ("_s + indexName + u" >= 0 && "_s + indexName - + u" < "_s + baseName + u".size())\n"_s; + m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s; m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s + conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s; m_body += u"else\n"_s @@ -757,8 +762,9 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index) m_body += u"if ("_s; if (!m_typeResolver->isIntegral(indexType)) m_body += u"QJSNumberCoercion::isInteger("_s + indexName + u") && "_s; - m_body += indexName + u" >= 0 && "_s - + indexName + u" < "_s + baseName + u".count(&"_s + baseName + if (!m_typeResolver->isUnsignedInteger(m_typeResolver->containedType(indexType))) + m_body += indexName + u" >= 0 && "_s; + m_body += indexName + u" < "_s + baseName + u".count(&"_s + baseName + u"))\n"_s; m_body += u" "_s + baseName + u".replace(&"_s + baseName + u", "_s + indexName + u", "_s; @@ -1067,7 +1073,8 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index) && m_jsUnitGenerator->lookupName(index) == u"length"_s) { m_body += m_state.accumulatorVariableOut + u" = "_s; m_body += conversion( - m_typeResolver->globalType(m_typeResolver->intType()), m_state.accumulatorOut(), + m_typeResolver->globalType(m_typeResolver->int32Type()), + m_state.accumulatorOut(), m_state.accumulatorVariableIn + u".count("_s + u'&' + m_state.accumulatorVariableIn + u')'); m_body += u";\n"_s; @@ -1084,7 +1091,7 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index) == QQmlJSScope::AccessSemantics::Sequence) && m_jsUnitGenerator->lookupName(index) == u"length"_s) { m_body += m_state.accumulatorVariableOut + u" = "_s - + conversion(m_typeResolver->globalType(m_typeResolver->intType()), + + conversion(m_typeResolver->globalType(m_typeResolver->int32Type()), m_state.accumulatorOut(), m_state.accumulatorVariableIn + u".length()"_s) + u";\n"_s; @@ -1393,16 +1400,10 @@ bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int const QQmlJSRegisterContent input = m_state.readRegister(argv); m_body += m_state.accumulatorVariableOut + u" = "_s; - if (m_typeResolver->registerContains(input, m_typeResolver->intType())) - m_body += ret(arg(m_typeResolver->intType())); - else if (m_typeResolver->registerContains(input, m_typeResolver->uintType())) - m_body += ret(arg(m_typeResolver->uintType())); + if (m_typeResolver->isNumeric(input)) + m_body += ret(arg(m_typeResolver->containedType(input))); else if (m_typeResolver->registerContains(input, m_typeResolver->boolType())) m_body += ret(arg(m_typeResolver->boolType())); - else if (m_typeResolver->registerContains(input, m_typeResolver->realType())) - m_body += ret(arg(m_typeResolver->realType())); - else if (m_typeResolver->registerContains(input, m_typeResolver->floatType())) - m_body += ret(arg(m_typeResolver->floatType())); else m_body += ret(arg(m_typeResolver->stringType())); m_body += u";\n"_s; @@ -1426,7 +1427,7 @@ bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, i }; const auto intArg = [&](int i) { - return i < argc ? arg(i, m_typeResolver->intType()) : u"-1"_s; + return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s; }; const auto stringRet = [&](const QString &expression) { @@ -2207,27 +2208,24 @@ void QQmlJSCodeGenerator::generate_CmpNeNull() QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst) { - if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->intType()) - || m_typeResolver->registerIsStoredIn( - m_state.accumulatorIn(), m_typeResolver->uintType())) { + if (m_typeResolver->isIntegral(m_state.accumulatorIn())) return QString::number(lhsConst) + u" == "_s + m_state.accumulatorVariableIn; - } if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->boolType())) { return QString::number(lhsConst) + u" == "_s - + convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->intType(), + + convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->int32Type(), consumedAccumulatorVariableIn()); } if (m_typeResolver->isNumeric(m_state.accumulatorIn())) { - return convertStored(m_typeResolver->intType(), m_typeResolver->realType(), + return convertStored(m_typeResolver->int32Type(), m_typeResolver->realType(), QString::number(lhsConst)) + u" == "_s + convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->realType(), consumedAccumulatorVariableIn()); } QString result; - result += convertStored(m_typeResolver->intType(), m_typeResolver->jsPrimitiveType(), + result += convertStored(m_typeResolver->int32Type(), m_typeResolver->jsPrimitiveType(), QString::number(lhsConst)); result += u".equals("_s; result += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(), @@ -2264,7 +2262,7 @@ QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return u'&' + var; - if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->intType()) + if (m_typeResolver->isNumeric(content.storedType()) && m_typeResolver->containedType(content)->scopeType() == QQmlJSScope::EnumScope) { return u'&' + var; } @@ -2292,10 +2290,8 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return metaTypeFromName(contained); - if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->intType()) - && m_typeResolver->containedType(content)->scopeType() == QQmlJSScope::EnumScope) { - return metaTypeFromType(m_typeResolver->intType()); - } + if (m_typeResolver->isNumeric(stored) && contained->scopeType() == QQmlJSScope::EnumScope) + return metaTypeFromType(contained->baseType()); if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty()) return metaTypeFromType(m_typeResolver->listPropertyType()); @@ -2832,7 +2828,7 @@ void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const Q generateArithmeticOperation( conversion(m_state.accumulatorIn(), m_state.readAccumulator(), consumedAccumulatorVariableIn()), - conversion(m_typeResolver->globalType(m_typeResolver->intType()), + conversion(m_typeResolver->globalType(m_typeResolver->int32Type()), m_state.readAccumulator(), QString::number(rhsConst)), cppOperator); } @@ -3017,7 +3013,7 @@ QString QQmlJSCodeGenerator::conversion( const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s; if (m_typeResolver->equals(contained, m_typeResolver->boolType())) return conversion.arg(u"Boolean"_s); - if (m_typeResolver->equals(contained, m_typeResolver->intType())) + if (m_typeResolver->isIntegral(to)) return conversion.arg(u"Integer"_s); if (m_typeResolver->equals(contained, m_typeResolver->realType())) return conversion.arg(u"Double"_s); @@ -3027,12 +3023,12 @@ QString QQmlJSCodeGenerator::conversion( } if (m_typeResolver->registerIsStoredIn(to, contained) - || m_typeResolver->registerIsStoredIn(from, m_typeResolver->intType()) + || m_typeResolver->isNumeric(from.storedType()) || to.storedType()->isReferenceType() || m_typeResolver->registerContains(from, contained)) { // If: // * the output is not actually wrapped at all, or - // * the input is stored in an int (as there are no internals to an int), or + // * the input is stored in a numeric type (as there are no internals to a number), or // * the output is a QObject pointer, or // * we merely wrap the value into a new container, // we can convert by stored type. @@ -3057,9 +3053,9 @@ QString QQmlJSCodeGenerator::convertStored( auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) { if (m_typeResolver->equals(to, boolType)) return u"false"_s; - if (m_typeResolver->equals(to, m_typeResolver->intType())) + if (m_typeResolver->isSignedInteger(to)) return u"0"_s; - if (m_typeResolver->equals(to, m_typeResolver->uintType())) + if (m_typeResolver->isUnsignedInteger(to)) return u"0u"_s; return QString(); }; @@ -3153,9 +3149,9 @@ QString QQmlJSCodeGenerator::convertStored( if (m_typeResolver->equals(from, m_typeResolver->realType()) || m_typeResolver->equals(from, m_typeResolver->floatType())) { - if (m_typeResolver->equals(to, m_typeResolver->intType())) + if (m_typeResolver->isSignedInteger(to)) return u"QJSNumberCoercion::toInteger("_s + variable + u')'; - if (m_typeResolver->equals(to, m_typeResolver->uintType())) + if (m_typeResolver->isUnsignedInteger(to)) return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s; if (m_typeResolver->equals(to, m_typeResolver->boolType())) return u'(' + variable + u" && !std::isnan("_s + variable + u"))"_s; @@ -3170,9 +3166,9 @@ QString QQmlJSCodeGenerator::convertStored( return variable + u".toDouble()"_s; if (m_typeResolver->equals(to, boolType)) return variable + u".toBoolean()"_s; - if (m_typeResolver->equals(to, m_typeResolver->intType())) + if (m_typeResolver->isSignedInteger(to)) return variable + u".toInteger()"_s; - if (m_typeResolver->equals(to, m_typeResolver->uintType())) + if (m_typeResolver->isUnsignedInteger(to)) return u"uint("_s + variable + u".toInteger())"_s; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return variable + u".toString()"_s; @@ -3198,10 +3194,14 @@ QString QQmlJSCodeGenerator::convertStored( Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->voidType())); if (m_typeResolver->equals(from, m_typeResolver->boolType()) - || m_typeResolver->equals(from, m_typeResolver->intType()) + || m_typeResolver->equals(from, m_typeResolver->int32Type()) || m_typeResolver->equals(from, m_typeResolver->realType()) || m_typeResolver->equals(from, m_typeResolver->stringType())) { return u"QJSPrimitiveValue("_s + variable + u')'; + } else if (m_typeResolver->isSignedInteger(from) + || m_typeResolver->equals(from, m_typeResolver->uint16Type()) + || m_typeResolver->equals(from, m_typeResolver->uint8Type())) { + return u"QJSPrimitiveValue(int("_s + variable + u"))"_s; } else if (m_typeResolver->isNumeric(from)) { return u"QJSPrimitiveValue(double("_s + variable + u"))"_s; } @@ -3264,9 +3264,9 @@ QString QQmlJSCodeGenerator::convertStored( { if (m_typeResolver->equals(type, m_typeResolver->boolType())) return expression + u".toBoolean()"_s; - if (m_typeResolver->equals(type, m_typeResolver->intType())) + if (m_typeResolver->isSignedInteger(type)) return expression + u".toInteger()"_s; - if (m_typeResolver->equals(type, m_typeResolver->uintType())) + if (m_typeResolver->isUnsignedInteger(type)) return u"uint("_s + expression + u".toInteger())"_s; if (m_typeResolver->equals(type, m_typeResolver->realType())) return expression + u".toDouble()"_s; @@ -3321,7 +3321,7 @@ QString QQmlJSCodeGenerator::convertContained(const QQmlJSRegisterContent &from, // Those should be handled before, by convertStored(). Q_ASSERT(!to.storedType()->isReferenceType()); Q_ASSERT(!m_typeResolver->registerIsStoredIn(to, containedTo)); - Q_ASSERT(!m_typeResolver->registerIsStoredIn(from, m_typeResolver->intType())); + Q_ASSERT(!m_typeResolver->isIntegral(from.storedType())); Q_ASSERT(!m_typeResolver->equals(containedFrom, containedTo)); if (!m_typeResolver->registerIsStoredIn(to, m_typeResolver->varType()) && diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index 67b12d299f..57844dee61 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -122,7 +122,7 @@ void QQmlJSTypePropagator::generate_LoadConst(int index) void QQmlJSTypePropagator::generate_LoadZero() { - setAccumulator(m_typeResolver->globalType(m_typeResolver->intType())); + setAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type())); } void QQmlJSTypePropagator::generate_LoadTrue() @@ -147,7 +147,7 @@ void QQmlJSTypePropagator::generate_LoadUndefined() void QQmlJSTypePropagator::generate_LoadInt(int) { - setAccumulator(m_typeResolver->globalType(m_typeResolver->intType())); + setAccumulator(m_typeResolver->globalType(m_typeResolver->int32Type())); } void QQmlJSTypePropagator::generate_MoveConst(int constIndex, int destTemp) @@ -669,8 +669,11 @@ void QQmlJSTypePropagator::generate_LoadElement(int base) return; } - if (m_typeResolver->isIntegral(m_state.accumulatorIn())) - addReadAccumulator(m_typeResolver->globalType(m_typeResolver->intType())); + 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())); @@ -699,8 +702,11 @@ void QQmlJSTypePropagator::generate_StoreElement(int base, int index) return; } - if (m_typeResolver->isIntegral(indexRegister)) - addReadRegister(index, m_typeResolver->globalType(m_typeResolver->intType())); + const auto contained = m_typeResolver->containedType(indexRegister); + if (m_typeResolver->isSignedInteger(contained)) + addReadRegister(index, m_typeResolver->globalType(m_typeResolver->int32Type())); + else if (m_typeResolver->isUnsignedInteger(contained)) + addReadRegister(index, m_typeResolver->globalType(m_typeResolver->uint32Type())); else addReadRegister(index, m_typeResolver->globalType(m_typeResolver->realType())); @@ -1261,7 +1267,7 @@ bool QQmlJSTypePropagator::propagateTranslationMethod( const QQmlJSMetaMethod method = methods.front(); const QQmlJSRegisterContent intType - = m_typeResolver->globalType(m_typeResolver->intType()); + = m_typeResolver->globalType(m_typeResolver->int32Type()); const QQmlJSRegisterContent stringType = m_typeResolver->globalType(m_typeResolver->stringType()); const QQmlJSRegisterContent returnType @@ -1370,17 +1376,25 @@ void QQmlJSTypePropagator::propagateStringArgCall(int argv) const QQmlJSScope::ConstPtr input = m_typeResolver->containedType( m_state.registers[argv].content); - for (QQmlJSScope::ConstPtr targetType : { - m_typeResolver->intType(), - m_typeResolver->uintType(), - m_typeResolver->realType(), - m_typeResolver->floatType(), - m_typeResolver->boolType(), - }) { - if (m_typeResolver->equals(input, targetType)) { - addReadRegister(argv, m_typeResolver->globalType(targetType)); - return; - } + + if (m_typeResolver->equals(input, m_typeResolver->uint32Type())) { + addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType())); + return; + } + + if (m_typeResolver->isIntegral(input)) { + addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->int32Type())); + return; + } + + if (m_typeResolver->isNumeric(input)) { + addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->realType())); + return; + } + + if (m_typeResolver->equals(input, m_typeResolver->boolType())) { + addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->boolType())); + return; } addReadRegister(argv, m_typeResolver->globalType(m_typeResolver->stringType())); @@ -1779,10 +1793,11 @@ void QQmlJSTypePropagator::recordEqualsType(int lhs) }; const auto isIntCompatible = [this](const QQmlJSRegisterContent &content) { - const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content); - return contained->scopeType() == QQmlJSScope::EnumScope - || m_typeResolver->equals(contained, m_typeResolver->intType()) - || m_typeResolver->equals(contained, m_typeResolver->uintType()); + auto contained = m_typeResolver->containedType(content); + if (contained->scopeType() == QQmlJSScope::EnumScope) + contained = contained->baseType(); + return m_typeResolver->isIntegral(contained) + && !m_typeResolver->equals(contained, m_typeResolver->uint32Type()); }; const auto accumulatorIn = m_state.accumulatorIn(); @@ -1797,7 +1812,7 @@ void QQmlJSTypePropagator::recordEqualsType(int lhs) return; } else if (isNumericOrEnum(accumulatorIn) && isNumericOrEnum(lhsRegister)) { const auto targetType = isIntCompatible(accumulatorIn) && isIntCompatible(lhsRegister) - ? m_typeResolver->globalType(m_typeResolver->intType()) + ? m_typeResolver->globalType(m_typeResolver->int32Type()) : m_typeResolver->globalType(m_typeResolver->realType()); addReadRegister(lhs, targetType); addReadAccumulator(targetType); @@ -1858,7 +1873,7 @@ void QQmlJSTypePropagator::generate_CmpEqInt(int lhsConst) recordEqualsIntType(); Q_UNUSED(lhsConst) setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation( - QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->intType()), + QSOperator::Op::Equal, m_typeResolver->globalType(m_typeResolver->int32Type()), m_state.accumulatorIn()))); } @@ -1867,7 +1882,7 @@ void QQmlJSTypePropagator::generate_CmpNeInt(int lhsConst) recordEqualsIntType(); Q_UNUSED(lhsConst) setAccumulator(QQmlJSRegisterContent(m_typeResolver->typeForBinaryOperation( - QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->intType()), + QSOperator::Op::NotEqual, m_typeResolver->globalType(m_typeResolver->int32Type()), m_state.accumulatorIn()))); } @@ -2040,7 +2055,7 @@ void QQmlJSTypePropagator::generateBinaryConstArithmeticOperation(QSOperator::Op { const QQmlJSRegisterContent type = m_typeResolver->typeForBinaryOperation( op, m_state.accumulatorIn(), - m_typeResolver->builtinType(m_typeResolver->intType())); + m_typeResolver->builtinType(m_typeResolver->int32Type())); checkConversion(m_state.accumulatorIn(), type); addReadAccumulator(type); diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index ece40f20ef..4c200b5677 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -28,8 +28,14 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer) m_nullType = builtinTypes.type(u"std::nullptr_t"_s).scope; m_realType = builtinTypes.type(u"double"_s).scope; m_floatType = builtinTypes.type(u"float"_s).scope; - m_intType = builtinTypes.type(u"int"_s).scope; - m_uintType = builtinTypes.type(u"uint"_s).scope; + m_int8Type = builtinTypes.type(u"qint8"_s).scope; + m_uint8Type = builtinTypes.type(u"quint8"_s).scope; + m_int16Type = builtinTypes.type(u"short"_s).scope; + m_uint16Type = builtinTypes.type(u"ushort"_s).scope; + m_int32Type = builtinTypes.type(u"int"_s).scope; + m_uint32Type = builtinTypes.type(u"uint"_s).scope; + m_int64Type = builtinTypes.type(u"qlonglong"_s).scope; + m_uint64Type = builtinTypes.type(u"qulonglong"_s).scope; m_boolType = builtinTypes.type(u"bool"_s).scope; m_stringType = builtinTypes.type(u"QString"_s).scope; m_stringListType = builtinTypes.type(u"QStringList"_s).scope; @@ -147,7 +153,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeForConst(QV4::ReturnedValue rv) co return voidType(); if (value.isInt32()) - return intType(); + return int32Type(); if (value.isBoolean()) return boolType(); @@ -187,9 +193,9 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi case QSOperator::Op::BitXor: case QSOperator::Op::LShift: case QSOperator::Op::RShift: - return builtinType(intType()); + return builtinType(int32Type()); case QSOperator::Op::URShift: - return builtinType(uintType()); + return builtinType(uint32Type()); case QSOperator::Op::Add: { const auto leftContents = containedType(left); const auto rightContents = containedType(right); @@ -198,7 +204,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi const QQmlJSScope::ConstPtr result = merge(leftContents, rightContents); if (equals(result, boolType())) - return builtinType(intType()); + return builtinType(int32Type()); if (isNumeric(result)) return builtinType(realType()); @@ -208,7 +214,7 @@ QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegi case QSOperator::Op::Mul: case QSOperator::Op::Exp: { const QQmlJSScope::ConstPtr result = merge(containedType(left), containedType(right)); - return builtinType(equals(result, boolType()) ? intType() : realType()); + return builtinType(equals(result, boolType()) ? int32Type() : realType()); } case QSOperator::Op::Div: case QSOperator::Op::Mod: @@ -229,14 +235,14 @@ QQmlJSRegisterContent QQmlJSTypeResolver::typeForArithmeticUnaryOperation( case UnaryOperator::Not: return builtinType(boolType()); case UnaryOperator::Complement: - return builtinType(intType()); + return builtinType(int32Type()); case UnaryOperator::Plus: if (isIntegral(operand)) return operand; Q_FALLTHROUGH(); default: if (equals(containedType(operand), boolType())) - return builtinType(intType()); + return builtinType(int32Type()); break; } @@ -255,13 +261,18 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const bool QQmlJSTypeResolver::isIntegral(const QQmlJSRegisterContent &type) const { - return equals(containedType(type), m_intType) || equals(containedType(type), m_uintType); + return isIntegral(containedType(type)); +} + +bool QQmlJSTypeResolver::isIntegral(const QQmlJSScope::ConstPtr &type) const +{ + // Only types of length <= 32bit count as integral + return isSignedInteger(type) || isUnsignedInteger(type); } bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const { - return equals(type, m_intType) || equals(type, m_uintType) - || equals(type, m_realType) || equals(type, m_floatType) + return isNumeric(type) || equals(type, m_boolType) || equals(type, m_voidType) || equals(type, m_nullType) || equals(type, m_stringType) || equals(type, m_jsPrimitiveType); } @@ -273,7 +284,23 @@ bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const if (mode == QQmlJSScope::ExtensionNamespace) return false; return equals(scope, m_numberPrototype); - }); + }); +} + +bool QQmlJSTypeResolver::isSignedInteger(const QQmlJSScope::ConstPtr &type) const +{ + // Only types of length <= 32bit count as integral + return equals(type, m_int8Type) + || equals(type, m_int16Type) + || equals(type, m_int32Type); +} + +bool QQmlJSTypeResolver::isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const +{ + // Only types of length <= 32bit count as integral + return equals(type, m_uint8Type) + || equals(type, m_uint16Type) + || equals(type, m_uint32Type); } QQmlJSScope::ConstPtr @@ -623,21 +650,31 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a, if (equals(b, jsValueType()) || equals(b, varType())) return b; - auto canConvert = [&](const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) { - return (equals(a, from) && equals(b, to)) || (equals(b, from) && equals(a, to)); + const auto isInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) { + return (isIntegral(type) && !equals(type, uint32Type())) || equals(type, boolType()); + }; + + if (isInt32Compatible(a) && isInt32Compatible(b)) + return int32Type(); + + const auto isUInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) { + return isUnsignedInteger(type) || equals(type, boolType()); }; + if (isUInt32Compatible(a) && isUInt32Compatible(b)) + return uint32Type(); + if (isNumeric(a) && isNumeric(b)) return realType(); - if (canConvert(boolType(), intType())) - return intType(); - if (canConvert(boolType(), uintType())) - return uintType(); - if (canConvert(intType(), stringType())) - return stringType(); - if (canConvert(uintType(), stringType())) + const auto isStringCompatible = [&](const QQmlJSScope::ConstPtr &type) { + // TODO: We can losslessly coerce more types to string. Should we? + return isIntegral(type) || equals(type, stringType()); + }; + + if (isStringCompatible(a) && isStringCompatible(b)) return stringType(); + if (isPrimitive(a) && isPrimitive(b)) return jsPrimitiveType(); @@ -765,6 +802,9 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType( if (type->isListProperty()) return m_listPropertyType; + if (type->scopeType() == QQmlJSScope::EnumScope) + return type->baseType(); + if (isPrimitive(type) || equals(type, m_jsValueType) || equals(type, m_urlType) || equals(type, m_dateTimeType) || equals(type, m_dateType) || equals(type, m_timeType) || equals(type, m_variantListType) || equals(type, m_variantMapType) @@ -773,12 +813,6 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType( return type; } - if (type->scopeType() == QQmlJSScope::EnumScope) - return m_intType; - - if (isNumeric(type)) - return m_realType; - if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) { if (const QQmlJSScope::ConstPtr valueType = type->valueType()) { switch (valueType->accessSemantics()) { @@ -944,7 +978,7 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS for (const auto &enumeration : enums) { if (enumeration.name() == name) { *result = QQmlJSRegisterContent::create( - storedType(intType()), enumeration, QString(), + storedType(enumeration.type()), enumeration, QString(), inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum : QQmlJSRegisterContent::ObjectEnum, scope); @@ -953,7 +987,7 @@ bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QS if (enumeration.hasKey(name)) { *result = QQmlJSRegisterContent::create( - storedType(intType()), enumeration, name, + storedType(enumeration.type()), enumeration, name, inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum : QQmlJSRegisterContent::ObjectEnum, scope); @@ -1109,9 +1143,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty( QQmlJSMetaProperty prop; prop.setPropertyName(u"length"_s); prop.setTypeName(u"int"_s); - prop.setType(intType()); + prop.setType(int32Type()); prop.setIsWritable(isWritable); - return QQmlJSRegisterContent::create(intType(), prop, QQmlJSRegisterContent::Builtin, scope); + return QQmlJSRegisterContent::create(int32Type(), prop, QQmlJSRegisterContent::Builtin, scope); } QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSScope::ConstPtr &type, @@ -1252,7 +1286,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent const auto enumeration = type.enumeration(); if (!type.enumMember().isEmpty() || !enumeration.hasKey(name)) return {}; - return QQmlJSRegisterContent::create(storedType(intType()), enumeration, name, + return QQmlJSRegisterContent::create(storedType(enumeration.type()), enumeration, name, QQmlJSRegisterContent::ObjectEnum, type.scopeType()); } if (type.isMethod()) { diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index 9d730630e0..4776caf226 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -45,8 +45,14 @@ public: QQmlJSScope::ConstPtr nullType() const { return m_nullType; } QQmlJSScope::ConstPtr realType() const { return m_realType; } QQmlJSScope::ConstPtr floatType() const { return m_floatType; } - QQmlJSScope::ConstPtr intType() const { return m_intType; } - QQmlJSScope::ConstPtr uintType() const { return m_uintType; } + QQmlJSScope::ConstPtr int8Type() const { return m_int8Type; } + QQmlJSScope::ConstPtr uint8Type() const { return m_uint8Type; } + QQmlJSScope::ConstPtr int16Type() const { return m_int16Type; } + QQmlJSScope::ConstPtr uint16Type() const { return m_uint16Type; } + QQmlJSScope::ConstPtr int32Type() const { return m_int32Type; } + QQmlJSScope::ConstPtr uint32Type() const { return m_uint32Type; } + QQmlJSScope::ConstPtr int64Type() const { return m_int64Type; } + QQmlJSScope::ConstPtr uint64Type() const { return m_uint64Type; } QQmlJSScope::ConstPtr boolType() const { return m_boolType; } QQmlJSScope::ConstPtr stringType() const { return m_stringType; } QQmlJSScope::ConstPtr stringListType() const { return m_stringListType; } @@ -166,6 +172,9 @@ public: bool canHoldUndefined(const QQmlJSRegisterContent &content) const; bool isNumeric(const QQmlJSScope::ConstPtr &type) const; + bool isIntegral(const QQmlJSScope::ConstPtr &type) const; + bool isSignedInteger(const QQmlJSScope::ConstPtr &type) const; + bool isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const; bool canHold(const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const; @@ -202,8 +211,14 @@ protected: QQmlJSScope::ConstPtr m_arrayType; QQmlJSScope::ConstPtr m_realType; QQmlJSScope::ConstPtr m_floatType; - QQmlJSScope::ConstPtr m_intType; - QQmlJSScope::ConstPtr m_uintType; + QQmlJSScope::ConstPtr m_int8Type; + QQmlJSScope::ConstPtr m_uint8Type; + QQmlJSScope::ConstPtr m_int16Type; + QQmlJSScope::ConstPtr m_uint16Type; + QQmlJSScope::ConstPtr m_int32Type; + QQmlJSScope::ConstPtr m_uint32Type; + QQmlJSScope::ConstPtr m_int64Type; + QQmlJSScope::ConstPtr m_uint64Type; QQmlJSScope::ConstPtr m_boolType; QQmlJSScope::ConstPtr m_stringType; QQmlJSScope::ConstPtr m_stringListType; diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp index df9ba2b6c6..51a4d3de8e 100644 --- a/src/qmltyperegistrar/qqmltypescreator.cpp +++ b/src/qmltyperegistrar/qqmltypescreator.cpp @@ -187,6 +187,10 @@ void QmlTypesCreator::writeType(const QJsonObject &property, const QString &key) #else type = QLatin1String("double"); #endif + } else if (type == QLatin1String("qint16")) { + type = QLatin1String("short"); + } else if (type == QLatin1String("quint16")) { + type = QLatin1String("ushort"); } else if (type == QLatin1String("qint32")) { type = QLatin1String("int"); } else if (type == QLatin1String("quint32")) { diff --git a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml index f12d3f5ea2..ec848429e8 100644 --- a/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml +++ b/tests/auto/qml/qmlcppcodegen/data/numbersInJsPrimitive.qml @@ -4,23 +4,57 @@ import TestTypes QtObject { function writeValues() { + Druggeljug.myInt8 = 35 + Druggeljug.myUint8 = 36 + Druggeljug.myInt16 = 37 + Druggeljug.myUint16 = 38 Druggeljug.myInt = 39 Druggeljug.myUint = 40 Druggeljug.myInt32 = 41 Druggeljug.myUint32 = 42 } + function negateValues() { + Druggeljug.myInt8 = -Druggeljug.myInt8; + Druggeljug.myUint8 = -Druggeljug.myUint8; + Druggeljug.myInt16 = -Druggeljug.myInt16; + Druggeljug.myUint16 = -Druggeljug.myUint16; + Druggeljug.myInt = -Druggeljug.myInt; + Druggeljug.myUint = -Druggeljug.myUint; + Druggeljug.myInt32 = -Druggeljug.myInt32; + Druggeljug.myUint32 = -Druggeljug.myUint32; + } + + function shuffleValues() { + Druggeljug.myInt8 = Druggeljug.myUint8; + Druggeljug.myUint8 = Druggeljug.myInt16; + Druggeljug.myInt16 = Druggeljug.myUint16; + Druggeljug.myUint16 = Druggeljug.myInt; + Druggeljug.myInt = Druggeljug.myUint; + Druggeljug.myUint = Druggeljug.myInt32; + Druggeljug.myInt32 = Druggeljug.myUint32; + Druggeljug.myUint32 = Druggeljug.myInt8; + } + function readValueAsString(i: int) : string { switch (i) { - case 0: return Druggeljug.myInt; - case 1: return Druggeljug.myUint; - case 2: return Druggeljug.myInt32; - case 3: return Druggeljug.myUint32; + case 0: return Druggeljug.myInt8; + case 1: return Druggeljug.myUint8; + case 2: return Druggeljug.myInt16; + case 3: return Druggeljug.myUint16; + case 4: return Druggeljug.myInt; + case 5: return Druggeljug.myUint; + case 6: return Druggeljug.myInt32; + case 7: return Druggeljug.myUint32; default: return ""; } } function storeValues() { + Druggeljug.storeMyInt8(1330) + Druggeljug.storeMyUint8(1331) + Druggeljug.storeMyInt16(1332) + Druggeljug.storeMyUint16(1333) Druggeljug.storeMyInt(1334) Druggeljug.storeMyUint(1335) Druggeljug.storeMyInt32(1336) diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index 821e676786..92b8efc203 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -3043,10 +3043,10 @@ void tst_QmlCppCodegen::lengthAccessArraySequenceCompat() QCOMPARE(o->property("length").toInt(), 100); } -static QList<QString> convertToStrings(const QList<int> &ints) +static QList<QString> convertToStrings(const QList<qint64> &ints) { QList<QString> strings; - for (int i : ints) + for (qint64 i : ints) strings.append(QString::number(i)); return strings; } @@ -3059,12 +3059,41 @@ void tst_QmlCppCodegen::numbersInJsPrimitive() QScopedPointer<QObject> o(c.create()); QVERIFY(!o.isNull()); - const QList<int> zeroes = {0, 0, 0, 0}; - const QList<int> written = {39, 40, 41, 42}; - const QList<int> stored = {1334, 1335, 1336, 1337}; - QStringList asStrings(4); + const QList<qint64> zeroes + = {0, 0, 0, 0, 0, 0, 0, 0}; + const QList<qint64> written + = {35, 36, 37, 38, 39, 40, 41, 42}; + const QList<qint64> writtenNegative + = {-35, 220, -37, 65498, -39, 4294967256, -41, 4294967254}; + const QList<QList<qint64>> writtenShuffled = { + { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 }, + { -37, 218, -39, 65496, -41, 4294967254, -36, 4294967259 }, + { -38, 217, -40, 65495, -42, 4294967260, -37, 4294967258 }, + { -39, 216, -41, 65494, -36, 4294967259, -38, 4294967257 }, + { -40, 215, -42, 65500, -37, 4294967258, -39, 4294967256 }, + { -41, 214, -36, 65499, -38, 4294967257, -40, 4294967255 }, + { -42, 220, -37, 65498, -39, 4294967256, -41, 4294967254 }, + { -36, 219, -38, 65497, -40, 4294967255, -42, 4294967260 }, + }; + + const QList<qint64> stored + = {50, 51, 1332, 1333, 1334, 1335, 1336, 1337}; + const QList<qint64> storedNegative + = {-50, 205, -1332, 64203, -1334, 4294965961, -1336, 4294965959}; + const QList<QList<qint64>> storedShuffled = { + { -51, 204, -1333, 64202, -1335, 4294965960, -1337, 4294967245 }, + { -52, 203, -1334, 64201, -1336, 4294965959, -51, 4294967244 }, + { -53, 202, -1335, 64200, -1337, 4294967245, -52, 4294967243 }, + { -54, 201, -1336, 64199, -51, 4294967244, -53, 4294967242 }, + { -55, 200, -1337, 65485, -52, 4294967243, -54, 4294967241 }, + { -56, 199, -51, 65484, -53, 4294967242, -55, 4294967240 }, + { -57, 205, -52, 65483, -54, 4294967241, -56, 4294967239 }, + { -51, 204, -53, 65482, -55, 4294967240, -57, 4294967245 }, + }; - for (int i = 0; i < 4; ++i) { + QStringList asStrings(8); + + for (int i = 0; i < 8; ++i) { QMetaObject::invokeMethod( o.data(), "readValueAsString", Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); @@ -3072,20 +3101,56 @@ void tst_QmlCppCodegen::numbersInJsPrimitive() QCOMPARE(asStrings, convertToStrings(zeroes)); QMetaObject::invokeMethod(o.data(), "writeValues"); - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < 8; ++i) { QMetaObject::invokeMethod( o.data(), "readValueAsString", Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); } QCOMPARE(asStrings, convertToStrings(written)); + QMetaObject::invokeMethod(o.data(), "negateValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(writtenNegative)); + + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod(o.data(), "shuffleValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(writtenShuffled[i])); + } + QMetaObject::invokeMethod(o.data(), "storeValues"); - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < 8; ++i) { QMetaObject::invokeMethod( o.data(), "readValueAsString", Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); } QCOMPARE(asStrings, convertToStrings(stored)); + + QMetaObject::invokeMethod(o.data(), "negateValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(storedNegative)); + + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod(o.data(), "shuffleValues"); + for (int i = 0; i < 8; ++i) { + QMetaObject::invokeMethod( + o.data(), "readValueAsString", + Q_RETURN_ARG(QString, asStrings[i]), Q_ARG(int, i)); + } + QCOMPARE(asStrings, convertToStrings(storedShuffled[i])); + } } void tst_QmlCppCodegen::infinitiesToInt() |