aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp73
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h68
-rw-r--r--src/qml/jsruntime/qv4arrayiterator.cpp1
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp39
-rw-r--r--src/qml/jsruntime/qv4engine.cpp2
-rw-r--r--src/qml/jsruntime/qv4managed.cpp3
-rw-r--r--src/qml/jsruntime/qv4managed_p.h9
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h7
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp4
-rw-r--r--src/qml/qml/qqmllist.h3
-rw-r--r--src/qml/qml/qqmllist_p.h169
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp516
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h14
13 files changed, 751 insertions, 157 deletions
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 5c62e806f7..6e01c50048 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -587,21 +587,6 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor)
s->setArrayData(o->engine(), n->value + Object::SetterOffset, v[Object::SetterOffset]);
}
-
-class ArrayElementLessThan
-{
-public:
- inline ArrayElementLessThan(ExecutionEngine *engine, const Value &comparefn)
- : m_engine(engine), m_comparefn(comparefn) {}
-
- bool operator()(Value v1, Value v2) const;
-
-private:
- ExecutionEngine *m_engine;
- const Value &m_comparefn;
-};
-
-
bool ArrayElementLessThan::operator()(Value v1, Value v2) const
{
Scope scope(m_engine);
@@ -634,60 +619,6 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const
return p1s->toQString() < p2s->toQString();
}
-template <typename RandomAccessIterator, typename T, typename LessThan>
-void sortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan)
-{
-top:
- int span = int(end - start);
- if (span < 2)
- return;
-
- --end;
- RandomAccessIterator low = start, high = end - 1;
- RandomAccessIterator pivot = start + span / 2;
-
- if (lessThan(*end, *start))
- qSwap(*end, *start);
- if (span == 2)
- return;
-
- if (lessThan(*pivot, *start))
- qSwap(*pivot, *start);
- if (lessThan(*end, *pivot))
- qSwap(*end, *pivot);
- if (span == 3)
- return;
-
- qSwap(*pivot, *end);
-
- while (low < high) {
- while (low < high && lessThan(*low, *end))
- ++low;
-
- while (high > low && lessThan(*end, *high))
- --high;
-
- if (low < high) {
- qSwap(*low, *high);
- ++low;
- --high;
- } else {
- break;
- }
- }
-
- if (lessThan(*low, *end))
- ++low;
-
- qSwap(*end, *low);
- sortHelper(start, low, t, lessThan);
-
- start = low + 1;
- ++end;
- goto top;
-}
-
-
void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &comparefn, uint len)
{
if (!len)
@@ -778,10 +709,10 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
}
- ArrayElementLessThan lessThan(engine, static_cast<const FunctionObject &>(comparefn));
+ ArrayElementLessThan lessThan(engine, comparefn);
Value *begin = thisObject->arrayData()->values.values;
- sortHelper(begin, begin + len, *begin, lessThan);
+ sortHelper(begin, begin + len, lessThan);
#ifdef CHECK_SPARSE_ARRAYS
thisObject->initSparseArray();
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index 25792f7076..a7ab1f4a71 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -231,6 +231,74 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData
static uint length(const Heap::ArrayData *d);
};
+class ArrayElementLessThan
+{
+public:
+ inline ArrayElementLessThan(ExecutionEngine *engine, const Value &comparefn)
+ : m_engine(engine), m_comparefn(comparefn) {}
+
+ bool operator()(Value v1, Value v2) const;
+
+private:
+ ExecutionEngine *m_engine;
+ const Value &m_comparefn;
+};
+
+template <typename RandomAccessIterator, typename LessThan>
+void sortHelper(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan)
+{
+top:
+ using std::swap;
+
+ int span = int(end - start);
+ if (span < 2)
+ return;
+
+ --end;
+ RandomAccessIterator low = start, high = end - 1;
+ RandomAccessIterator pivot = start + span / 2;
+
+ if (lessThan(*end, *start))
+ swap(*end, *start);
+ if (span == 2)
+ return;
+
+ if (lessThan(*pivot, *start))
+ swap(*pivot, *start);
+ if (lessThan(*end, *pivot))
+ swap(*end, *pivot);
+ if (span == 3)
+ return;
+
+ swap(*pivot, *end);
+
+ while (low < high) {
+ while (low < high && lessThan(*low, *end))
+ ++low;
+
+ while (high > low && lessThan(*end, *high))
+ --high;
+
+ if (low < high) {
+ swap(*low, *high);
+ ++low;
+ --high;
+ } else {
+ break;
+ }
+ }
+
+ if (lessThan(*low, *end))
+ ++low;
+
+ swap(*end, *low);
+ sortHelper(start, low, lessThan);
+
+ start = low + 1;
+ ++end;
+ goto top;
+}
+
namespace Heap {
inline uint ArrayData::mappedIndex(uint index) const
diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp
index 80e7506143..15e0bf4e4c 100644
--- a/src/qml/jsruntime/qv4arrayiterator.cpp
+++ b/src/qml/jsruntime/qv4arrayiterator.cpp
@@ -36,7 +36,6 @@ ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const
quint32 index = thisObject->d()->nextIndex;
IteratorKind itemKind = thisObject->d()->iterationKind;
- Scoped<TypedArray> ta(scope, a->as<TypedArray>());
quint32 len = a->getLength();
if (index >= len) {
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index 67cf5c2fdd..ebe70406cb 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -398,7 +398,7 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value
return scope.engine->throwTypeError();
}
}
- } else if (eltAsObj && eltAsObj->isListType()) {
+ } else if (eltAsObj && eltAsObj->isArrayLike()) {
const uint startIndex = result->getLength();
for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) {
entry = eltAsObj->get(i);
@@ -1188,31 +1188,38 @@ ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *
if (!instance)
RETURN_UNDEFINED();
- uint len = instance->getLength();
- int relativeStart = argc > 1 ? argv[1].toInteger() : 0;
- int relativeEnd = len;
- if (argc > 2 && !argv[2].isUndefined()) {
+ const qsizetype len = instance->getLength();
+ Q_ASSERT(len >= 0);
+
+ const qsizetype relativeStart = argc > 1 ? argv[1].toInteger() : 0;
+ qsizetype relativeEnd = len;
+ if (argc > 2 && !argv[2].isUndefined())
relativeEnd = argv[2].toInteger();
- }
- uint k = 0;
- uint fin = 0;
+
+ qsizetype k = 0;
+ qsizetype fin = 0;
if (relativeStart < 0) {
- k = std::max(len+relativeStart, uint(0));
+ if (relativeStart > -len)
+ k = std::max(len + relativeStart, qsizetype(0));
} else {
- k = std::min(uint(relativeStart), len);
+ k = std::min(relativeStart, len);
}
+ Q_ASSERT(k >= 0);
if (relativeEnd < 0) {
- fin = std::max(len + relativeEnd, uint(0));
+ if (relativeEnd > -len)
+ fin = std::max(len + relativeEnd, qsizetype(0));
} else {
- fin = std::min(uint(relativeEnd), len);
+ fin = std::min(relativeEnd, len);
}
+ Q_ASSERT(fin >= 0);
- while (k < fin) {
- instance->setIndexed(k, argv[0], QV4::Object::DoThrowOnRejection);
- k++;
- }
+ if (sizeof(qsizetype) > sizeof(uint) && fin > qsizetype(std::numeric_limits<uint>::max()))
+ return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
+
+ for (; k < fin; ++k)
+ instance->setIndexed(uint(k), argv[0], QV4::Object::DoThrowOnRejection);
return instance.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 63fafeda60..c6c504154b 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -696,7 +696,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor());
static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor());
static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor());
- static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this);
+ static_cast<PropertyListPrototype *>(propertyListPrototype())->init();
static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor());
static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor());
static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor());
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index 3c44ed525b..647b858f0f 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -104,6 +104,9 @@ QString Managed::className() const
case Type_V4Sequence:
s = "V4Sequence";
break;
+ case Type_QmlListProperty:
+ s = "QML List";
+ break;
}
return QString::fromLatin1(s);
}
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index 4865b75e2f..299d4b4196 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -120,7 +120,9 @@ public:
Type_ForInIterator,
Type_RegExp,
- Type_V4Sequence
+ Type_V4Sequence,
+ Type_QmlListProperty,
+
};
Q_MANAGED_TYPE(Invalid)
@@ -128,8 +130,9 @@ public:
const VTable *vtable() const { return d()->internalClass->vtable; }
inline ExecutionEngine *engine() const { return internalClass()->engine; }
- bool isListType() const { return d()->internalClass->vtable->type == Type_V4Sequence; }
- bool isArrayLike() const { return isArrayObject() || isListType(); }
+ bool isV4SequenceType() const { return d()->internalClass->vtable->type == Type_V4Sequence; }
+ bool isQmlListPropertyType() const { return d()->internalClass->vtable->type == Type_QmlListProperty; }
+ bool isArrayLike() const { return isArrayObject() || isV4SequenceType() || isQmlListPropertyType(); }
bool isArrayObject() const { return d()->internalClass->vtable->type == Type_ArrayObject; }
bool isStringObject() const { return d()->internalClass->vtable->type == Type_StringObject; }
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index 09e8b60c91..fd68fcc1b3 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -32,9 +32,14 @@ namespace QV4 {
struct ScopedValue;
+inline bool hasExceptionOrIsInterrupted(ExecutionEngine *engine)
+{
+ return engine->hasException || engine->isInterrupted.loadRelaxed();
+}
+
#define CHECK_EXCEPTION() \
do { \
- if (scope.hasException() || scope.engine->isInterrupted.loadRelaxed()) { \
+ if (hasExceptionOrIsInterrupted(scope.engine)) { \
return QV4::Encode::undefined(); \
} \
} while (false)
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index f23d832953..7f4b6057ce 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -547,7 +547,7 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu
{
Scope scope(b);
QV4::ScopedObject o(scope, thisObject);
- if (!o || !o->isListType())
+ if (!o || !o->isV4SequenceType())
THROW_TYPE_ERROR();
if (argc >= 2)
@@ -610,7 +610,7 @@ ReturnedValue SequencePrototype::fromData(ExecutionEngine *engine, QMetaType typ
QVariant SequencePrototype::toVariant(const Sequence *object)
{
- Q_ASSERT(object->isListType());
+ Q_ASSERT(object->isV4SequenceType());
return object->toVariant();
}
diff --git a/src/qml/qml/qqmllist.h b/src/qml/qml/qqmllist.h
index 051f3d67ff..89bee81294 100644
--- a/src/qml/qml/qqmllist.h
+++ b/src/qml/qml/qqmllist.h
@@ -18,8 +18,6 @@ struct QMetaObject;
#define QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT Q_CLASSINFO("QML.ListPropertyAssignBehavior", "ReplaceIfNotDefault")
#define QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE Q_CLASSINFO("QML.ListPropertyAssignBehavior", "Replace")
-#ifndef QQMLLISTPROPERTY
-#define QQMLLISTPROPERTY
template<typename T>
class QQmlListProperty {
public:
@@ -153,7 +151,6 @@ private:
list->append(list, item);
}
};
-#endif
class QQmlEngine;
class QQmlListReferencePrivate;
diff --git a/src/qml/qml/qqmllist_p.h b/src/qml/qml/qqmllist_p.h
index 72a4710a15..2a323fcd59 100644
--- a/src/qml/qml/qqmllist_p.h
+++ b/src/qml/qml/qqmllist_p.h
@@ -18,7 +18,6 @@
#include "qqmllist.h"
#include "qqmlmetaobject_p.h"
#include "qqmlmetatype_p.h"
-#include "qqmlengine_p.h"
#include <QtQml/private/qbipointer_p.h>
QT_BEGIN_NAMESPACE
@@ -56,6 +55,174 @@ private:
const QMetaObject *m_elementType = nullptr;
};
+template<typename T>
+class QQmlListIterator {
+public:
+ using difference_type = qsizetype;
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = T*;
+
+ class reference
+ {
+ public:
+ explicit reference(const QQmlListIterator *iter) : m_iter(iter) {}
+ reference(const reference &) = default;
+ reference(reference &&) = default;
+ ~reference() = default;
+
+ operator T *() const
+ {
+ if (m_iter == nullptr)
+ return nullptr;
+ return m_iter->m_list->at(m_iter->m_list, m_iter->m_i);
+ }
+
+ reference &operator=(T *value) {
+ m_iter->m_list->replace(m_iter->m_list, m_iter->m_i, value);
+ return *this;
+ }
+
+ reference &operator=(const reference &value) { return operator=((T *)(value)); }
+ reference &operator=(reference &&value) { return operator=((T *)(value)); }
+
+ friend void swap(reference a, reference b)
+ {
+ T *tmp = a;
+ a = b;
+ b = std::move(tmp);
+ }
+ private:
+ const QQmlListIterator *m_iter;
+ };
+
+ class pointer
+ {
+ public:
+ explicit pointer(const QQmlListIterator *iter) : m_iter(iter) {}
+ reference operator*() const { return reference(m_iter); }
+ QQmlListIterator operator->() const { return *m_iter; }
+
+ private:
+ const QQmlListIterator *m_iter;
+ };
+
+ QQmlListIterator() = default;
+ QQmlListIterator(QQmlListProperty<T> *list, qsizetype i) : m_list(list), m_i(i) {}
+
+ QQmlListIterator &operator++()
+ {
+ ++m_i;
+ return *this;
+ }
+
+ QQmlListIterator operator++(int)
+ {
+ QQmlListIterator result = *this;
+ ++m_i;
+ return result;
+ }
+
+ QQmlListIterator &operator--()
+ {
+ --m_i;
+ return *this;
+ }
+
+ QQmlListIterator operator--(int)
+ {
+ QQmlListIterator result = *this;
+ --m_i;
+ return result;
+ }
+
+ QQmlListIterator &operator+=(qsizetype j)
+ {
+ m_i += j;
+ return *this;
+ }
+
+ QQmlListIterator &operator-=(qsizetype j)
+ {
+ m_i -= j;
+ return *this;
+ }
+
+ QQmlListIterator operator+(qsizetype j)
+ {
+ return QQmlListIterator(m_list, m_i + j);
+ }
+
+ QQmlListIterator operator-(qsizetype j)
+ {
+ return QQmlListIterator(m_list, m_i - j);
+ }
+
+ reference operator*() const
+ {
+ return reference(this);
+ }
+
+ pointer operator->() const
+ {
+ return pointer(this);
+ }
+
+private:
+ friend inline bool operator==(const QQmlListIterator &a, const QQmlListIterator &b)
+ {
+ return a.m_list == b.m_list && a.m_i == b.m_i;
+ }
+
+ friend inline bool operator!=(const QQmlListIterator &a, const QQmlListIterator &b)
+ {
+ return a.m_list != b.m_list || a.m_i != b.m_i;
+ }
+
+ friend inline bool operator<(const QQmlListIterator &i, const QQmlListIterator &j)
+ {
+ return i - j < 0;
+ }
+
+ friend inline bool operator>=(const QQmlListIterator &i, const QQmlListIterator &j)
+ {
+ return !(i < j);
+ }
+
+ friend inline bool operator>(const QQmlListIterator &i, const QQmlListIterator &j)
+ {
+ return i - j > 0;
+ }
+
+ friend inline bool operator<=(const QQmlListIterator &i, const QQmlListIterator &j)
+ {
+ return !(i > j);
+ }
+
+ friend inline QQmlListIterator operator+(qsizetype i, const QQmlListIterator &j)
+ {
+ return j + i;
+ }
+
+ friend inline qsizetype operator-(const QQmlListIterator &i, const QQmlListIterator &j)
+ {
+ return i.m_i - j.m_i;
+ }
+
+ QQmlListProperty<T> *m_list = nullptr;
+ qsizetype m_i = 0;
+};
+
+template<typename T>
+QQmlListIterator<T> begin(QQmlListProperty<T> &list)
+{
+ return QQmlListIterator<T>(&list, 0);
+}
+
+template<typename T>
+QQmlListIterator<T> end(QQmlListProperty<T> &list)
+{
+ return QQmlListIterator<T>(&list, list.count(&list));
+}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index bf45cb0bad..29da1002c4 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -2,12 +2,16 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmllistwrapper_p.h"
+
#include <private/qqmllist_p.h>
-#include <private/qv4objectproto_p.h>
-#include <qv4objectiterator_p.h>
+#include <private/qv4arrayiterator_p.h>
+#include <private/qv4arrayobject_p.h>
#include <private/qv4functionobject_p.h>
+#include <private/qv4objectiterator_p.h>
+#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4symbol_p.h>
QT_BEGIN_NAMESPACE
@@ -16,6 +20,18 @@ 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();
@@ -92,13 +108,6 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const
return Value::undefinedValue().asReturnedValue();
}
- if (id.isString() && id == v4->id_length()->propertyKey()) {
- if (hasProperty)
- *hasProperty = true;
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- return Value::fromUInt32(count).asReturnedValue();
- }
-
return Object::virtualGet(m, id, receiver, hasProperty);
}
@@ -135,43 +144,6 @@ bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value,
return false;
}
- if (id.isString() && id == v4->id_length()->propertyKey()) {
- if (!prop->count)
- return false;
-
- const quint32 count = prop->count(prop);
-
- bool ok = false;
- const uint newLength = value.asArrayLength(&ok);
- if (!ok)
- return false;
-
- if (newLength == 0) {
- if (!prop->clear)
- return false;
- prop->clear(prop);
- return true;
- }
-
- if (newLength < count) {
- if (!prop->removeLast)
- return false;
-
- for (uint i = newLength; i < count; ++i)
- prop->removeLast(prop);
-
- return true;
- }
-
- if (!prop->append)
- return false;
-
- for (uint i = count; i < newLength; ++i)
- prop->append(prop, nullptr);
-
- return true;
- }
-
return Object::virtualPut(m, id, value, receiver);
}
@@ -195,9 +167,13 @@ PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property
if (pd)
pd->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), index));
return PropertyKey::fromArrayIndex(index);
+ } else if (memberIndex == 0) {
+ ++memberIndex;
+ return o->engine()->id_length()->propertyKey();
}
- return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+ // You cannot add any own properties via the regular JavaScript interfaces.
+ return PropertyKey::invalid();
}
OwnPropertyKeyIterator *QmlListWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
@@ -206,9 +182,48 @@ OwnPropertyKeyIterator *QmlListWrapper::virtualOwnPropertyKeys(const Object *m,
return new QmlListWrapperOwnPropertyKeyIterator;
}
-void PropertyListPrototype::init(ExecutionEngine *)
+void PropertyListPrototype::init()
{
+ defineDefaultProperty(QStringLiteral("pop"), method_pop, 0);
defineDefaultProperty(QStringLiteral("push"), method_push, 1);
+ defineDefaultProperty(QStringLiteral("shift"), method_shift, 0);
+ defineDefaultProperty(QStringLiteral("splice"), method_splice, 2);
+ defineDefaultProperty(QStringLiteral("unshift"), method_unshift, 1);
+ defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
+ defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
+ defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length);
+}
+
+ReturnedValue PropertyListPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+ const qsizetype len = property->count(property);
+ if (!len)
+ RETURN_UNDEFINED();
+
+ if (!property->at)
+ return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
+ ScopedValue result(
+ scope, QV4::QObjectWrapper::wrap(scope.engine, property->at(property, len - 1)));
+
+ if (!property->removeLast)
+ return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
+ property->removeLast(property);
+
+ return result->asReturnedValue();
}
ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -224,19 +239,406 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const
QQmlListProperty<QObject> *property = &w->d()->property();
if (!property->append)
return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+
+ for (int i = 0; i < argc; ++i) {
+ const Value &arg = argv[i];
+ if (!arg.isNull() && !arg.as<QObjectWrapper>())
+ THROW_TYPE_ERROR();
+ }
- QV4::ScopedObject so(scope);
- for (int i = 0, ei = argc; i < ei; ++i)
- {
- if (argv[i].isNull()) {
+ const qsizetype length = property->count(property);
+ if (!isAtMostUintLimit(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) {
+ if (argv[i].isNull())
property->append(property, nullptr);
- } else {
- so = argv[i].toObject(scope.engine);
- if (QV4::QObjectWrapper *wrapper = so->as<QV4::QObjectWrapper>())
- property->append(property, wrapper->object() );
+ else
+ property->append(property, argv[i].as<QV4::QObjectWrapper>()->object());
+ }
+
+ return Encode(uint(length + argc));
+}
+
+ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+ QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+ const qsizetype len = property->count(property);
+ if (!len)
+ RETURN_UNDEFINED();
+
+ if (!property->at)
+ return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
+ ScopedValue result(scope, QV4::QObjectWrapper::wrap(scope.engine, property->at(property, 0)));
+
+ if (!property->replace)
+ return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
+ if (!property->removeLast)
+ return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
+
+ for (qsizetype i = 1; i < len; ++i)
+ property->replace(property, i - 1, property->at(property, i));
+ property->removeLast(property);
+
+ return result->asReturnedValue();
+}
+
+ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+ QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+ const qsizetype len = property->count(property);
+
+ const double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
+ qsizetype start;
+ if (rs < 0)
+ start = static_cast<qsizetype>(qMax(0., len + rs));
+ else
+ start = static_cast<qsizetype>(qMin(rs, static_cast<double>(len)));
+
+ qsizetype deleteCount = 0;
+ qsizetype itemCount = 0;
+ if (argc == 1) {
+ deleteCount = len - start;
+ } else if (argc > 1){
+ itemCount = argc - 2;
+ double dc = argv[1].toInteger();
+ deleteCount = static_cast<qsizetype>(qMin(qMax(dc, 0.), double(len - start)));
+ }
+
+ if (itemCount > deleteCount
+ && len > std::numeric_limits<qsizetype>::max() - itemCount + deleteCount) {
+ return scope.engine->throwTypeError();
+ }
+
+ if (!isAtMostUintLimit(deleteCount, std::numeric_limits<uint>::max() - 1))
+ return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
+
+ if (!property->at)
+ return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
+
+ for (qsizetype i = 0; i < itemCount; ++i) {
+ const auto arg = argv[i + 2];
+ if (!arg.isNull() && !arg.as<QObjectWrapper>())
+ THROW_TYPE_ERROR();
+ }
+
+ ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
+ newArray->arrayReserve(deleteCount);
+ ScopedValue v(scope);
+ for (qsizetype i = 0; i < deleteCount; ++i) {
+ newArray->arrayPut(
+ i, QObjectWrapper::wrap(scope.engine, property->at(property, start + i)));
+ }
+ newArray->setArrayLengthUnchecked(deleteCount);
+
+ if (!property->replace)
+ return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
+ if (!property->removeLast)
+ return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
+ if (!property->append)
+ return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
+
+ if (itemCount < deleteCount) {
+ for (qsizetype k = start; k < len - deleteCount; ++k)
+ property->replace(property, k + itemCount, property->at(property, k + deleteCount));
+ for (qsizetype k = len; k > len - deleteCount + itemCount; --k)
+ property->removeLast(property);
+ } else if (itemCount > deleteCount) {
+ for (qsizetype k = 0; k < itemCount - deleteCount; ++k)
+ property->append(property, nullptr);
+ for (qsizetype k = len - deleteCount; k > start; --k) {
+ property->replace(
+ property, k + itemCount - 1, property->at(property, k + deleteCount - 1));
+ }
+ }
+
+ for (qsizetype i = 0; i < itemCount; ++i) {
+ const auto arg = argv[i + 2];
+ if (arg.isNull())
+ property->replace(property, start + i, nullptr);
+ else
+ property->replace(property, start + i, arg.as<QObjectWrapper>()->object());
+ }
+
+ return newArray->asReturnedValue();
+}
+
+ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+
+ if (!property->count)
+ 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))
+ return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
+
+ if (!property->append)
+ return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
+ if (!property->replace)
+ return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
+
+ for (int i = 0; i < argc; ++i) {
+ const auto arg = argv[i];
+ if (!arg.isNull() && !arg.as<QObjectWrapper>())
+ THROW_TYPE_ERROR();
+ }
+
+ for (int i = 0; i < argc; ++i)
+ property->append(property, nullptr);
+
+ for (qsizetype k = len; k > 0; --k)
+ property->replace(property, k + argc - 1, property->at(property, k - 1));
+
+ for (int i = 0; i < argc; ++i)
+ property->replace(property, i, argv[i].as<QObjectWrapper>()->object());
+
+ return Encode(uint(len + argc));
+}
+
+template<typename Iterate>
+ReturnedValue firstOrLastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, Iterate iterate)
+{
+ Scope scope(b);
+
+ // Undefined cannot be encoded as QObject*. In particular it's not nullptr.
+ if (argc == 0)
+ THROW_TYPE_ERROR();
+
+ QObject *searchValue;
+ if (argv[0].isNull()) {
+ searchValue = nullptr;
+ } else {
+ Scoped<QObjectWrapper> wrapper(scope, argv[0]);
+ if (wrapper)
+ searchValue = wrapper->object();
+ else
+ THROW_TYPE_ERROR();
+ }
+
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+ const qsizetype len = property->count(property);
+ if (!len)
+ return Encode(-1);
+
+
+ return iterate(scope.engine, property, len, searchValue);
+}
+
+ReturnedValue PropertyListPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ return firstOrLastIndexOf(
+ b, thisObject, argv, argc,
+ [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property,
+ qsizetype len, QObject *searchValue) -> ReturnedValue {
+ qsizetype fromIndex = 0;
+ if (argc >= 2) {
+ double f = argv[1].toInteger();
+ if (hasExceptionOrIsInterrupted(engine))
+ return Encode::undefined();
+ if (f >= len)
+ return Encode(-1);
+ if (f < 0)
+ f = qMax(len + f, 0.);
+ fromIndex = qsizetype(f);
+ }
+
+ for (qsizetype i = fromIndex; i < len; ++i) {
+ if (property->at(property, i) == searchValue) {
+ if (isAtMostUintLimit(i))
+ return Encode(uint(i));
+ return engine->throwRangeError(QString::fromLatin1("List length out of range."));
+ }
}
+
+ return Encode(-1);
+ });
+}
+
+ReturnedValue PropertyListPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ return firstOrLastIndexOf(
+ b, thisObject, argv, argc,
+ [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property,
+ qsizetype len, QObject *searchValue) -> ReturnedValue {
+ qsizetype fromIndex = len - 1;
+ if (argc >= 2) {
+ double f = argv[1].toInteger();
+ if (hasExceptionOrIsInterrupted(engine))
+ return Encode::undefined();
+ if (f > 0)
+ f = qMin(f, (double)(len - 1));
+ else if (f < 0) {
+ f = len + f;
+ if (f < 0)
+ return Encode(-1);
+ }
+ fromIndex = qsizetype(f);
+ }
+
+ for (qsizetype i = fromIndex; i >= 0; --i) {
+ if (property->at(property, i) == searchValue) {
+ if (isAtMostUintLimit(i))
+ return Encode(uint(i));
+ return engine->throwRangeError(QString::fromLatin1("List length out of range."));
+ }
+ }
+
+ return Encode(-1);
+ });
+}
+
+ReturnedValue PropertyListPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+ if (property->count(property) == 0)
+ return thisObject->asReturnedValue();
+ if (!property->at)
+ return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
+ if (!property->replace)
+ return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
+
+ ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
+ if (!comparefn->isUndefined() && !comparefn->isFunctionObject())
+ THROW_TYPE_ERROR();
+
+ const ArrayElementLessThan lessThan(scope.engine, comparefn);
+ sortHelper(begin(*property), end(*property), [&](QObject *a, QObject *b) {
+ Scoped<QObjectWrapper> o1(scope, QObjectWrapper::wrap(scope.engine, a));
+ Scoped<QObjectWrapper> o2(scope, QObjectWrapper::wrap(scope.engine, b));
+ return lessThan(o1, o2);
+ });
+
+ return thisObject->asReturnedValue();
+}
+
+ReturnedValue PropertyListPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ const QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+
+ qsizetype count = property->count(property);
+ if (isAtMostUintLimit(count))
+ return Encode(uint(count));
+
+ return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
+}
+
+ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ const QmlListWrapper *w = instance->as<QmlListWrapper>();
+ if (!w)
+ RETURN_UNDEFINED();
+
+ QQmlListProperty<QObject> *property = &w->d()->property();
+
+ bool ok = false;
+ const uint newLength = argc ? argv[0].asArrayLength(&ok) : 0;
+ if (!ok)
+ return scope.engine->throwRangeError(QString::fromLatin1("Invalid list length."));
+
+ if (newLength == 0 && property->clear) {
+ property->clear(property);
+ return true;
}
- return Encode::undefined();
+
+ if (!property->count)
+ return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
+
+ qsizetype count = property->count(property);
+ if (!isAtMostUintLimit(count))
+ return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
+
+ if (newLength < uint(count)) {
+ if (!property->removeLast)
+ return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
+
+ for (uint i = count; i > newLength; --i)
+ property->removeLast(property);
+
+ return true;
+ }
+
+ if (!property->append)
+ return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
+
+ for (uint i = count; i < newLength; ++i)
+ property->append(property, nullptr);
+
+ return true;
+
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index 201346786c..21240d6b98 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -52,6 +52,7 @@ struct Q_QML_EXPORT QmlListWrapper : Object
V4_OBJECT2(QmlListWrapper, Object)
V4_NEEDS_DESTROY
V4_PROTOTYPE(propertyListPrototype)
+ Q_MANAGED_TYPE(QmlListProperty)
static ReturnedValue create(ExecutionEngine *engine, QObject *object, int propId, QMetaType propType);
static ReturnedValue create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType);
@@ -66,9 +67,20 @@ struct Q_QML_EXPORT QmlListWrapper : Object
struct PropertyListPrototype : Object
{
- void init(ExecutionEngine *engine);
+ V4_PROTOTYPE(arrayPrototype)
+ void init();
+
+ static ReturnedValue method_pop(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_push(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_shift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_unshift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}