diff options
Diffstat (limited to 'src/qml/jsruntime/qv4sequenceobject.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject.cpp | 1202 |
1 files changed, 554 insertions, 648 deletions
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 77a98247ac..cc899428c2 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -1,43 +1,7 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#include <QtQml/qqml.h> +// 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 <QtCore/qsequentialiterable.h> #include "qv4sequenceobject_p.h" @@ -46,19 +10,56 @@ #include <private/qqmlengine_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4jscall_p.h> -#include "qv4runtime_p.h" -#include "qv4objectiterator_p.h" +#include <private/qqmlmetatype_p.h> +#include <private/qqmltype_p_p.h> #include <private/qqmlvaluetypewrapper_p.h> -#if QT_CONFIG(qml_itemmodel) -#include <private/qqmlmodelindexvaluetype_p.h> -#include <QtCore/qabstractitemmodel.h> -#endif #include <algorithm> QT_BEGIN_NAMESPACE -using namespace QV4; +namespace QV4 { + +DEFINE_OBJECT_VTABLE(Sequence); + +static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) { + QV4::Scope scope(s->engine()); + + Heap::ReferenceObject::Flags flags = + Heap::ReferenceObject::EnforcesLocation; + if (s->d()->metaSequence().canSetValueAtIndex()) + flags |= Heap::ReferenceObject::CanWriteBack; + if (s->d()->valueMetaType() == QMetaType::fromType<QVariant>()) + flags |= Heap::ReferenceObject::IsVariant; + + QV4::ScopedValue v(scope, scope.engine->fromVariant( + s->at(index), s->d(), index, flags)); + if (QQmlValueTypeWrapper *ref = v->as<QQmlValueTypeWrapper>()) { + if (CppStackFrame *frame = scope.engine->currentStackFrame) + ref->d()->setLocation(frame->v4Function, frame->statementNumber()); + // No need to read the reference. at() has done that already. + } + return v->asReturnedValue(); +} + +template<typename Compare> +void sortSequence(Sequence *sequence, const Compare &compare) +{ + /* non-const */ Heap::Sequence *p = sequence->d(); + + QSequentialIterable iterable(p->metaSequence(), p->listType(), p->storagePointer()); + if (iterable.canRandomAccessIterate()) { + std::sort(QSequentialIterable::RandomAccessIterator(iterable.mutableBegin()), + QSequentialIterable::RandomAccessIterator(iterable.mutableEnd()), + compare); + } else if (iterable.canReverseIterate()) { + std::sort(QSequentialIterable::BidirectionalIterator(iterable.mutableBegin()), + QSequentialIterable::BidirectionalIterator(iterable.mutableEnd()), + compare); + } else { + qWarning() << "Container has no suitable iterator for sorting"; + } +} // helper function to generate valid warnings if errors occur during sequence operations. static void generateWarning(QV4::ExecutionEngine *v4, const QString& description) @@ -76,637 +77,532 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description QQmlEnginePrivate::warning(engine, retn); } -// F(elementType, elementTypeName, sequenceType, defaultValue) -#if QT_CONFIG(qml_itemmodel) -#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) \ - F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ - F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ - F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ - F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) -#else -#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) -#endif +struct SequenceOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator +{ + ~SequenceOwnPropertyKeyIterator() override = default; + PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override + { + const Sequence *s = static_cast<const Sequence *>(o); + + if (s->d()->isReference() && !s->loadReference()) + return PropertyKey::invalid(); + + const qsizetype size = s->size(); + if (size > 0 && qIsAtMostSizetypeLimit(arrayIndex, size - 1)) { + const uint index = arrayIndex; + ++arrayIndex; + if (attrs) + *attrs = QV4::Attr_Data; + if (pd) + pd->value = doGetIndexed(s, index); + return PropertyKey::fromArrayIndex(index); + } + + if (memberIndex == 0) { + ++memberIndex; + return o->engine()->id_length()->propertyKey(); + } -#define FOREACH_QML_SEQUENCE_TYPE(F) \ - F(int, IntVector, QVector<int>, 0) \ - F(qreal, RealVector, QVector<qreal>, 0.0) \ - F(bool, BoolVector, QVector<bool>, false) \ - F(int, IntStdVector, std::vector<int>, 0) \ - F(qreal, RealStdVector, std::vector<qreal>, 0.0) \ - F(bool, BoolStdVector, std::vector<bool>, false) \ - F(int, Int, QList<int>, 0) \ - F(qreal, Real, QList<qreal>, 0.0) \ - F(bool, Bool, QList<bool>, false) \ - F(QString, String, QList<QString>, QString()) \ - F(QString, QString, QStringList, QString()) \ - F(QString, StringVector, QVector<QString>, QString()) \ - F(QString, StringStdVector, std::vector<QString>, QString()) \ - F(QUrl, Url, QList<QUrl>, QUrl()) \ - F(QUrl, UrlVector, QVector<QUrl>, QUrl()) \ - F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \ - FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) + // You cannot add any own properties via the regular JavaScript interfaces. + return PropertyKey::invalid(); + } +}; -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) +struct SequenceCompareFunctor { - return engine->newString(element)->asReturnedValue(); -} + SequenceCompareFunctor(QV4::ExecutionEngine *v4, const QV4::Value &compareFn) + : m_v4(v4), m_compareFn(&compareFn) + {} -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, int element) -{ - return QV4::Encode(element); -} + bool operator()(const QVariant &lhs, const QVariant &rhs) + { + QV4::Scope scope(m_v4); + ScopedFunctionObject compare(scope, m_compareFn); + if (!compare) + return m_v4->throwTypeError(); + Value *argv = scope.alloc(2); + argv[0] = m_v4->fromVariant(lhs); + argv[1] = m_v4->fromVariant(rhs); + QV4::ScopedValue result(scope, compare->call(m_v4->globalObject, argv, 2)); + if (scope.hasException()) + return false; + return result->toNumber() < 0; + } -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QUrl &element) -{ - return engine->newString(element.toString())->asReturnedValue(); -} +private: + QV4::ExecutionEngine *m_v4; + const QV4::Value *m_compareFn; +}; -#if QT_CONFIG(qml_itemmodel) -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QModelIndex &element) +struct SequenceDefaultCompareFunctor { - const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(QMetaType::QModelIndex); - return QV4::QQmlValueTypeWrapper::create(engine, QVariant(element), vtmo, QMetaType::QModelIndex); -} + bool operator()(const QVariant &lhs, const QVariant &rhs) + { + return lhs.toString() < rhs.toString(); + } +}; -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QItemSelectionRange &element) +void Heap::Sequence::initTypes(QMetaType listType, QMetaSequence metaSequence) { - int metaTypeId = qMetaTypeId<QItemSelectionRange>(); - const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(metaTypeId); - return QV4::QQmlValueTypeWrapper::create(engine, QVariant::fromValue(element), vtmo, metaTypeId); + m_listType = listType.iface(); + Q_ASSERT(m_listType); + m_metaSequence = metaSequence.iface(); + Q_ASSERT(m_metaSequence); + QV4::Scope scope(internalClass->engine); + QV4::Scoped<QV4::Sequence> o(scope, this); + o->setArrayType(Heap::ArrayData::Custom); } -#endif -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, qreal element) +void Heap::Sequence::init(QMetaType listType, QMetaSequence metaSequence, const void *container) { - return QV4::Encode(element); + ReferenceObject::init(nullptr, -1, NoFlag); + initTypes(listType, metaSequence); + m_container = listType.create(container); } -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, bool element) +void Heap::Sequence::init( + QMetaType listType, QMetaSequence metaSequence, const void *container, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) { - return QV4::Encode(element); + ReferenceObject::init(object, propertyIndex, flags); + initTypes(listType, metaSequence); + + if (CppStackFrame *frame = internalClass->engine->currentStackFrame) + setLocation(frame->v4Function, frame->statementNumber()); + if (container) + m_container = listType.create(container); + else if (flags & EnforcesLocation) + QV4::ReferenceObject::readReference(this); } -static QString convertElementToString(const QString &element) +Heap::Sequence *Heap::Sequence::detached() const { - return element; + return internalClass->engine->memoryManager->allocate<QV4::Sequence>( + QMetaType(m_listType), QMetaSequence(m_metaSequence), m_container); } -static QString convertElementToString(int element) +void Heap::Sequence::destroy() { - return QString::number(element); + if (m_container) + listType().destroy(m_container); + ReferenceObject::destroy(); } -static QString convertElementToString(const QUrl &element) +void *Heap::Sequence::storagePointer() { - return element.toString(); + if (!m_container) + m_container = listType().create(); + return m_container; } -#if QT_CONFIG(qml_itemmodel) -static QString convertElementToString(const QModelIndex &element) +bool Heap::Sequence::setVariant(const QVariant &variant) { - return reinterpret_cast<const QQmlModelIndexValueType *>(&element)->toString(); + const QMetaType variantReferenceType = variant.metaType(); + if (variantReferenceType != listType()) { + // This is a stale reference. That is, the property has been + // overwritten with a different type in the meantime. + // We need to modify this reference to the updated type, if + // possible, or return false if it is not a sequence. + const QQmlType newType = QQmlMetaType::qmlListType(variantReferenceType); + if (newType.isSequentialContainer()) { + if (m_container) + listType().destroy(m_container); + m_listType = newType.qListTypeId().iface(); + m_metaSequence = newType.listMetaSequence().iface(); + m_container = listType().create(variant.constData()); + return true; + } else { + return false; + } + } + if (m_container) { + variantReferenceType.destruct(m_container); + variantReferenceType.construct(m_container, variant.constData()); + } else { + m_container = variantReferenceType.create(variant.constData()); + } + return true; } - -static QString convertElementToString(const QItemSelectionRange &element) +QVariant Heap::Sequence::toVariant() const { - return reinterpret_cast<const QQmlItemSelectionRangeValueType *>(&element)->toString(); + return QVariant(listType(), m_container); } -#endif -static QString convertElementToString(qreal element) +qsizetype Sequence::size() const { - QString qstr; - RuntimeHelpers::numberToString(&qstr, element, 10); - return qstr; + const auto *p = d(); + Q_ASSERT(p->storagePointer()); // Must readReference() before + return p->metaSequence().size(p->storagePointer()); } -static QString convertElementToString(bool element) +QVariant Sequence::at(qsizetype index) const { - if (element) - return QStringLiteral("true"); - else - return QStringLiteral("false"); + const auto *p = d(); + Q_ASSERT(p->storagePointer()); // Must readReference() before + const QMetaType v = p->valueMetaType(); + QVariant result; + if (v == QMetaType::fromType<QVariant>()) { + p->metaSequence().valueAtIndex(p->storagePointer(), index, &result); + } else { + result = QVariant(v); + p->metaSequence().valueAtIndex(p->storagePointer(), index, result.data()); + } + return result; } -template <typename ElementType> ElementType convertValueToElement(const Value &value); -template <> QString convertValueToElement(const Value &value) +template<typename Action> +void convertAndDo(const QVariant &item, const QMetaType v, Action action) { - return value.toQString(); + if (item.metaType() == v) { + action(item.constData()); + } else if (v == QMetaType::fromType<QVariant>()) { + action(&item); + } else { + QVariant converted = item; + if (!converted.convert(v)) + converted = QVariant(v); + action(converted.constData()); + } } -template <> int convertValueToElement(const Value &value) +void Sequence::append(const QVariant &item) { - return value.toInt32(); + Heap::Sequence *p = d(); + convertAndDo(item, p->valueMetaType(), [p](const void *data) { + p->metaSequence().addValueAtEnd(p->storagePointer(), data); + }); } -template <> QUrl convertValueToElement(const Value &value) +void Sequence::append(qsizetype num, const QVariant &item) { - return QUrl(value.toQString()); + Heap::Sequence *p = d(); + convertAndDo(item, p->valueMetaType(), [p, num](const void *data) { + const QMetaSequence m = p->metaSequence(); + void *container = p->storagePointer(); + for (qsizetype i = 0; i < num; ++i) + m.addValueAtEnd(container, data); + }); } -#if QT_CONFIG(qml_itemmodel) -template <> QModelIndex convertValueToElement(const Value &value) +void Sequence::replace(qsizetype index, const QVariant &item) { - const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); - if (v) - return v->toVariant().toModelIndex(); - return QModelIndex(); + Heap::Sequence *p = d(); + convertAndDo(item, p->valueMetaType(), [p, index](const void *data) { + p->metaSequence().setValueAtIndex(p->storagePointer(), index, data); + }); } -template <> QItemSelectionRange convertValueToElement(const Value &value) +void Sequence::removeLast(qsizetype num) { - const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); - if (v) - return v->toVariant().value<QItemSelectionRange>(); - return QItemSelectionRange(); -} -#endif + auto *p = d(); + const QMetaSequence m = p->metaSequence(); -template <> qreal convertValueToElement(const Value &value) -{ - return value.toNumber(); + if (m.canEraseRangeAtIterator() && m.hasRandomAccessIterator() && num > 1) { + void *i = m.end(p->storagePointer()); + m.advanceIterator(i, -num); + void *j = m.end(p->storagePointer()); + m.eraseRangeAtIterator(p->storagePointer(), i, j); + m.destroyIterator(i); + m.destroyIterator(j); + } else { + for (int i = 0; i < num; ++i) + m.removeValueAtEnd(p->storagePointer()); + } } -template <> bool convertValueToElement(const Value &value) +ReturnedValue Sequence::containerGetIndexed(qsizetype index, bool *hasProperty) const { - return value.toBoolean(); -} - -namespace QV4 { - -template <typename Container> struct QQmlSequence; - -namespace Heap { + if (d()->isReference() && !loadReference()) + return Encode::undefined(); -template <typename Container> -struct QQmlSequence : Object { - void init(const Container &container); - void init(QObject *object, int propertyIndex, bool readOnly); - void destroy() { - delete container; - object.destroy(); - Object::destroy(); + if (index >= 0 && index < size()) { + if (hasProperty) + *hasProperty = true; + return doGetIndexed(this, index); } - - mutable Container *container; - QQmlQPointer<QObject> object; - int propertyIndex; - bool isReference : 1; - bool isReadOnly : 1; -}; - + if (hasProperty) + *hasProperty = false; + return Encode::undefined(); } -template <typename Container> -struct QQmlSequence : public QV4::Object +bool Sequence::containerPutIndexed(qsizetype index, const Value &value) { - V4_OBJECT2(QQmlSequence<Container>, QV4::Object) - Q_MANAGED_TYPE(QmlSequence) - V4_PROTOTYPE(sequencePrototype) - V4_NEEDS_DESTROY -public: + if (internalClass()->engine->hasException) + return false; - void init() - { - defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length); + if (d()->isReadOnly()) { + engine()->throwTypeError(QLatin1String("Cannot insert into a readonly container")); + return false; } - QV4::ReturnedValue containerGetIndexed(uint index, bool *hasProperty) const - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine(), QLatin1String("Index out of range during indexed get")); - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - if (d()->isReference) { - if (!d()->object) { - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - loadReference(); - } - if (index < size_t(d()->container->size())) { - if (hasProperty) - *hasProperty = true; - return convertElementToValue(engine(), qAsConst(*(d()->container))[index]); - } - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } + if (d()->isReference() && !loadReference()) + return false; - bool containerPutIndexed(uint index, const QV4::Value &value) - { - if (internalClass()->engine->hasException) - return false; + const qsizetype count = size(); + const QMetaType valueType = d()->valueMetaType(); + const QVariant element = ExecutionEngine::toVariant(value, valueType, false); - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine(), QLatin1String("Index out of range during indexed set")); - return false; - } + if (index < 0) + return false; - if (d()->isReadOnly) - return false; + if (index == count) { + append(element); + } else if (index < count) { + replace(index, element); + } else { + /* according to ECMA262r3 we need to insert */ + /* the value at the given index, increasing length to index+1. */ + append(index - count, + valueType == QMetaType::fromType<QVariant>() ? QVariant() : QVariant(valueType)); + append(element); + } - if (d()->isReference) { - if (!d()->object) - return false; - loadReference(); - } + if (d()->object()) + storeReference(); + return true; +} - size_t count = size_t(d()->container->size()); +SequenceOwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value *target) +{ + *target = *m; + return new SequenceOwnPropertyKeyIterator; +} - typename Container::value_type element = convertValueToElement<typename Container::value_type>(value); +bool Sequence::containerDeleteIndexedProperty(qsizetype index) +{ + if (d()->isReadOnly()) + return false; + if (d()->isReference() && !loadReference()) + return false; + if (index < 0 || index >= size()) + return false; - if (index == count) { - d()->container->push_back(element); - } else if (index < count) { - (*d()->container)[index] = element; - } else { - /* according to ECMA262r3 we need to insert */ - /* the value at the given index, increasing length to index+1. */ - d()->container->reserve(index + 1); - while (index > count++) { - d()->container->push_back(typename Container::value_type()); - } - d()->container->push_back(element); - } + /* according to ECMA262r3 it should be Undefined, */ + /* but we cannot, so we insert a default-value instead. */ + replace(index, QVariant()); - if (d()->isReference) - storeReference(); - return true; - } + if (d()->object()) + storeReference(); - QV4::PropertyAttributes containerQueryIndexed(uint index) const - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine(), QLatin1String("Index out of range during indexed query")); - return QV4::Attr_Invalid; - } - if (d()->isReference) { - if (!d()->object) - return QV4::Attr_Invalid; - loadReference(); - } - return (index < size_t(d()->container->size())) ? QV4::Attr_Data : QV4::Attr_Invalid; - } + return true; +} - struct OwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator - { - ~OwnPropertyKeyIterator() override = default; - PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override - { - const QQmlSequence *s = static_cast<const QQmlSequence *>(o); - - if (s->d()->isReference) { - if (!s->d()->object) - return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); - s->loadReference(); - } +bool Sequence::containerIsEqualTo(Managed *other) +{ + if (!other) + return false; + Sequence *otherSequence = other->as<Sequence>(); + if (!otherSequence) + return false; + if (d()->object() && otherSequence->d()->object()) { + return d()->object() == otherSequence->d()->object() + && d()->property() == otherSequence->d()->property(); + } else if (!d()->object() && !otherSequence->d()->object()) { + return this == otherSequence; + } + return false; +} - if (arrayIndex < static_cast<uint>(s->d()->container->size())) { - uint index = arrayIndex; - ++arrayIndex; - if (attrs) - *attrs = QV4::Attr_Data; - if (pd) - pd->value = convertElementToValue(s->engine(), s->d()->container->at(index)); - return PropertyKey::fromArrayIndex(index); - } +bool Sequence::sort(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + if (d()->isReadOnly()) + return false; + if (d()->isReference() && !loadReference()) + return false; - return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); - } - }; + if (argc == 1 && argv[0].as<FunctionObject>()) + sortSequence(this, SequenceCompareFunctor(f->engine(), argv[0])); + else + sortSequence(this, SequenceDefaultCompareFunctor()); - static OwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value *target) - { - *target = *m; - return new OwnPropertyKeyIterator; - } + if (d()->object()) + storeReference(); - bool containerDeleteIndexedProperty(uint index) - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) - return false; - if (d()->isReadOnly) - return false; - if (d()->isReference) { - if (!d()->object) - return false; - loadReference(); - } + return true; +} - if (index >= size_t(d()->container->size())) - return false; +void *Sequence::getRawContainerPtr() const +{ return d()->storagePointer(); } - /* according to ECMA262r3 it should be Undefined, */ - /* but we cannot, so we insert a default-value instead. */ - (*d()->container)[index] = typename Container::value_type(); +bool Sequence::loadReference() const +{ + Q_ASSERT(d()->object()); + // If locations are enforced we only read once + return d()->enforcesLocation() || QV4::ReferenceObject::readReference(d()); +} - if (d()->isReference) - storeReference(); +bool Sequence::storeReference() +{ + Q_ASSERT(d()->object()); + return d()->isAttachedToProperty() && QV4::ReferenceObject::writeBack(d()); +} - return true; - } +ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty) +{ + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + if (qIsAtMostSizetypeLimit(index)) + return static_cast<const Sequence *>(that)->containerGetIndexed(qsizetype(index), hasProperty); - bool containerIsEqualTo(Managed *other) - { - if (!other) - return false; - QQmlSequence<Container> *otherSequence = other->as<QQmlSequence<Container> >(); - if (!otherSequence) - return false; - if (d()->isReference && otherSequence->d()->isReference) { - return d()->object == otherSequence->d()->object && d()->propertyIndex == otherSequence->d()->propertyIndex; - } else if (!d()->isReference && !otherSequence->d()->isReference) { - return this == otherSequence; - } + generateWarning(that->engine(), QLatin1String("Index out of range during indexed get")); return false; } - struct DefaultCompareFunctor - { - bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) - { - return convertElementToString(lhs) < convertElementToString(rhs); - } - }; - - struct CompareFunctor - { - CompareFunctor(QV4::ExecutionEngine *v4, const QV4::Value &compareFn) - : m_v4(v4), m_compareFn(&compareFn) - {} - - bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) - { - QV4::Scope scope(m_v4); - ScopedFunctionObject compare(scope, m_compareFn); - if (!compare) - return m_v4->throwTypeError(); - Value *argv = scope.alloc(2); - argv[0] = convertElementToValue(m_v4, lhs); - argv[1] = convertElementToValue(m_v4, rhs); - QV4::ScopedValue result(scope, compare->call(m_v4->globalObject, argv, 2)); - return result->toNumber() < 0; - } - - private: - QV4::ExecutionEngine *m_v4; - const QV4::Value *m_compareFn; - }; - - bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc) - { - if (d()->isReadOnly) - return false; - if (d()->isReference) { - if (!d()->object) - return false; - loadReference(); - } + return Object::virtualGet(that, id, receiver, hasProperty); +} - if (argc == 1 && argv[0].as<FunctionObject>()) { - CompareFunctor cf(f->engine(), argv[0]); - std::sort(d()->container->begin(), d()->container->end(), cf); - } else { - DefaultCompareFunctor cf; - std::sort(d()->container->begin(), d()->container->end(), cf); - } +qint64 Sequence::virtualGetLength(const Managed *m) +{ + const Sequence *s = static_cast<const Sequence *>(m); + if (s->d()->isReference() && !s->loadReference()) + return 0; + return s->size(); +} - if (d()->isReference) - storeReference(); +bool Sequence::virtualPut(Managed *that, PropertyKey id, const Value &value, Value *receiver) +{ + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + if (qIsAtMostSizetypeLimit(index)) + return static_cast<Sequence *>(that)->containerPutIndexed(qsizetype(index), value); - return true; + generateWarning(that->engine(), QLatin1String("Index out of range during indexed set")); + return false; } + return Object::virtualPut(that, id, value, receiver); +} - static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) - { - QV4::Scope scope(b); - QV4::Scoped<QQmlSequence<Container>> This(scope, thisObject->as<QQmlSequence<Container> >()); - if (!This) - THROW_TYPE_ERROR(); +bool Sequence::virtualDeleteProperty(Managed *that, PropertyKey id) +{ + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + if (qIsAtMostSizetypeLimit(index)) + return static_cast<Sequence *>(that)->containerDeleteIndexedProperty(qsizetype(index)); - if (This->d()->isReference) { - if (!This->d()->object) - RETURN_RESULT(Encode(0)); - This->loadReference(); - } - RETURN_RESULT(Encode(qint32(This->d()->container->size()))); + generateWarning(that->engine(), QLatin1String("Index out of range during indexed delete")); + return false; } + return Object::virtualDeleteProperty(that, id); +} - static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) - { - QV4::Scope scope(f); - QV4::Scoped<QQmlSequence<Container>> This(scope, thisObject->as<QQmlSequence<Container> >()); - if (!This) - THROW_TYPE_ERROR(); +bool Sequence::virtualIsEqualTo(Managed *that, Managed *other) +{ + return static_cast<Sequence *>(that)->containerIsEqualTo(other); +} - quint32 newLength = argc ? argv[0].toUInt32() : 0; - /* Qt containers have int (rather than uint) allowable indexes. */ - if (newLength > INT_MAX) { - generateWarning(scope.engine, QLatin1String("Index out of range during length set")); - RETURN_UNDEFINED(); - } +OwnPropertyKeyIterator *Sequence::virtualOwnPropertyKeys(const Object *m, Value *target) +{ + return containerOwnPropertyKeys(m, target); +} - if (This->d()->isReadOnly) - THROW_TYPE_ERROR(); +int Sequence::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a) +{ + Sequence *sequence = static_cast<Sequence *>(object); + Q_ASSERT(sequence); - /* Read the sequence from the QObject property if we're a reference */ - if (This->d()->isReference) { - if (!This->d()->object) - RETURN_UNDEFINED(); - This->loadReference(); - } - /* Determine whether we need to modify the sequence */ - quint32 newCount = static_cast<quint32>(newLength); - quint32 count = static_cast<quint32>(This->d()->container->size()); - if (newCount == count) { - RETURN_UNDEFINED(); - } else if (newCount > count) { - /* according to ECMA262r3 we need to insert */ - /* undefined values increasing length to newLength. */ - /* We cannot, so we insert default-values instead. */ - This->d()->container->reserve(newCount); - while (newCount > count++) { - This->d()->container->push_back(typename Container::value_type()); - } - } else { - /* according to ECMA262r3 we need to remove */ - /* elements until the sequence is the required length. */ - if (newCount < count) { - This->d()->container->erase(This->d()->container->begin() + newCount, This->d()->container->end()); - } - } - /* write back if required. */ - if (This->d()->isReference) { - /* write back. already checked that object is non-null, so skip that check here. */ - This->storeReference(); - } - RETURN_UNDEFINED(); + switch (call) { + case QMetaObject::ReadProperty: { + const QMetaType valueType = sequence->d()->valueMetaType(); + if (sequence->d()->isReference() && !sequence->loadReference()) + return 0; + const QMetaSequence metaSequence = sequence->d()->metaSequence(); + if (metaSequence.valueMetaType() != valueType) + return 0; // value metatype is not what the caller expects anymore. + + const void *storagePointer = sequence->d()->storagePointer(); + if (index < 0 || index >= metaSequence.size(storagePointer)) + return 0; + metaSequence.valueAtIndex(storagePointer, index, a[0]); + break; + } + case QMetaObject::WriteProperty: { + void *storagePointer = sequence->d()->storagePointer(); + const QMetaSequence metaSequence = sequence->d()->metaSequence(); + if (index < 0 || index >= metaSequence.size(storagePointer)) + return 0; + metaSequence.setValueAtIndex(storagePointer, index, a[0]); + if (sequence->d()->isReference()) + sequence->storeReference(); + break; + } + default: + return 0; // not supported } - QVariant toVariant() const - { return QVariant::fromValue<Container>(*d()->container); } + return -1; +} - static QVariant toVariant(QV4::ArrayObject *array) - { - QV4::Scope scope(array->engine()); - Container result; - quint32 length = array->getLength(); - QV4::ScopedValue v(scope); - for (quint32 i = 0; i < length; ++i) - result.push_back(convertValueToElement<typename Container::value_type>((v = array->get(i)))); - return QVariant::fromValue(result); - } +static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + QV4::Scope scope(b); + QV4::Scoped<Sequence> This(scope, thisObject->as<Sequence>()); + if (!This) + THROW_TYPE_ERROR(); - void* getRawContainerPtr() const - { return d()->container; } + if (This->d()->isReference() && !This->loadReference()) + return Encode::undefined(); - void loadReference() const - { - Q_ASSERT(d()->object); - Q_ASSERT(d()->isReference); - void *a[] = { d()->container, nullptr }; - QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->propertyIndex, a); - } + const qsizetype size = This->size(); + if (qIsAtMostUintLimit(size)) + RETURN_RESULT(Encode(uint(size))); - void storeReference() - { - Q_ASSERT(d()->object); - Q_ASSERT(d()->isReference); - int status = -1; - QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding; - void *a[] = { d()->container, nullptr, &status, &flags }; - QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); - } + return scope.engine->throwRangeError(QLatin1String("Sequence length out of range")); +} - static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty) - { - if (!id.isArrayIndex()) - return Object::virtualGet(that, id, receiver, hasProperty); - return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty); - } - static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver) - { - if (id.isArrayIndex()) - return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(id.asArrayIndex(), value); - return Object::virtualPut(that, id, value, receiver); - } - static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) - { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); } - static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id) - { - if (id.isArrayIndex()) { - uint index = id.asArrayIndex(); - return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); - } - return Object::virtualDeleteProperty(that, id); +static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + QV4::Scope scope(f); + QV4::Scoped<Sequence> This(scope, thisObject->as<Sequence>()); + if (!This) + THROW_TYPE_ERROR(); + + bool ok = false; + const quint32 argv0 = argc ? argv[0].asArrayLength(&ok) : 0; + if (!ok || !qIsAtMostSizetypeLimit(argv0)) { + generateWarning(scope.engine, QLatin1String("Index out of range during length set")); + RETURN_UNDEFINED(); } - static bool virtualIsEqualTo(Managed *that, Managed *other) - { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } - static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target) - { return static_cast<const QQmlSequence<Container> *>(m)->containerOwnPropertyKeys(m, target);} -}; + if (This->d()->isReadOnly()) + THROW_TYPE_ERROR(); + const qsizetype newCount = qsizetype(argv0); -template <typename Container> -void Heap::QQmlSequence<Container>::init(const Container &container) -{ - Object::init(); - this->container = new Container(container); - propertyIndex = -1; - isReference = false; - isReadOnly = false; - object.init(); + /* Read the sequence from the QObject property if we're a reference */ + if (This->d()->isReference() && !This->loadReference()) + RETURN_UNDEFINED(); - QV4::Scope scope(internalClass->engine); - QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); - o->setArrayType(Heap::ArrayData::Custom); - o->init(); -} + /* Determine whether we need to modify the sequence */ + const qsizetype count = This->size(); + if (newCount == count) { + RETURN_UNDEFINED(); + } else if (newCount > count) { + const QMetaType valueMetaType = This->d()->valueMetaType(); + /* according to ECMA262r3 we need to insert */ + /* undefined values increasing length to newLength. */ + /* We cannot, so we insert default-values instead. */ + This->append(newCount - count, QVariant(valueMetaType)); + } else { + /* according to ECMA262r3 we need to remove */ + /* elements until the sequence is the required length. */ + Q_ASSERT(newCount < count); + This->removeLast(count - newCount); + } -template <typename Container> -void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex, bool readOnly) -{ - Object::init(); - this->container = new Container; - this->propertyIndex = propertyIndex; - isReference = true; - this->isReadOnly = readOnly; - this->object.init(object); - QV4::Scope scope(internalClass->engine); - QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); - o->setArrayType(Heap::ArrayData::Custom); - o->loadReference(); - o->init(); -} + /* write back if required. */ + if (This->d()->object()) + This->storeReference(); + RETURN_UNDEFINED(); } -namespace QV4 { - -typedef QQmlSequence<QVector<int> > QQmlIntVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntVectorList); -typedef QQmlSequence<QVector<qreal> > QQmlRealVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealVectorList); -typedef QQmlSequence<QVector<bool> > QQmlBoolVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolVectorList); -typedef QQmlSequence<std::vector<int> > QQmlIntStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntStdVectorList); -typedef QQmlSequence<std::vector<qreal> > QQmlRealStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealStdVectorList); -typedef QQmlSequence<std::vector<bool> > QQmlBoolStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolStdVectorList); -typedef QQmlSequence<QStringList> QQmlQStringList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQStringList); -typedef QQmlSequence<QList<QString> > QQmlStringList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringList); -typedef QQmlSequence<QVector<QString> > QQmlStringVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringVectorList); -typedef QQmlSequence<std::vector<QString> > QQmlStringStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringStdVectorList); -typedef QQmlSequence<QList<int> > QQmlIntList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntList); -typedef QQmlSequence<QList<QUrl> > QQmlUrlList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlList); -typedef QQmlSequence<QVector<QUrl> > QQmlUrlVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlVectorList); -typedef QQmlSequence<std::vector<QUrl> > QQmlUrlStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlStdVectorList); -#if QT_CONFIG(qml_itemmodel) -typedef QQmlSequence<QModelIndexList> QQmlQModelIndexList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexList); -typedef QQmlSequence<QVector<QModelIndex> > QQmlQModelIndexVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexVectorList); -typedef QQmlSequence<std::vector<QModelIndex> > QQmlQModelIndexStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexStdVectorList); -typedef QQmlSequence<QItemSelection> QQmlQItemSelectionRangeList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQItemSelectionRangeList); -#endif -typedef QQmlSequence<QList<bool> > QQmlBoolList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolList); -typedef QQmlSequence<QList<qreal> > QQmlRealList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealList); - -} - -#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType); void SequencePrototype::init() { - FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); + defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length); } -#undef REGISTER_QML_SEQUENCE_METATYPE ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int) { @@ -717,132 +613,142 @@ 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) return o.asReturnedValue(); -#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ - if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ - if (!s->sort(b, thisObject, argv, argc)) \ - THROW_TYPE_ERROR(); \ - } else - - FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) + if (auto *s = o->as<Sequence>()) { + if (!s->sort(b, thisObject, argv, argc)) + THROW_TYPE_ERROR(); + } -#undef CALL_SORT - {} return o.asReturnedValue(); } -#define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ - if (sequenceTypeId == qMetaTypeId<SequenceType>()) { \ - return true; \ - } else - -bool SequencePrototype::isSequenceType(int sequenceTypeId) -{ - FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE) { /* else */ return false; } -} -#undef IS_SEQUENCE - -#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ - if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(object, propertyIndex, readOnly)); \ - return obj.asReturnedValue(); \ - } else - -ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool readOnly, bool *succeeded) +ReturnedValue SequencePrototype::newSequence( + QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) { - QV4::Scope scope(engine); // This function is called when the property is a QObject Q_PROPERTY of - // the given sequence type. Internally we store a typed-sequence + // the given sequence type. Internally we store a sequence // (as well as object ptr + property index for updated-read and write-back) // and so access/mutate avoids variant conversion. - *succeeded = true; - FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return QV4::Encode::undefined(); } + + return engine->memoryManager->allocate<Sequence>( + type, metaSequence, data, object, propertyIndex, flags)->asReturnedValue(); } -#undef NEW_REFERENCE_SEQUENCE -#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ - if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(v.value<SequenceType >())); \ - return obj.asReturnedValue(); \ - } else +ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant &v) +{ + const QMetaType type = v.metaType(); + const QQmlType qmlType = QQmlMetaType::qmlListType(type); + if (qmlType.isSequentialContainer()) + return fromData(engine, type, qmlType.listMetaSequence(), v.constData()); + + QSequentialIterable iterable; + if (QMetaType::convert( + type, v.constData(), QMetaType::fromType<QSequentialIterable>(), &iterable)) { + return fromData(engine, type, iterable.metaContainer(), v.constData()); + } -ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded) + return Encode::undefined(); +} + +ReturnedValue SequencePrototype::fromData( + ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data) { - QV4::Scope scope(engine); // This function is called when assigning a sequence value to a normal JS var // in a JS block. Internally, we store a sequence of the specified type. // Access and mutation is extremely fast since it will not need to modify any // QObject property. - int sequenceType = v.userType(); - *succeeded = true; - FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return QV4::Encode::undefined(); } -} -#undef NEW_COPY_SEQUENCE -#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ - if (QQml##ElementTypeName##List *list = object->as<QQml##ElementTypeName##List>()) \ - return list->toVariant(); \ - else + return engine->memoryManager->allocate<Sequence>(type, metaSequence, data)->asReturnedValue(); +} -QVariant SequencePrototype::toVariant(Object *object) +QVariant SequencePrototype::toVariant(const Sequence *object) { - Q_ASSERT(object->isListType()); - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); } -} + Q_ASSERT(object->isV4SequenceType()); + const auto *p = object->d(); -#undef SEQUENCE_TO_VARIANT -#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ - if (typeHint == qMetaTypeId<SequenceType>()) { \ - return QQml##ElementTypeName##List::toVariant(a); \ - } else + // Note: For historical reasons, we ignore the result of loadReference() + // here. This allows us to retain sequences whose objects have vaninshed + // as "var" properties. It comes at the price of potentially returning + // outdated data. This is the behavior sequences have always shown. + if (p->isReference()) + object->loadReference(); + if (!p->hasData()) + return QVariant(); -QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, bool *succeeded) -{ - *succeeded = true; + return QVariant(p->listType(), p->storagePointer()); +} - if (!array.as<ArrayObject>()) { - *succeeded = false; +QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHint) +{ + if (!array.as<ArrayObject>()) return QVariant(); - } + QV4::Scope scope(array.as<Object>()->engine()); QV4::ScopedArrayObject a(scope, array); - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } -} + const QQmlType type = QQmlMetaType::qmlListType(typeHint); + if (type.isSequentialContainer()) { + const QQmlTypePrivate *priv = type.priv(); + const QMetaSequence *meta = &priv->extraData.sequentialContainerTypeData; + const QMetaType containerMetaType(priv->listId); + QVariant result(containerMetaType); + qint64 length = a->getLength(); + Q_ASSERT(length >= 0); + Q_ASSERT(length <= qint64(std::numeric_limits<quint32>::max())); -#undef SEQUENCE_TO_VARIANT + QV4::ScopedValue v(scope); + for (quint32 i = 0; i < quint32(length); ++i) { + const QMetaType valueMetaType = priv->typeId; + QVariant variant = ExecutionEngine::toVariant(a->get(i), valueMetaType, false); + if (valueMetaType == QMetaType::fromType<QVariant>()) { + meta->addValueAtEnd(result.data(), &variant); + } else { + const QMetaType originalType = variant.metaType(); + if (originalType != valueMetaType) { + const QVariant converted = QQmlValueTypeProvider::createValueType( + variant, valueMetaType); + if (converted.isValid()) { + variant = converted; + } else if (!variant.convert(valueMetaType) && originalType.isValid()) { + // If the original type was void, we're converting a "hole" in a sparse + // array. There is no point in warning about that. + qWarning().noquote() + << QLatin1String("Could not convert array value " + "at position %1 from %2 to %3") + .arg(QString::number(i), + QString::fromUtf8(originalType.name()), + QString::fromUtf8(valueMetaType.name())); + } + } + meta->addValueAtEnd(result.data(), variant.constData()); + } + } + return result; + } -#define SEQUENCE_GET_RAWCONTAINERPTR(ElementType, ElementTypeName, SequenceType, unused) \ - if (const QQml##ElementTypeName##List *list = [&]() -> const QQml##ElementTypeName##List* \ - { if (typeHint == qMetaTypeId<SequenceType>()) return object->as<QQml##ElementTypeName##List>(); return nullptr;}()) \ - return list->getRawContainerPtr(); \ - else + return QVariant(); +} -void* SequencePrototype::getRawContainerPtr(const Object *object, int typeHint) +void *SequencePrototype::getRawContainerPtr(const Sequence *object, QMetaType typeHint) { - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_GET_RAWCONTAINERPTR) { /* else */ return nullptr; } + if (object->d()->listType() == typeHint) + return object->getRawContainerPtr(); + return nullptr; } -#undef SEQUENCE_GET_RAWCONTAINERPTR - -#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \ - if (object->as<QQml##ElementTypeName##List>()) { \ - return qMetaTypeId<SequenceType>(); \ - } else - -int SequencePrototype::metaTypeForSequence(const QV4::Object *object) +QMetaType SequencePrototype::metaTypeForSequence(const Sequence *object) { - FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE) - /*else*/ { - return -1; - } + return object->d()->listType(); } -#undef MAP_META_TYPE +} // namespace QV4 QT_END_NAMESPACE + +#include "moc_qv4sequenceobject_p.cpp" |