aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4sequenceobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4sequenceobject.cpp')
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp1202
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"