diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/jsruntime/qv4arraydata.cpp | 73 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4arraydata_p.h | 68 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4arrayiterator.cpp | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4arrayobject.cpp | 39 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4managed.cpp | 3 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4managed_p.h | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4scopedvalue_p.h | 7 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject.cpp | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmllist.h | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmllist_p.h | 169 | ||||
-rw-r--r-- | src/qml/qml/qqmllistwrapper.cpp | 516 | ||||
-rw-r--r-- | src/qml/qml/qqmllistwrapper_p.h | 14 |
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); }; } |