diff options
Diffstat (limited to 'src/qml/qml/qqmllistwrapper.cpp')
-rw-r--r-- | src/qml/qml/qqmllistwrapper.cpp | 749 |
1 files changed, 621 insertions, 128 deletions
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index e5c1cb04a9..8d5a585b62 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -1,105 +1,174 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// 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 <QtQml/qqmlinfo.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 using namespace QV4; +using namespace Qt::StringLiterals; DEFINE_OBJECT_VTABLE(QmlListWrapper); -void Heap::QmlListWrapper::init() +static void setArrayData(Heap::QmlListWrapper *d) +{ + QV4::Scope scope(d->internalClass->engine); + QV4::ScopedObject o(scope, d); + o->arrayCreate(); +} + +struct ListWrapperObject +{ + QV4::Scope scope; + QV4::ScopedObject object; + + ListWrapperObject(QQmlListProperty<QObject> *p) + : scope(static_cast<Heap::QmlListWrapper *>(p->data)->internalClass->engine) + , object(scope, static_cast<Heap::QmlListWrapper *>(p->data)) + { + Q_ASSERT(object); + Q_ASSERT(object->arrayData()); + } + + Heap::ArrayData *arrayData() const + { + return object->arrayData(); + } +}; + +static void appendWrapped(QQmlListProperty<QObject> *p, QObject *o) +{ + ListWrapperObject object(p); + Heap::ArrayData *arrayData = object.arrayData(); + + const uint length = arrayData->length(); + if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) { + object.scope.engine->throwRangeError(QLatin1String("Too many elements.")); + return; + } + + ArrayData::realloc(object.object, Heap::ArrayData::Simple, length + 1, false); + arrayData->vtable()->put( + object.object, length, QV4::QObjectWrapper::wrap(object.scope.engine, o)); +} + +static qsizetype countWrapped(QQmlListProperty<QObject> *p) +{ + ListWrapperObject object(p); + return object.arrayData()->length(); +} + +static QObject *atWrapped(QQmlListProperty<QObject> *p, qsizetype i) +{ + ListWrapperObject object(p); + QV4::Scoped<QObjectWrapper> result(object.scope, object.arrayData()->get(i)); + return result ? result->object() : nullptr; +} + +static void clearWrapped(QQmlListProperty<QObject> *p) +{ + ListWrapperObject object(p); + object.arrayData()->vtable()->truncate(object.object, 0); +} + +static void replaceWrapped(QQmlListProperty<QObject> *p, qsizetype i, QObject *o) +{ + ListWrapperObject object(p); + object.arrayData()->vtable()->put( + object.object, i, QV4::QObjectWrapper::wrap(object.scope.engine, o)); +} + +static void removeLastWrapped(QQmlListProperty<QObject> *p) +{ + ListWrapperObject object(p); + Heap::ArrayData *arrayData = object.arrayData(); + const uint length = arrayData->length(); + if (length > 0) + arrayData->vtable()->truncate(object.object, length - 1); +} + +void Heap::QmlListWrapper::init(QMetaType propertyType) +{ + Object::init(); + m_object.init(); + m_propertyType = propertyType.iface(); + setArrayData(this); + *property() = QQmlListProperty<QObject>( + nullptr, this, + appendWrapped, countWrapped, atWrapped, + clearWrapped, replaceWrapped, removeLastWrapped); +} + +void Heap::QmlListWrapper::init(QObject *object, int propertyId, QMetaType propertyType) +{ + Object::init(); + m_object.init(object); + m_propertyType = propertyType.iface(); + void *args[] = { property(), nullptr }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyId, args); +} + +void Heap::QmlListWrapper::init( + QObject *object, const QQmlListProperty<QObject> &list, QMetaType propertyType) { Object::init(); - object.init(); - QV4::Scope scope(internalClass->engine); - QV4::ScopedObject o(scope, this); - o->setArrayType(Heap::ArrayData::Custom); + m_object.init(object); + m_propertyType = propertyType.iface(); + *property() = list; } void Heap::QmlListWrapper::destroy() { - object.destroy(); + m_object.destroy(); Object::destroy(); } -ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, int propId, int propType) +ReturnedValue QmlListWrapper::create( + ExecutionEngine *engine, QObject *object, int propId, QMetaType propType) { if (!object || propId == -1) return Encode::null(); - - Scope scope(engine); - - Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>()); - r->d()->object = object; - r->d()->propertyType = propType; - void *args[] = { &r->d()->property(), nullptr }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args); - return r.asReturnedValue(); + return engine->memoryManager->allocate<QmlListWrapper>(object, propId, propType) + ->asReturnedValue(); } -ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, int propType) +ReturnedValue QmlListWrapper::create( + ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType) { - Scope scope(engine); + return engine->memoryManager->allocate<QmlListWrapper>(prop.object, prop, propType) + ->asReturnedValue(); +} - Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>()); - r->d()->object = prop.object; - r->d()->property() = prop; - r->d()->propertyType = propType; - return r.asReturnedValue(); +ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QMetaType propType) +{ + return engine->memoryManager->allocate<QmlListWrapper>(propType)->asReturnedValue(); } QVariant QmlListWrapper::toVariant() const { - if (!d()->object) + if (!d()->object()) return QVariant(); - return QVariant::fromValue(QQmlListReferencePrivate::init(d()->property(), d()->propertyType, engine()->qmlEngine())); + return QVariant::fromValue(toListReference()); } +QQmlListReference QmlListWrapper::toListReference() const +{ + const Heap::QmlListWrapper *wrapper = d(); + return QQmlListReferencePrivate::init(*wrapper->property(), wrapper->propertyType()); +} ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { @@ -108,12 +177,14 @@ ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const QV4::ExecutionEngine *v4 = w->engine(); if (id.isArrayIndex()) { - uint index = id.asArrayIndex(); - quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0; - if (index < count && w->d()->property().at) { + const uint index = id.asArrayIndex(); + const quint32 count = w->d()->property()->count + ? w->d()->property()->count(w->d()->property()) + : 0; + if (index < count && w->d()->property()->at) { if (hasProperty) *hasProperty = true; - return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index)); + return QV4::QObjectWrapper::wrap(v4, w->d()->property()->at(w->d()->property(), index)); } if (hasProperty) @@ -121,16 +192,17 @@ 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); } +qint64 QmlListWrapper::virtualGetLength(const Managed *m) +{ + Q_ASSERT(m->as<QmlListWrapper>()); + QQmlListProperty<QObject> *property = static_cast<const QmlListWrapper *>(m)->d()->property(); + Q_ASSERT(property); + return property->count ? property->count(property) : 0; +} + bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { Q_ASSERT(m->as<QmlListWrapper>()); @@ -138,7 +210,7 @@ bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, const auto *w = static_cast<const QmlListWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); - QQmlListProperty<QObject> *prop = &(w->d()->property()); + QQmlListProperty<QObject> *prop = w->d()->property(); if (id.isArrayIndex()) { if (!prop->count || !prop->replace) @@ -149,6 +221,11 @@ bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, if (count < 0 || index >= uint(count)) return false; + if (value.isNull()) { + prop->replace(prop, index, nullptr); + return true; + } + QV4::Scope scope(v4); QV4::ScopedObject so(scope, value.toObject(scope.engine)); if (auto *wrapper = so->as<QV4::QObjectWrapper>()) { @@ -159,43 +236,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); } @@ -210,18 +250,24 @@ PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property { const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o); - quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0; + quint32 count = w->d()->property()->count ? w->d()->property()->count(w->d()->property()) : 0; if (arrayIndex < count) { uint index = arrayIndex; ++arrayIndex; if (attrs) *attrs = QV4::Attr_Data; - if (pd) - pd->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), index)); + 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) @@ -230,9 +276,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) @@ -244,17 +329,425 @@ ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const QmlListWrapper *w = instance->as<QmlListWrapper>(); if (!w) RETURN_UNDEFINED(); - if (!w->d()->property().append) - THROW_GENERIC_ERROR("List doesn't define an Append function"); - QV4::ScopedObject so(scope); - for (int i = 0, ei = argc; i < ei; ++i) - { - so = argv[i].toObject(scope.engine); - if (QV4::QObjectWrapper *wrapper = so->as<QV4::QObjectWrapper>()) - w->d()->property().append(&w->d()->property(), wrapper->object() ); + 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(); + } + + const qsizetype length = property->count(property); + 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) { + if (argv[i].isNull()) + property->append(property, nullptr); + else + property->append(property, argv[i].as<QV4::QObjectWrapper>()->object()); + } + + const auto actualLength = property->count(property); + if (actualLength != length + argc) + qmlWarning(property->object) << "List didn't append all objects"; + + return Encode(uint(actualLength)); +} + +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 (!qIsAtMostUintLimit(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 || !qIsAtMostUintLimit(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); + if (property->count(property) != argc + len) + return scope.engine->throwTypeError(u"List doesn't append null objects"_s); + + 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) { + const auto *wrapper = argv[i].as<QObjectWrapper>(); + property->replace(property, i, wrapper ? wrapper->object() : nullptr); + } + + 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(); } - return Encode::undefined(); + + 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 (qIsAtMostUintLimit(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 (qIsAtMostUintLimit(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 (qIsAtMostUintLimit(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; + } + + if (!property->count) + return scope.engine->throwTypeError(u"List doesn't define a Count function"_s); + + qsizetype count = property->count(property); + if (!qIsAtMostUintLimit(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); + + count = property->count(property); + if (!qIsAtMostUintLimit(count)) + return scope.engine->throwRangeError(QString::fromLatin1("List length out of range.")); + + if (uint(count) != newLength) + return scope.engine->throwTypeError(u"List doesn't append null objects"_s); + + return true; + } QT_END_NAMESPACE |