summaryrefslogtreecommitdiff
path: root/src/qml/jsruntime/qv4sequenceobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4sequenceobject.cpp')
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp192
1 files changed, 101 insertions, 91 deletions
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>()) {