aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h25
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp192
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h19
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp26
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp14
-rw-r--r--tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml198
-rw-r--r--tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml47
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp163
11 files changed, 566 insertions, 134 deletions
diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h
index f6b95e2413..b07e27b24f 100644
--- a/src/qml/jsruntime/qv4arrayobject_p.h
+++ b/src/qml/jsruntime/qv4arrayobject_p.h
@@ -20,6 +20,31 @@
QT_BEGIN_NAMESPACE
+inline bool qIsAtMostUintLimit(qsizetype length, uint limit = std::numeric_limits<uint>::max())
+{
+ // Use the type with the larger positive range to do the comparison.
+
+ Q_ASSERT(length >= 0);
+ if constexpr (sizeof(qsizetype) > sizeof(uint)) {
+ return length <= qsizetype(limit);
+ } else {
+ return uint(length) <= limit;
+ }
+}
+
+inline bool qIsAtMostSizetypeLimit(uint length, qsizetype limit = std::numeric_limits<qsizetype>::max())
+{
+ // Use the type with the larger positive range to do the comparison.
+
+ Q_ASSERT(limit >= 0);
+ if constexpr (sizeof(qsizetype) > sizeof(uint)) {
+ return qsizetype(length) <= limit;
+ } else {
+ return length <= uint(limit);
+ }
+}
+
+
namespace QV4 {
namespace Heap {
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 3fa107ebda..021872b7ad 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -597,7 +597,7 @@ void QObjectWrapper::setProperty(
PROPERTY_STORE(QQmlScriptString, ss);
} else {
QVariant v;
- if (property->isQList())
+ if (property->isQList() && propType.flags().testFlag(QMetaType::IsQmlList))
v = scope.engine->toVariant(value, QMetaType::fromType<QList<QObject *> >());
else
v = scope.engine->toVariant(value, propType);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 7f4b6057ce..4dc15009c9 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -77,17 +77,24 @@ struct SequenceOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
s->loadReference();
}
- if (arrayIndex < quint32(s->size())) {
- uint index = arrayIndex;
+ const qsizetype size = s->size();
+ if (size > 0 && qIsAtMostSizetypeLimit(arrayIndex, size - 1)) {
+ const uint index = arrayIndex;
++arrayIndex;
if (attrs)
*attrs = QV4::Attr_Data;
if (pd)
- pd->value = s->engine()->fromVariant(s->at(index));
+ pd->value = s->engine()->fromVariant(s->at(qsizetype(index)));
return PropertyKey::fromArrayIndex(index);
}
- return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+ if (memberIndex == 0) {
+ ++memberIndex;
+ return o->engine()->id_length()->propertyKey();
+ }
+
+ // You cannot add any own properties via the regular JavaScript interfaces.
+ return PropertyKey::invalid();
}
};
@@ -182,7 +189,7 @@ qsizetype Sequence::size() const
return metaSequence(p)->size(p->container);
}
-QVariant Sequence::at(int index) const
+QVariant Sequence::at(qsizetype index) const
{
const auto *p = d();
const QMetaType v = valueMetaType(p);
@@ -196,41 +203,50 @@ QVariant Sequence::at(int index) const
return result;
}
-void Sequence::append(const QVariant &item)
+
+template<typename Action>
+void convertAndDo(const QVariant &item, const QMetaType v, Action action)
{
- const auto *p = d();
- const auto *m = metaSequence(p);
- const QMetaType v = valueMetaType(p);
if (item.metaType() == v) {
- m->addValueAtEnd(p->container, item.constData());
+ action(item.constData());
} else if (v == QMetaType::fromType<QVariant>()) {
- m->addValueAtEnd(p->container, &item);
+ action(&item);
} else {
QVariant converted = item;
if (!converted.convert(v))
converted = QVariant(v);
- m->addValueAtEnd(p->container, converted.constData());
+ action(converted.constData());
}
}
-void Sequence::replace(int index, const QVariant &item)
+void Sequence::append(const QVariant &item)
{
- const auto *p = d();
- const auto *m = metaSequence(p);
- const QMetaType v = valueMetaType(p);
- if (item.metaType() == v) {
- m->setValueAtIndex(p->container, index, item.constData());
- } else if (v == QMetaType::fromType<QVariant>()) {
- m->setValueAtIndex(p->container, index, &item);
- } else {
- QVariant converted = item;
- if (!converted.convert(v))
- converted = QVariant(v);
- m->setValueAtIndex(p->container, index, converted.constData());
- }
+ const Heap::Sequence *p = d();
+ convertAndDo(item, valueMetaType(p), [p](const void *data) {
+ metaSequence(p)->addValueAtEnd(p->container, data);
+ });
+}
+
+void Sequence::append(qsizetype num, const QVariant &item)
+{
+ const Heap::Sequence *p = d();
+ convertAndDo(item, valueMetaType(p), [p, num](const void *data) {
+ const QMetaSequence *m = metaSequence(p);
+ void *container = p->container;
+ for (qsizetype i = 0; i < num; ++i)
+ m->addValueAtEnd(container, data);
+ });
}
-void Sequence::removeLast(int num)
+void Sequence::replace(qsizetype index, const QVariant &item)
+{
+ const Heap::Sequence *p = d();
+ convertAndDo(item, valueMetaType(p), [p, index](const void *data) {
+ metaSequence(p)->setValueAtIndex(p->container, index, data);
+ });
+}
+
+void Sequence::removeLast(qsizetype num)
{
const auto *p = d();
const auto *m = metaSequence(p);
@@ -254,15 +270,8 @@ QVariant Sequence::toVariant() const
return QVariant(p->typePrivate->listId, p->container);
}
-ReturnedValue Sequence::containerGetIndexed(uint index, bool *hasProperty) const
+ReturnedValue Sequence::containerGetIndexed(qsizetype index, bool *hasProperty) const
{
- /* Qt containers have int (rather than uint) allowable indexes. */
- if (index > INT_MAX) {
- generateWarning(engine(), QLatin1String("Index out of range during indexed get"));
- if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
- }
if (d()->isReference) {
if (!d()->object) {
if (hasProperty)
@@ -271,7 +280,7 @@ ReturnedValue Sequence::containerGetIndexed(uint index, bool *hasProperty) const
}
loadReference();
}
- if (index < quint32(size())) {
+ if (index >= 0 && index < size()) {
if (hasProperty)
*hasProperty = true;
return engine()->fromVariant(at(index));
@@ -281,17 +290,11 @@ ReturnedValue Sequence::containerGetIndexed(uint index, bool *hasProperty) const
return Encode::undefined();
}
-bool Sequence::containerPutIndexed(uint index, const Value &value)
+bool Sequence::containerPutIndexed(qsizetype index, const Value &value)
{
if (internalClass()->engine->hasException)
return false;
- /* Qt containers have int (rather than uint) allowable indexes. */
- if (index > INT_MAX) {
- generateWarning(engine(), QLatin1String("Index out of range during indexed set"));
- return false;
- }
-
if (d()->isReadOnly) {
engine()->throwTypeError(QLatin1String("Cannot insert into a readonly container"));
return false;
@@ -303,10 +306,13 @@ bool Sequence::containerPutIndexed(uint index, const Value &value)
loadReference();
}
- quint32 count = quint32(size());
+ const qsizetype count = size();
const QMetaType valueType = valueMetaType(d());
const QVariant element = engine()->toVariant(value, valueType, false);
+ if (index < 0)
+ return false;
+
if (index == count) {
append(element);
} else if (index < count) {
@@ -314,11 +320,8 @@ bool Sequence::containerPutIndexed(uint index, const Value &value)
} else {
/* according to ECMA262r3 we need to insert */
/* the value at the given index, increasing length to index+1. */
- while (index > count++) {
- append(valueType == QMetaType::fromType<QVariant>()
- ? QVariant()
- : QVariant(valueType));
- }
+ append(index - count,
+ valueType == QMetaType::fromType<QVariant>() ? QVariant() : QVariant(valueType));
append(element);
}
@@ -327,32 +330,14 @@ bool Sequence::containerPutIndexed(uint index, const Value &value)
return true;
}
-PropertyAttributes Sequence::containerQueryIndexed(uint index) const
-{
- /* Qt containers have int (rather than uint) allowable indexes. */
- if (index > INT_MAX) {
- generateWarning(engine(), QLatin1String("Index out of range during indexed query"));
- return QV4::Attr_Invalid;
- }
- if (d()->isReference) {
- if (!d()->object)
- return QV4::Attr_Invalid;
- loadReference();
- }
- return (index < quint32(size())) ? QV4::Attr_Data : QV4::Attr_Invalid;
-}
-
SequenceOwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value *target)
{
*target = *m;
return new SequenceOwnPropertyKeyIterator;
}
-bool Sequence::containerDeleteIndexedProperty(uint index)
+bool Sequence::containerDeleteIndexedProperty(qsizetype index)
{
- /* Qt containers have int (rather than uint) allowable indexes. */
- if (index > INT_MAX)
- return false;
if (d()->isReadOnly)
return false;
if (d()->isReference) {
@@ -361,7 +346,7 @@ bool Sequence::containerDeleteIndexedProperty(uint index)
loadReference();
}
- if (index >= quint32(size()))
+ if (index < 0 || index >= size())
return false;
/* according to ECMA262r3 it should be Undefined, */
@@ -433,26 +418,40 @@ void Sequence::storeReference()
ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- if (!id.isArrayIndex())
- return Object::virtualGet(that, id, receiver, hasProperty);
- return static_cast<const Sequence *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty);
+ if (id.isArrayIndex()) {
+ const uint index = id.asArrayIndex();
+ if (qIsAtMostSizetypeLimit(index))
+ return static_cast<const Sequence *>(that)->containerGetIndexed(qsizetype(index), hasProperty);
+
+ generateWarning(that->engine(), QLatin1String("Index out of range during indexed get"));
+ return false;
+ }
+
+ return Object::virtualGet(that, id, receiver, hasProperty);
}
bool Sequence::virtualPut(Managed *that, PropertyKey id, const Value &value, Value *receiver)
{
- if (id.isArrayIndex())
- return static_cast<Sequence *>(that)->containerPutIndexed(id.asArrayIndex(), value);
+ if (id.isArrayIndex()) {
+ const uint index = id.asArrayIndex();
+ if (qIsAtMostSizetypeLimit(index))
+ return static_cast<Sequence *>(that)->containerPutIndexed(qsizetype(index), value);
+
+ generateWarning(that->engine(), QLatin1String("Index out of range during indexed set"));
+ return false;
+ }
return Object::virtualPut(that, id, value, receiver);
}
-PropertyAttributes Sequence::queryIndexed(const Managed *that, uint index)
-{ return static_cast<const Sequence *>(that)->containerQueryIndexed(index); }
-
bool Sequence::virtualDeleteProperty(Managed *that, PropertyKey id)
{
if (id.isArrayIndex()) {
- uint index = id.asArrayIndex();
- return static_cast<Sequence *>(that)->containerDeleteIndexedProperty(index);
+ const uint index = id.asArrayIndex();
+ if (qIsAtMostSizetypeLimit(index))
+ return static_cast<Sequence *>(that)->containerDeleteIndexedProperty(qsizetype(index));
+
+ generateWarning(that->engine(), QLatin1String("Index out of range during indexed delete"));
+ return false;
}
return Object::virtualDeleteProperty(that, id);
}
@@ -479,7 +478,12 @@ static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value
RETURN_RESULT(Encode(0));
This->loadReference();
}
- RETURN_RESULT(Encode(qint32(This->size())));
+
+ const qsizetype size = This->size();
+ if (qIsAtMostUintLimit(size))
+ RETURN_RESULT(Encode(uint(size)));
+
+ return scope.engine->throwRangeError(QLatin1String("Sequence length out of range"));
}
static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
@@ -489,9 +493,9 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value
if (!This)
THROW_TYPE_ERROR();
- quint32 newLength = argc ? argv[0].toUInt32() : 0;
- /* Qt containers have int (rather than uint) allowable indexes. */
- if (newLength > INT_MAX) {
+ bool ok = false;
+ const quint32 argv0 = argc ? argv[0].asArrayLength(&ok) : 0;
+ if (!ok || !qIsAtMostSizetypeLimit(argv0)) {
generateWarning(scope.engine, QLatin1String("Index out of range during length set"));
RETURN_UNDEFINED();
}
@@ -499,15 +503,17 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value
if (This->d()->isReadOnly)
THROW_TYPE_ERROR();
+ const qsizetype newCount = qsizetype(argv0);
+
/* Read the sequence from the QObject property if we're a reference */
if (This->d()->isReference) {
if (!This->d()->object)
RETURN_UNDEFINED();
This->loadReference();
}
+
/* Determine whether we need to modify the sequence */
- quint32 newCount = static_cast<quint32>(newLength);
- quint32 count = static_cast<quint32>(This->size());
+ const qsizetype count = This->size();
if (newCount == count) {
RETURN_UNDEFINED();
} else if (newCount > count) {
@@ -515,19 +521,20 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value
/* according to ECMA262r3 we need to insert */
/* undefined values increasing length to newLength. */
/* We cannot, so we insert default-values instead. */
- while (newCount > count++)
- This->append(QVariant(valueMetaType));
+ This->append(newCount - count, QVariant(valueMetaType));
} else {
/* according to ECMA262r3 we need to remove */
/* elements until the sequence is the required length. */
- if (newCount < count)
- This->removeLast(count - newCount);
+ Q_ASSERT(newCount < count);
+ This->removeLast(count - newCount);
}
+
/* write back if required. */
if (This->d()->isReference) {
/* write back. already checked that object is non-null, so skip that check here. */
This->storeReference();
}
+
RETURN_UNDEFINED();
}
@@ -631,9 +638,12 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin
const QMetaSequence *meta = priv->extraData.ld;
const QMetaType containerMetaType(priv->listId);
QVariant result(containerMetaType);
- quint32 length = a->getLength();
+ qint64 length = a->getLength();
+ Q_ASSERT(length >= 0);
+ Q_ASSERT(length <= qint64(std::numeric_limits<quint32>::max()));
+
QV4::ScopedValue v(scope);
- for (quint32 i = 0; i < length; ++i) {
+ for (quint32 i = 0; i < quint32(length); ++i) {
const QMetaType valueMetaType = priv->typeId;
QVariant variant = scope.engine->toVariant(a->get(i), valueMetaType, false);
if (valueMetaType == QMetaType::fromType<QVariant>()) {
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index 51cdc2fc2c..256d324355 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -75,26 +75,21 @@ public:
static QV4::ReturnedValue virtualGet(
const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver);
- static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index);
static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id);
static bool virtualIsEqualTo(Managed *that, Managed *other);
static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
qsizetype size() const;
- QVariant at(int index) const;
+ QVariant at(qsizetype index) const;
void append(const QVariant &item);
- void replace(int index, const QVariant &item);
- void removeLast(int num);
+ void append(qsizetype num, const QVariant &item);
+ void replace(qsizetype index, const QVariant &item);
+ void removeLast(qsizetype num);
QVariant toVariant() const;
- // ### Qt 7 use qsizetype instead.
- QV4::ReturnedValue containerGetIndexed(uint index, bool *hasProperty) const;
-
- // ### Qt 7 use qsizetype instead.
- bool containerPutIndexed(uint index, const QV4::Value &value);
-
- QV4::PropertyAttributes containerQueryIndexed(uint index) const;
- bool containerDeleteIndexedProperty(uint index);
+ QV4::ReturnedValue containerGetIndexed(qsizetype index, bool *hasProperty) const;
+ bool containerPutIndexed(qsizetype index, const QV4::Value &value);
+ bool containerDeleteIndexedProperty(qsizetype index);
bool containerIsEqualTo(Managed *other);
bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc);
void *getRawContainerPtr() const;
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 29da1002c4..ba6058ff3d 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -20,18 +20,6 @@ using namespace Qt::StringLiterals;
DEFINE_OBJECT_VTABLE(QmlListWrapper);
-static bool isAtMostUintLimit(qsizetype length, uint limit = std::numeric_limits<uint>::max())
-{
- // Use the type with the larger positive range to do the comparison.
-
- Q_ASSERT(length >= 0);
- if constexpr (sizeof(qsizetype) > sizeof(uint)) {
- return length <= qsizetype(limit);
- } else {
- return uint(length) <= limit;
- }
-}
-
void Heap::QmlListWrapper::init()
{
Object::init();
@@ -249,7 +237,7 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const
}
const qsizetype length = property->count(property);
- if (!isAtMostUintLimit(length, std::numeric_limits<uint>::max() - argc))
+ if (!qIsAtMostUintLimit(length, std::numeric_limits<uint>::max() - argc))
return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
for (int i = 0; i < argc; ++i) {
@@ -334,7 +322,7 @@ ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, cons
return scope.engine->throwTypeError();
}
- if (!isAtMostUintLimit(deleteCount, std::numeric_limits<uint>::max() - 1))
+ if (!qIsAtMostUintLimit(deleteCount, std::numeric_limits<uint>::max() - 1))
return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
if (!property->at)
@@ -404,7 +392,7 @@ ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, con
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
const qsizetype len = property->count(property);
- if (std::numeric_limits<qsizetype>::max() - len < argc || !isAtMostUintLimit(len + argc))
+ if (std::numeric_limits<qsizetype>::max() - len < argc || !qIsAtMostUintLimit(len + argc))
return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
if (!property->append)
@@ -490,7 +478,7 @@ ReturnedValue PropertyListPrototype::method_indexOf(const FunctionObject *b, con
for (qsizetype i = fromIndex; i < len; ++i) {
if (property->at(property, i) == searchValue) {
- if (isAtMostUintLimit(i))
+ if (qIsAtMostUintLimit(i))
return Encode(uint(i));
return engine->throwRangeError(QString::fromLatin1("List length out of range."));
}
@@ -523,7 +511,7 @@ ReturnedValue PropertyListPrototype::method_lastIndexOf(const FunctionObject *b,
for (qsizetype i = fromIndex; i >= 0; --i) {
if (property->at(property, i) == searchValue) {
- if (isAtMostUintLimit(i))
+ if (qIsAtMostUintLimit(i))
return Encode(uint(i));
return engine->throwRangeError(QString::fromLatin1("List length out of range."));
}
@@ -585,7 +573,7 @@ ReturnedValue PropertyListPrototype::method_get_length(const FunctionObject *b,
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
qsizetype count = property->count(property);
- if (isAtMostUintLimit(count))
+ if (qIsAtMostUintLimit(count))
return Encode(uint(count));
return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
@@ -618,7 +606,7 @@ ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b,
return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
qsizetype count = property->count(property);
- if (!isAtMostUintLimit(count))
+ if (!qIsAtMostUintLimit(count))
return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
if (newLength < uint(count)) {
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index dec7eb63b7..8a0bc1a08e 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -829,7 +829,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
engine, propType, a[0], &success));
if (!success) {
qmlWarning(object)
- << "Could not create a QML sequence object for"
+ << "Could not create a QML sequence object for "
<< propType.name();
}
needActivate = true;
diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml
index a3f306f717..b1d422b6e9 100644
--- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml
+++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml
@@ -30,15 +30,19 @@ Item {
var tooBigIndex = msco.tooBigIndex;
var negativeIndex = msco.negativeIndex;
- // shouldn't be able to set the length > maxIndex.
- msco.intListProperty.length = tooBigIndex;
+ // We cannot test this anymore since INT_MAX + <a bit> is actually supported on 64bit.
+ // Trying to do this just wastes a lot of memory and takes forever.
+ // msco.intListProperty.length = tooBigIndex;
+
if (msco.intListProperty.length != expectedLength)
success = false;
if (!verifyExpected(msco.intListProperty, 4))
success = false;
- // shouldn't be able to set any index > maxIndex.
- msco.intListProperty[tooBigIndex] = 12;
+ // We cannot test this anymore since INT_MAX + <a bit> is actually supported on 64bit.
+ // Trying to do this just wastes a lot of memory and takes forever.
+ // msco.intListProperty[tooBigIndex] = 12;
+
if (msco.intListProperty.length != expectedLength)
success = false;
if (!verifyExpected(msco.intListProperty, 4))
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 9aacb05043..033de56695 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -5948,17 +5948,19 @@ void tst_qqmlecmascript::sequenceConversionIndexes()
{
// ensure that we gracefully fail if unsupported index values are specified.
// Qt container classes only support non-negative, signed integer index values.
+
+ // Since Qt6, on 64bit the maximum length is beyond what we can encode in a 32bit integer.
+ // Therefore we cannot test the overflow anymore.
+
QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml");
QQmlEngine engine;
QQmlComponent component(&engine, qmlFile);
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
- QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
- QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
- QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
- QTest::ignoreMessage(QtWarningMsg, qPrintable(w1));
- QTest::ignoreMessage(QtWarningMsg, qPrintable(w2));
- QTest::ignoreMessage(QtWarningMsg, qPrintable(w3));
+
+ const QString w = qmlFile.toString() + QLatin1String(":59: Index out of range during length set");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w));
+
QMetaObject::invokeMethod(object.data(), "indexedAccess");
QVERIFY(object->property("success").toBool());
QMetaObject::invokeMethod(object.data(), "indexOf");
diff --git a/tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml b/tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml
new file mode 100644
index 0000000000..9553ed6087
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/v4SequenceMethods.qml
@@ -0,0 +1,198 @@
+import QtQml
+
+QtObject {
+ property rect self: Qt.rect(9, 9, 9, 9)
+
+ property rect r1: Qt.rect(1, 2, 3, 4)
+ property rect r2: Qt.rect(5, 6, 7, 8)
+ property rect r3: Qt.rect(9, 10, 11, 12)
+
+ function jsArray() { return [r1, r2, r3] }
+ property list<rect> v4Sequence: [r1, r2, r3]
+
+ property string jsArrayToString: jsArray().toString()
+ property string v4SequenceToString: v4Sequence.toString()
+
+ property string jsArrayToLocaleString: jsArray().toLocaleString()
+ property string v4SequenceToLocaleString: v4Sequence.toLocaleString()
+
+ property list<rect> v4SequenceConcat: v4Sequence.concat(v4Sequence)
+ property list<rect> jsArrayConcat: jsArray().concat(jsArray())
+
+ property rect v4SequenceFind: v4Sequence.find(element => element.x === 1)
+ property rect jsArrayFind: jsArray().find(element => element.x === 1)
+
+ property int v4SequenceFindIndex: v4Sequence.findIndex(element => element === r2)
+ property int jsArrayFindIndex: jsArray().findIndex(element => element === r2)
+
+ property bool v4SequenceIncludes: v4Sequence.includes(r3)
+ property bool jsArrayIncludes: jsArray().includes(r3)
+
+ property string v4SequenceJoin: v4Sequence.join()
+ property string jsArrayJoin: jsArray().join()
+
+ property bool entriesMatch: {
+ var iterator = v4Sequence.entries();
+ for (var [index, element] of jsArray().entries()) {
+ var v = iterator.next().value;
+ if (index !== v[0] || element !== v[1]) {
+ console.log(index, v[0], element, v[1]);
+ return false;
+ }
+ }
+
+ var iterator = jsArray().entries();
+ for (var [index, element] of v4Sequence.entries()) {
+ var v = iterator.next().value;
+ if (index !== v[0] || element !== v[1]) {
+ console.log(index, v[0], element, v[1]);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ property bool keysMatch: {
+ var iterator = v4Sequence.keys();
+ for (var index of jsArray().keys()) {
+ var v = iterator.next().value;
+ if (index !== v) {
+ console.log(index, v);
+ return false;
+ }
+ }
+
+ var iterator = jsArray().keys();
+ for (var index of v4Sequence.keys()) {
+ var v = iterator.next().value;
+ if (index !== v) {
+ console.log(index, v);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ property bool valuesMatch: {
+ var iterator = v4Sequence.values();
+ for (var obj of jsArray().values()) {
+ var v = iterator.next().value;
+ if (obj !== v) {
+ console.log(obj, v);
+ return false;
+ }
+ }
+
+ var iterator = jsArray().values();
+ for (var obj of v4Sequence.values()) {
+ var v = iterator.next().value;
+ if (obj !== v) {
+ console.log(obj, v);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ property list<rect> v4SequencePop: v4Sequence
+ property rect v4SequencePopped
+
+ property list<rect> jsArrayPop
+ property rect jsArrayPopped
+
+ property list<rect> v4SequencePush: v4Sequence
+ property int v4SequencePushed
+
+ property list<rect> jsArrayPush
+ property int jsArrayPushed
+
+ property list<rect> v4SequenceReverse: v4Sequence
+ property list<rect> jsArrayReverse: jsArray().reverse()
+
+ property list<rect> v4SequenceShift: v4Sequence
+ property rect v4SequenceShifted
+
+ property list<rect> jsArrayShift
+ property rect jsArrayShifted
+
+ property list<rect> v4SequenceSplice: v4Sequence
+ property list<rect> v4SequenceSpliced
+
+ property list<rect> jsArraySplice
+ property list<rect> jsArraySpliced
+
+ property list<rect> v4SequenceUnshift: v4Sequence
+ property int v4SequenceUnshifted
+
+ property list<rect> jsArrayUnshift
+ property int jsArrayUnshifted
+
+ property int v4SequenceIndexOf: v4Sequence.indexOf(r2)
+ property int jsArrayIndexOf: jsArray().indexOf(r2)
+
+ property int v4SequenceLastIndexOf: v4Sequence.lastIndexOf(r3)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(r3)
+
+ property bool v4SequenceEvery: v4Sequence.every((element) => element != null)
+ property bool jsArrayEvery: jsArray().every((element) => element != null)
+
+ property bool v4SequenceSome: v4Sequence.some((element) => element.x === 1)
+ property bool jsArraySome: jsArray().some((element) => element.x === 1)
+
+ property string v4SequenceForEach
+ property string jsArrayForEach
+
+ property list<int> v4SequenceMap: v4Sequence.map(((element) => element.x))
+ property list<int> jsArrayMap: jsArray().map(((element) => element.x))
+
+ property list<rect> v4SequenceFilter: v4Sequence.filter((element) => element.x != 1)
+ property list<rect> jsArrayFilter: jsArray().filter((element) => element.x != 1)
+
+ property string v4SequenceReduce: v4Sequence.reduce((element, v) => v + '-' + element.x + 'v', "")
+ property string jsArrayReduce: jsArray().reduce((element, v) => v + '-' + element.x + 'v', "")
+
+ property string v4SequenceReduceRight: v4Sequence.reduceRight((element, v) => v + '-' + element.x + 'v', "")
+ property string jsArrayReduceRight: jsArray().reduceRight((element, v) => v + '-' + element.x + 'v', "")
+
+ property list<string> jsArrayOwnPropertyNames: Object.getOwnPropertyNames(jsArray())
+ property list<string> v4SequenceOwnPropertyNames: Object.getOwnPropertyNames(v4Sequence)
+
+ property list<rect> v4SequenceSort1: v4Sequence
+ property list<rect> jsArraySort1: jsArray().sort()
+
+ property list<rect> v4SequenceSort2: v4Sequence
+ property list<rect> jsArraySort2: jsArray().sort((a, b) => (a.x - b.x))
+
+ Component.onCompleted: {
+ v4SequenceReverse.reverse();
+
+ v4SequencePopped = v4SequencePop.pop();
+ var a = jsArray();
+ jsArrayPopped = a.pop();
+ jsArrayPop = a;
+
+ v4SequencePushed = v4SequencePush.push(self);
+ a = jsArray();
+ jsArrayPushed = a.push(self);
+ jsArrayPush = a;
+
+ v4SequenceShifted = v4SequenceShift.shift();
+ a = jsArray();
+ jsArrayShifted = a.shift();
+ jsArrayShift = a;
+
+ v4SequenceUnshifted = v4SequenceUnshift.unshift(self);
+ a = jsArray();
+ jsArrayUnshifted = a.unshift(self);
+ jsArrayUnshift = a;
+
+ v4Sequence.forEach((element) => { v4SequenceForEach += "-" + element.x + "-" });
+ jsArray().forEach((element) => { jsArrayForEach += "-" + element.x + "-" });
+
+ v4SequenceSort1.sort();
+ v4SequenceSort2.sort((a, b) => (a.x - b.x))
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml b/tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml
new file mode 100644
index 0000000000..001f092c0a
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/v4SequenceMethodsWithParams.qml
@@ -0,0 +1,47 @@
+import QtQml
+
+QtObject {
+ property rect self: Qt.rect(9, 9, 9, 9)
+
+ required property real i
+ required property real j
+ required property real k
+
+ property rect r1: Qt.rect(1, 2, 3, 4)
+ property rect r2: Qt.rect(5, 6, 7, 8)
+ property rect r3: Qt.rect(9, 10, 11, 12)
+
+ function jsArray() { return [r1, r2, r3] }
+ property list<rect> v4Sequence: [r1, r2, r3]
+
+ property list<rect> v4SequenceCopyWithin: v4Sequence
+ property list<rect> jsArrayCopyWithin: jsArray().copyWithin(i, j, k)
+
+ property list<rect> v4SequenceFill: v4Sequence
+ property list<rect> jsArrayFill: jsArray().fill(self, i, Math.min(j, 1024))
+
+ property list<rect> v4SequenceSlice: v4Sequence.slice(i, j)
+ property list<rect> jsArraySlice: jsArray().slice(i, j)
+
+ property list<rect> v4SequenceSplice: v4Sequence
+ property list<rect> v4SequenceSpliced
+
+ property list<rect> jsArraySplice
+ property list<rect> jsArraySpliced
+
+ property int v4SequenceIndexOf: v4Sequence.indexOf(r2, i)
+ property int jsArrayIndexOf: jsArray().indexOf(r2, i)
+
+ property int v4SequenceLastIndexOf: v4Sequence.lastIndexOf(r3, i)
+ property int jsArrayLastIndexOf: jsArray().lastIndexOf(r3, i)
+
+ Component.onCompleted: {
+ v4SequenceCopyWithin.copyWithin(i, j, k);
+ v4SequenceFill.fill(self, i, Math.min(j, 1024));
+
+ v4SequenceSpliced = v4SequenceSplice.splice(i, j, self, self, self);
+ var a = jsArray();
+ jsArraySpliced = a.splice(i, j, self, self, self);
+ jsArraySplice = a;
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index ebc677c74f..8bed8a9880 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -11,6 +11,7 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qscopeguard.h>
+#include <QtCore/qrandom.h>
#include <QtGui/qevent.h>
#include <QSignalSpy>
#include <QFont>
@@ -388,6 +389,9 @@ private slots:
void leakingAttributesQmlForeign();
void attachedOwnProperties();
void bindableOnly();
+ void v4SequenceMethods();
+ void v4SequenceMethodsWithParams_data();
+ void v4SequenceMethodsWithParams();
private:
QQmlEngine engine;
@@ -7410,6 +7414,165 @@ void tst_qqmllanguage::bindableOnly()
QCOMPARE(o->property("a").toInt(), 5);
}
+static void listsEqual(QObject *object, const char *method)
+{
+ const QByteArray v4SequencePropertyName = QByteArray("v4Sequence") + method;
+ const QByteArray jsArrayPropertyName = QByteArray("jsArray") + method;
+
+ const QList<QRectF> v4SequenceProperty
+ = object->property(v4SequencePropertyName.constData()).value<QList<QRectF>>();
+ const QList<QRectF> jsArrayProperty
+ = object->property(jsArrayPropertyName.constData()).value<QList<QRectF>>();
+
+ const qsizetype v4SequenceCount = v4SequenceProperty.count();
+ QCOMPARE(v4SequenceCount, jsArrayProperty.count());
+
+ for (qsizetype i = 0; i < v4SequenceCount; ++i)
+ QCOMPARE(v4SequenceProperty.at(i), jsArrayProperty.at(i));
+}
+
+void tst_qqmllanguage::v4SequenceMethods()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("v4SequenceMethods.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QCOMPARE(object->property("v4SequenceToString"), object->property("jsArrayToString"));
+ QCOMPARE(object->property("v4SequenceToLocaleString"), object->property("jsArrayToLocaleString"));
+
+ QVERIFY(object->property("entriesMatch").toBool());
+ QVERIFY(object->property("keysMatch").toBool());
+ QVERIFY(object->property("valuesMatch").toBool());
+
+ listsEqual(object.data(), "Concat");
+ listsEqual(object.data(), "Pop");
+ listsEqual(object.data(), "Push");
+ listsEqual(object.data(), "Reverse");
+ listsEqual(object.data(), "Shift");
+ listsEqual(object.data(), "Unshift");
+ listsEqual(object.data(), "Filter");
+ listsEqual(object.data(), "Sort1");
+ listsEqual(object.data(), "Sort2");
+
+ QCOMPARE(object->property("v4SequenceFind"), object->property("jsArrayFind"));
+ QCOMPARE(object->property("v4SequenceFind").value<QRectF>().x(), 1.0);
+
+ QCOMPARE(object->property("v4SequenceFindIndex"), object->property("jsArrayFindIndex"));
+ QCOMPARE(object->property("v4SequenceFindIndex").toInt(), 1);
+
+ QCOMPARE(object->property("v4SequenceIncludes"), object->property("jsArrayIncludes"));
+ QVERIFY(object->property("v4SequenceIncludes").toBool());
+
+ QCOMPARE(object->property("v4SequenceJoin"), object->property("jsArrayJoin"));
+ QVERIFY(object->property("v4SequenceJoin").toString().contains(QStringLiteral("1")));
+
+ QCOMPARE(object->property("v4SequencePopped"), object->property("jsArrayPopped"));
+ QVERIFY(object->property("v4SequencePopped").value<QRectF>().x() != 1.0);
+
+ QCOMPARE(object->property("v4SequencePushed"), object->property("jsArrayPushed"));
+ QCOMPARE(object->property("v4SequencePushed").toInt(), 4);
+
+ QCOMPARE(object->property("v4SequenceShifted"), object->property("jsArrayShifted"));
+ QCOMPARE(object->property("v4SequenceShifted").value<QRectF>().x(), 1.0);
+
+ QCOMPARE(object->property("v4SequenceUnshifted"), object->property("jsArrayUnshifted"));
+ QCOMPARE(object->property("v4SequenceUnshifted").toInt(), 4);
+
+ QCOMPARE(object->property("v4SequenceIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("v4SequenceIndexOf").toInt(), 1);
+
+ QCOMPARE(object->property("v4SequenceLastIndexOf"), object->property("jsArrayLastIndexOf"));
+ QCOMPARE(object->property("v4SequenceLastIndexOf").toInt(), 2);
+
+ QCOMPARE(object->property("v4SequenceEvery"), object->property("jsArrayEvery"));
+ QVERIFY(object->property("v4SequenceEvery").toBool());
+
+ QCOMPARE(object->property("v4SequenceSome"), object->property("jsArrayEvery"));
+ QVERIFY(object->property("v4SequenceSome").toBool());
+
+ QCOMPARE(object->property("v4SequenceForEach"), object->property("jsArrayForEach"));
+ QCOMPARE(object->property("v4SequenceForEach").toString(), QStringLiteral("-1--5--9-"));
+
+ QCOMPARE(object->property("v4SequenceMap").toStringList(), object->property("jsArrayMap").toStringList());
+ QCOMPARE(object->property("v4SequenceReduce").toString(), object->property("jsArrayReduce").toString());
+
+ QCOMPARE(object->property("v4SequenceOwnPropertyNames").toStringList(),
+ object->property("jsArrayOwnPropertyNames").toStringList());
+}
+
+void tst_qqmllanguage::v4SequenceMethodsWithParams_data()
+{
+ QTest::addColumn<double>("i");
+ QTest::addColumn<double>("j");
+ QTest::addColumn<double>("k");
+
+ const double indices[] = {
+ double(std::numeric_limits<qsizetype>::min()),
+ double(std::numeric_limits<qsizetype>::min()) + 1,
+ double(std::numeric_limits<uint>::min()) - 1,
+ double(std::numeric_limits<uint>::min()),
+ double(std::numeric_limits<uint>::min()) + 1,
+ double(std::numeric_limits<int>::min()),
+ -10, -3, -2, -1, 0, 1, 2, 3, 10,
+ double(std::numeric_limits<int>::max()),
+ double(std::numeric_limits<uint>::max()) - 1,
+ double(std::numeric_limits<uint>::max()),
+ double(std::numeric_limits<uint>::max()) + 1,
+ double(std::numeric_limits<qsizetype>::max() - 1),
+ double(std::numeric_limits<qsizetype>::max()),
+ };
+
+ // We cannot test the full cross product. So, take a random sample instead.
+ const qsizetype numIndices = sizeof(indices) / sizeof(double);
+ qsizetype seed = QRandomGenerator::global()->generate();
+ const int numSamples = 4;
+ for (int i = 0; i < numSamples; ++i) {
+ seed = qHash(i, seed);
+ const double vi = indices[qAbs(seed) % numIndices];
+ for (int j = 0; j < numSamples; ++j) {
+ seed = qHash(j, seed);
+ const double vj = indices[qAbs(seed) % numIndices];
+ for (int k = 0; k < numSamples; ++k) {
+ seed = qHash(k, seed);
+ const double vk = indices[qAbs(seed) % numIndices];
+ const QString tag = QLatin1String("%1/%2/%3")
+ .arg(QString::number(vi), QString::number(vj), QString::number(vk));
+ QTest::newRow(qPrintable(tag)) << vi << vj << vk;
+
+ // output all the tags so that we can find out what combination caused a test to hang.
+ qDebug().noquote() << "scheduling" << tag;
+ }
+ }
+ }
+}
+
+void tst_qqmllanguage::v4SequenceMethodsWithParams()
+{
+ QFETCH(double, i);
+ QFETCH(double, j);
+ QFETCH(double, k);
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("v4SequenceMethodsWithParams.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> object(component.createWithInitialProperties({
+ {QStringLiteral("i"), i},
+ {QStringLiteral("j"), j},
+ {QStringLiteral("k"), k}
+ }));
+ QVERIFY(!object.isNull());
+
+ listsEqual(object.data(), "CopyWithin");
+ listsEqual(object.data(), "Fill");
+ listsEqual(object.data(), "Slice");
+ listsEqual(object.data(), "Splice");
+ listsEqual(object.data(), "Spliced");
+
+ QCOMPARE(object->property("v4SequenceIndexOf"), object->property("jsArrayIndexOf"));
+ QCOMPARE(object->property("v4SequenceLastIndexOf"), object->property("jsArrayLastIndexOf"));
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"