diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2024-04-16 11:40:10 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-04-26 07:13:50 +0200 |
commit | 23fc22e16022e355f2a1aff8705c09b807fbe024 (patch) | |
tree | 1e30c3c7d5897fa1e48071a8d0238083baff82a9 | |
parent | 372db480cda5899148629d8edad76405bc1b98de (diff) |
QmlCompiler: Consider lengths to be qsizetype
This is what we get as "source material" from all the containers. In
JavaScript, arrays sizes are up to 32bit unsigned, but we need to
represent -1 in a few places. Therefore, we cannot clamp the result to
32bit signed if the underlying container supports 64bit indices.
We can change qjslist.h here because this header is only supposed to be
used from generated code. Therefore, if you rebuild any users of this
code, you will also re-generate them, adapting to the new API.
Since we check for QJSNumberCoercion::isArrayIndex() before we use any
number as an array index when generating code, this is actually safe. We
just need to adapt isArrayIndex() to consider numbers greater than
INT_MAX as possible values.
Fixes: QTBUG-122582
Change-Id: I1147978a05bfedb63037c7f4437921f85a6aabb1
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/qml/common/qjsnumbercoercion.cpp | 18 | ||||
-rw-r--r-- | src/qml/common/qjsnumbercoercion.h | 17 | ||||
-rw-r--r-- | src/qml/jsapi/qjslist.h | 80 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 40 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstypepropagator.cpp | 2 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 18 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 1 |
7 files changed, 101 insertions, 75 deletions
diff --git a/src/qml/common/qjsnumbercoercion.cpp b/src/qml/common/qjsnumbercoercion.cpp index ba76c12bb0..8cd96a4e25 100644 --- a/src/qml/common/qjsnumbercoercion.cpp +++ b/src/qml/common/qjsnumbercoercion.cpp @@ -24,10 +24,26 @@ QT_BEGIN_NAMESPACE \internal Checks whether \a d contains a value that can serve as an index into an array. - For that, \a d must be a non-negative value representable as an int. + For that, \a d must be a non-negative value representable as an unsigned 32bit int. */ /*! + \fn bool QJSNumberCoercion::isArrayIndex(qint64 i) + \internal + + Checks whether \a i contains a value that can serve as an index into an array. + For that, \a d must be a non-negative value representable as an unsigned 32bit int. +*/ + +/*! + \fn bool QJSNumberCoercion::isArrayIndex(quint64 i) + \internal + + Checks whether \a i contains a value that can serve as an index into an array. + For that, \a d must be a value representable as an unsigned 32bit int. +*/ + +/*! \fn int QJSNumberCoercion::toInteger(double d) \internal diff --git a/src/qml/common/qjsnumbercoercion.h b/src/qml/common/qjsnumbercoercion.h index 569976454f..0023bff6e8 100644 --- a/src/qml/common/qjsnumbercoercion.h +++ b/src/qml/common/qjsnumbercoercion.h @@ -27,11 +27,20 @@ public: static constexpr bool isArrayIndex(double d) { - if (d < 0 || !equals(d, d) || d > (std::numeric_limits<int>::max)()) { - return false; - } + return d >= 0 + && equals(d, d) + && d <= (std::numeric_limits<uint>::max)() + && equals(static_cast<uint>(d), d); + } - return equals(static_cast<int>(d), d); + static constexpr bool isArrayIndex(qint64 i) + { + return i >= 0 && i <= (std::numeric_limits<uint>::max)(); + } + + static constexpr bool isArrayIndex(quint64 i) + { + return i <= (std::numeric_limits<uint>::max)(); } static constexpr int toInteger(double d) { diff --git a/src/qml/jsapi/qjslist.h b/src/qml/jsapi/qjslist.h index f55d669a41..d604e266f2 100644 --- a/src/qml/jsapi/qjslist.h +++ b/src/qml/jsapi/qjslist.h @@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE struct QJSListIndexClamp { - static qsizetype clamp(int start, qsizetype max, qsizetype min = 0) + static qsizetype clamp(qsizetype start, qsizetype max, qsizetype min = 0) { Q_ASSERT(min >= 0); Q_ASSERT(min <= max); @@ -43,18 +43,15 @@ struct QJSList : private QJSListIndexClamp QJSList(List *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} - Value at(int index) const + Value at(qsizetype 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()))); - } + qsizetype size() const { return m_list->size(); } - void resize(int size) + void resize(qsizetype size) { m_list->resize(size); } @@ -64,7 +61,7 @@ struct QJSList : private QJSListIndexClamp return std::find(m_list->cbegin(), m_list->cend(), value) != m_list->cend(); } - bool includes(const Value &value, int start) const + bool includes(const Value &value, qsizetype start) const { return std::find(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(), value) != m_list->cend(); @@ -88,14 +85,14 @@ struct QJSList : private QJSListIndexClamp { return *m_list; } - List slice(int start) const + List slice(qsizetype start) const { List result; std::copy(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(), std::back_inserter(result)); return result; } - List slice(int start, int end) const + List slice(qsizetype start, qsizetype end) const { const qsizetype size = m_list->size(); const qsizetype clampedStart = clamp(start, size); @@ -107,7 +104,7 @@ struct QJSList : private QJSListIndexClamp return result; } - int indexOf(const Value &value) const + qsizetype indexOf(const Value &value) const { const auto begin = m_list->cbegin(); const auto end = m_list->cend(); @@ -116,9 +113,9 @@ struct QJSList : private QJSListIndexClamp return -1; const qsizetype result = it - begin; Q_ASSERT(result >= 0); - return result > std::numeric_limits<int>::max() ? -1 : int(result); + return result; } - int indexOf(const Value &value, int start) const + qsizetype indexOf(const Value &value, qsizetype start) const { const auto begin = m_list->cbegin(); const auto end = m_list->cend(); @@ -127,18 +124,17 @@ struct QJSList : private QJSListIndexClamp return -1; const qsizetype result = it - begin; Q_ASSERT(result >= 0); - return result > std::numeric_limits<int>::max() ? -1 : int(result); + return result; } - int lastIndexOf(const Value &value) const + qsizetype lastIndexOf(const Value &value) const { const auto begin = std::make_reverse_iterator(m_list->cend()); const auto end = std::make_reverse_iterator(m_list->cbegin()); const auto it = std::find(begin, end, value); - const qsizetype result = (end - it) - 1; - return result > std::numeric_limits<int>::max() ? -1 : int(result); + return (end - it) - 1; } - int lastIndexOf(const Value &value, int start) const + qsizetype lastIndexOf(const Value &value, qsizetype start) const { const qsizetype size = m_list->size(); if (size == 0) @@ -150,8 +146,7 @@ struct QJSList : private QJSListIndexClamp const auto end = std::make_reverse_iterator(m_list->cbegin()); const auto it = std::find(begin, end, value); - const qsizetype result = (end - it) - 1; - return result > std::numeric_limits<int>::max() ? -1 : int(result); + return (end - it) - 1; } QString toString() const { return join(); } @@ -168,18 +163,18 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam QJSList(QQmlListProperty<QObject> *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} - QObject *at(int index) const + QObject *at(qsizetype index) const { Q_ASSERT(index >= 0 && index < size()); return m_list->at(m_list, index); } - int size() const + qsizetype size() const { - return int(std::min(m_list->count(m_list), qsizetype(std::numeric_limits<int>::max()))); + return m_list->count(m_list); } - void resize(int size) + void resize(qsizetype size) { qsizetype current = m_list->count(m_list); if (current < size && m_list->append) { @@ -206,7 +201,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam return false; } - bool includes(const QObject *value, int start) const + bool includes(const QObject *value, qsizetype start) const { if (!m_list->count || !m_list->at) return false; @@ -239,7 +234,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam { return m_list->toList<QObjectList>(); } - QObjectList slice(int start) const + QObjectList slice(qsizetype start) const { if (!m_list->count || !m_list->at) return QObjectList(); @@ -252,7 +247,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam result.append(m_list->at(m_list, i)); return result; } - QObjectList slice(int start, int end) const + QObjectList slice(qsizetype start, qsizetype end) const { if (!m_list->count || !m_list->at) return QObjectList(); @@ -267,46 +262,43 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam return result; } - int indexOf(const QObject *value) const + qsizetype indexOf(const QObject *value) const { if (!m_list->count || !m_list->at) return -1; - const qsizetype end - = std::min(m_list->count(m_list), qsizetype(std::numeric_limits<int>::max())); + const qsizetype end = m_list->count(m_list); for (qsizetype i = 0; i < end; ++i) { if (m_list->at(m_list, i) == value) - return int(i); + return i; } return -1; } - int indexOf(const QObject *value, int start) const + qsizetype indexOf(const QObject *value, qsizetype start) const { if (!m_list->count || !m_list->at) return -1; const qsizetype size = m_list->count(m_list); - for (qsizetype i = clamp(start, size), - end = std::min(size, qsizetype(std::numeric_limits<int>::max())); - i < end; ++i) { + for (qsizetype i = clamp(start, size); i < size; ++i) { if (m_list->at(m_list, i) == value) - return int(i); + return i; } return -1; } - int lastIndexOf(const QObject *value) const + qsizetype lastIndexOf(const QObject *value) const { if (!m_list->count || !m_list->at) return -1; for (qsizetype i = m_list->count(m_list) - 1; i >= 0; --i) { if (m_list->at(m_list, i) == value) - return i > std::numeric_limits<int>::max() ? -1 : int(i); + return i; } return -1; } - int lastIndexOf(const QObject *value, int start) const + qsizetype lastIndexOf(const QObject *value, qsizetype start) const { if (!m_list->count || !m_list->at) return -1; @@ -318,7 +310,7 @@ struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClam qsizetype clampedStart = std::min(clamp(start, size), size - 1); for (qsizetype i = clampedStart; i >= 0; --i) { if (m_list->at(m_list, i) == value) - return i > std::numeric_limits<int>::max() ? -1 : int(i); + return i; } return -1; } @@ -342,11 +334,11 @@ public: } bool hasNext() const { return m_index < m_size; } - int next() { return m_index++; } + qsizetype next() { return m_index++; } private: - int m_index; - int m_size; + qsizetype m_index; + qsizetype m_size; }; // QJSListForInIterator must not require initialization so that we can jump over it with goto. @@ -365,7 +357,7 @@ public: Value next(const QJSList<List, Value> &list) { return list.at(m_index++); } private: - int m_index; + qsizetype m_index; }; // QJSListForOfIterator must not require initialization so that we can jump over it with goto. diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index ce2334b935..7b0664f918 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -723,13 +723,11 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base) AccumulatorConverter registers(this); const QString baseName = registerVariable(base); - if (!m_typeResolver->isIntegral(indexType)) { + if (!m_typeResolver->isNativeArrayIndex(indexType)) { m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s + voidAssignment + u"else "_s; - } - - if (!m_typeResolver->isUnsignedInteger(indexType)) { + } else if (!m_typeResolver->isUnsignedInteger(indexType)) { m_body += u"if ("_s + indexName + u" < 0)\n"_s + voidAssignment + u"else "_s; @@ -774,9 +772,9 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index) INJECT_TRACE_INFO(generate_StoreElement); const QQmlJSRegisterContent baseType = registerType(base); - const QQmlJSRegisterContent indexType = registerType(index); + const QQmlJSScope::ConstPtr indexType = m_typeResolver->containedType(registerType(index)); - if (!m_typeResolver->isNumeric(registerType(index)) || !baseType.isList()) { + if (!m_typeResolver->isNumeric(indexType) || !baseType.isList()) { reject(u"StoreElement with non-list base type or non-numeric arguments"_s); return; } @@ -794,26 +792,27 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index) m_typeResolver->containedType(valueType))); addInclude(u"QtQml/qjslist.h"_s); - m_body += u"if ("_s; - if (!m_typeResolver->isIntegral(indexType)) - m_body += u"QJSNumberCoercion::isArrayIndex("_s + indexName + u") && "_s; - if (!m_typeResolver->isUnsignedInteger(m_typeResolver->containedType(indexType))) - m_body += indexName + u" >= 0"_s; + if (!m_typeResolver->isNativeArrayIndex(indexType)) + m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s; + else if (!m_typeResolver->isUnsignedInteger(indexType)) + m_body += u"if ("_s + indexName + u" >= 0) {\n"_s; + else + m_body += u"{\n"_s; if (m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) { - m_body += u" && "_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName + m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName + u"))\n"_s; - m_body += u" "_s + baseName + u".replace(&"_s + baseName + m_body += u" "_s + baseName + u".replace(&"_s + baseName + u", "_s + indexName + u", "_s; m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn) + u");\n"_s; + m_body += u"}\n"_s; return; } if (m_state.isRegisterAffectedBySideEffects(base)) reject(u"LoadElement on a sequence potentially affected by side effects"_s); - m_body += u") {\n"_s; m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s; m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s + indexName + u" + 1);\n"_s; @@ -1381,7 +1380,7 @@ void QQmlJSCodeGenerator::generate_GetLookupHelper(int index) if (stored->isListProperty()) { m_body += m_state.accumulatorVariableOut + u" = "_s; m_body += conversion( - m_typeResolver->globalType(m_typeResolver->int32Type()), + m_typeResolver->globalType(m_typeResolver->sizeType()), m_state.accumulatorOut(), m_state.accumulatorVariableIn + u".count("_s + u'&' + m_state.accumulatorVariableIn + u')'); @@ -1389,7 +1388,7 @@ void QQmlJSCodeGenerator::generate_GetLookupHelper(int index) } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence || m_typeResolver->equals(stored, m_typeResolver->stringType())) { m_body += m_state.accumulatorVariableOut + u" = "_s - + conversion(m_typeResolver->globalType(m_typeResolver->int32Type()), + + conversion(m_typeResolver->globalType(m_typeResolver->sizeType()), m_state.accumulatorOut(), m_state.accumulatorVariableIn + u".length()"_s) + u";\n"_s; @@ -2321,14 +2320,13 @@ void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv) + u"QLatin1String(\"Invalid array length\"));\n"_s; const QString indexName = registerVariable(argv); - const auto indexType = registerType(argv); - if (!m_typeResolver->isIntegral(indexType)) { + const auto indexType = m_typeResolver->containedType(registerType(argv)); + if (!m_typeResolver->isNativeArrayIndex(indexType)) { m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s + error; generateReturnError(); m_body += u"}\n"_s; - } else if (!m_typeResolver->isUnsignedInteger( - m_typeResolver->containedType(indexType))) { + } else if (!m_typeResolver->isUnsignedInteger(indexType)) { m_body += u"if ("_s + indexName + u" < 0) {\n"_s + error; generateReturnError(); @@ -2340,7 +2338,7 @@ void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv) m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut + u", aotContext->engine).resize("_s + convertStored( - registerType(argv).storedType(), m_typeResolver->int32Type(), + registerType(argv).storedType(), m_typeResolver->sizeType(), consumedRegisterVariable(argv)) + u");\n"_s; } else if (!m_error->isValid()) { diff --git a/src/qmlcompiler/qqmljstypepropagator.cpp b/src/qmlcompiler/qqmljstypepropagator.cpp index 0ff2e18684..baa6eab694 100644 --- a/src/qmlcompiler/qqmljstypepropagator.cpp +++ b/src/qmlcompiler/qqmljstypepropagator.cpp @@ -742,7 +742,7 @@ void QQmlJSTypePropagator::generate_LoadElement(int base) 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())); + addReadAccumulator(m_typeResolver->globalType(m_typeResolver->sizeType())); else if (m_typeResolver->isUnsignedInteger(contained)) addReadAccumulator(m_typeResolver->globalType(m_typeResolver->uint32Type())); else diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index f136120f75..93f9c7f7f4 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -376,6 +376,16 @@ bool QQmlJSTypeResolver::isUnsignedInteger(const QQmlJSScope::ConstPtr &type) co || equals(type, m_uint64Type); } +bool QQmlJSTypeResolver::isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const +{ + return (equals(type, m_uint8Type) + || equals(type, m_int8Type) + || equals(type, m_uint16Type) + || equals(type, m_int16Type) + || equals(type, m_uint32Type) + || equals(type, m_int32Type)); +} + QQmlJSScope::ConstPtr QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const { @@ -1395,11 +1405,11 @@ QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty( { QQmlJSMetaProperty prop; prop.setPropertyName(u"length"_s); - prop.setTypeName(u"int"_s); - prop.setType(int32Type()); + prop.setTypeName(u"qsizetype"_s); + prop.setType(sizeType()); prop.setIsWritable(isWritable); return QQmlJSRegisterContent::create( - int32Type(), prop, QQmlJSRegisterContent::InvalidLookupIndex, + sizeType(), prop, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSRegisterContent::Builtin, scope); } @@ -1612,7 +1622,7 @@ QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent return scope->valueType(); if (equals(scope, m_forInIteratorPtr)) - return m_int32Type; + return m_sizeType; if (equals(scope, m_forOfIteratorPtr)) return list.scopeType()->valueType(); diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index e72e7e78f0..6a5ea8cf9b 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -201,6 +201,7 @@ public: bool isIntegral(const QQmlJSScope::ConstPtr &type) const; bool isSignedInteger(const QQmlJSScope::ConstPtr &type) const; bool isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const; + bool isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const; bool canHold(const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const; |