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 <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Ulf Hermann 2023-03-27 18:00:46 +02:00
parent 407ed344da
commit 03ff348b49
9 changed files with 320 additions and 129 deletions

View File

@ -167,6 +167,30 @@ Module {
valueType: "QObject"
}
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"
@ -182,13 +206,13 @@ Module {
}
Component {
name: "qlonglong"
accessSemantics: "value"
name: "qlonglong"
accessSemantics: "value"
}
Component {
name: "qulonglong"
accessSemantics: "value"
name: "qulonglong"
accessSemantics: "value"
}
Component {

View File

@ -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()) {

View File

@ -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()) &&

View File

@ -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);

View File

@ -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()) {

View File

@ -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;

View File

@ -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")) {

View File

@ -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)

View File

@ -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 },
};
for (int i = 0; i < 4; ++i) {
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 },
};
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()