diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2020-09-09 10:34:48 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2020-10-02 13:21:09 +0200 |
commit | d621027babff9a30d56ab6af871a465108c9eaba (patch) | |
tree | 54a5ccff0410476538929e4ab1f1400075c0df81 /src/qml/jsruntime | |
parent | ed7719a3af58b9ba36a6fbaccd08c85b78f8a961 (diff) |
V4: Rewrite qv4sequenceobject based on QMetaSequence
This avoids the template explosion and makes the mechanism extendable.
You can now register additional anonymous sequential containers.
Fixes: QTBUG-71574
Task-number: QTBUG-82443
Change-Id: I5b9ed9af1533a3b7df8fc5bb37bbb73b8304e592
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject.cpp | 580 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject_p.h | 2 |
3 files changed, 247 insertions, 337 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5afe7b5923..923d458feb 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -709,6 +709,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) static_cast<VariantPrototype *>(variantPrototype())->init(); #if QT_CONFIG(qml_sequence_object) + static const bool registered = QV4::SequencePrototype::registerDefaultTypes(); + Q_UNUSED(registered); sequencePrototype()->cast<SequencePrototype>()->init(); #endif diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 191e3fd275..5c7f8289f2 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include <QtQml/qqml.h> +#include <QtCore/qsequentialiterable.h> #include "qv4sequenceobject_p.h" @@ -48,7 +49,8 @@ #include <private/qv4jscall_p.h> #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" -#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmltype_p_p.h> #if QT_CONFIG(qml_itemmodel) #include <private/qqmlmodelindexvaluetype_p.h> #include <QtCore/qabstractitemmodel.h> @@ -76,173 +78,22 @@ 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_QV4_SEQUENCE_TYPE_FOR_ITEMMODEL(F) \ - F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ - F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ - F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) -#else -#define FOREACH_QV4_SEQUENCE_TYPE_FOR_ITEMMODEL(F) -#endif - -#define FOREACH_QV4_SEQUENCE_TYPE(F) \ - 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, QString, QStringList, QString()) \ - F(QString, StringStdVector, std::vector<QString>, QString()) \ - F(QUrl, Url, QList<QUrl>, QUrl()) \ - F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \ - FOREACH_QV4_SEQUENCE_TYPE_FOR_ITEMMODEL(F) - -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) -{ - return engine->newString(element)->asReturnedValue(); -} - -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, int element) -{ - return QV4::Encode(element); -} - -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QUrl &element) -{ - return engine->newString(element.toString())->asReturnedValue(); -} - -#if QT_CONFIG(qml_itemmodel) -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QModelIndex &element) -{ - const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(QMetaType::QModelIndex); - return QV4::QQmlValueTypeWrapper::create(engine, QVariant(element), vtmo, QMetaType::QModelIndex); -} - -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QItemSelectionRange &element) -{ - int metaTypeId = qMetaTypeId<QItemSelectionRange>(); - const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(metaTypeId); - return QV4::QQmlValueTypeWrapper::create(engine, QVariant::fromValue(element), vtmo, metaTypeId); -} -#endif - -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, qreal element) -{ - return QV4::Encode(element); -} - -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, bool element) -{ - return QV4::Encode(element); -} - -static QString convertElementToString(const QString &element) -{ - return element; -} - -static QString convertElementToString(int element) -{ - return QString::number(element); -} - -static QString convertElementToString(const QUrl &element) -{ - return element.toString(); -} - -#if QT_CONFIG(qml_itemmodel) -static QString convertElementToString(const QModelIndex &element) -{ - return reinterpret_cast<const QQmlModelIndexValueType *>(&element)->toString(); -} - -static QString convertElementToString(const QItemSelectionRange &element) -{ - return reinterpret_cast<const QQmlItemSelectionRangeValueType *>(&element)->toString(); -} -#endif - -static QString convertElementToString(qreal element) -{ - QString qstr; - RuntimeHelpers::numberToString(&qstr, element, 10); - return qstr; -} - -static QString convertElementToString(bool element) -{ - if (element) - return QStringLiteral("true"); - else - return QStringLiteral("false"); -} - -template <typename ElementType> ElementType convertValueToElement(const Value &value); - -template <> QString convertValueToElement(const Value &value) -{ - return value.toQString(); -} - -template <> int convertValueToElement(const Value &value) -{ - return value.toInt32(); -} - -template <> QUrl convertValueToElement(const Value &value) -{ - return QUrl(value.toQString()); -} - -#if QT_CONFIG(qml_itemmodel) -template <> QModelIndex convertValueToElement(const Value &value) -{ - const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); - if (v) - return v->toVariant().toModelIndex(); - return QModelIndex(); -} - -template <> QItemSelectionRange convertValueToElement(const Value &value) -{ - const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); - if (v) - return v->toVariant().value<QItemSelectionRange>(); - return QItemSelectionRange(); -} -#endif - -template <> qreal convertValueToElement(const Value &value) -{ - return value.toNumber(); -} - -template <> bool convertValueToElement(const Value &value) -{ - return value.toBoolean(); -} - namespace QV4 { -template <typename Container> struct QV4Sequence; - namespace Heap { -template <typename Container> struct QV4Sequence : Object { - void init(const Container &container); - void init(QObject *object, int propertyIndex, bool readOnly); + void init(const QQmlType &qmlType, const void *container); + void init(QObject *object, int propertyIndex, const QQmlType &qmlType, bool readOnly); void destroy() { - delete container; + typePrivate->typeId.destroy(container); + QQmlType::derefHandle(typePrivate); object.destroy(); Object::destroy(); } - mutable Container *container; + mutable void *container; + const QQmlTypePrivate *typePrivate; QV4QPointer<QObject> object; int propertyIndex; bool isReference : 1; @@ -251,15 +102,106 @@ struct QV4Sequence : Object { } -template <typename Container> struct QV4Sequence : public QV4::Object { - V4_OBJECT2(QV4Sequence<Container>, QV4::Object) + V4_OBJECT2(QV4Sequence, QV4::Object) Q_MANAGED_TYPE(V4Sequence) V4_PROTOTYPE(sequencePrototype) V4_NEEDS_DESTROY public: + static const QMetaSequence *meta(const Heap::QV4Sequence *p) + { + return p->typePrivate->extraData.ld; + } + + qsizetype size() const + { + const auto *p = d(); + return meta(p)->size(p->container); + } + + QVariant at(int index) const + { + const auto *p = d(); + const auto *m = meta(p); + QVariant result(m->valueMetaType()); + m->valueAtIndex(p->container, index, result.data()); + return result; + } + + void append(const QVariant &item) + { + const auto *p = d(); + const auto *m = meta(p); + if (item.metaType() == m->valueMetaType()) { + m->addValueAtEnd(p->container, item.constData()); + } else { + QVariant converted = item; + if (!converted.convert(m->valueMetaType())) + converted = QVariant(m->valueMetaType()); + m->addValueAtEnd(p->container, converted.constData()); + } + } + + void replace(int index, const QVariant &item) + { + const auto *p = d(); + const auto *m = meta(p); + if (item.metaType() == m->valueMetaType()) { + m->setValueAtIndex(p->container, index, item.constData()); + } else { + QVariant converted = item; + if (!converted.convert(m->valueMetaType())) + converted = QVariant(m->valueMetaType()); + m->setValueAtIndex(p->container, index, converted.constData()); + } + } + + template<typename Compare> + void sort(const Compare &compare) + { + const auto *p = d(); + const auto *m = meta(p); + + QSequentialIterable iterable(*m, p->typePrivate->typeId, p->container); + 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"; + } + } + + void removeLast(int num) + { + const auto *p = d(); + const auto *m = meta(p); + + if (m->canEraseRangeAtIterator() && m->hasRandomAccessIterator() && num > 1) { + void *i = m->end(p->container); + m->advanceIterator(i, -num); + void *j = m->end(p->container); + m->eraseRangeAtIterator(p->container, i, j); + m->destroyIterator(i); + m->destroyIterator(j); + } else { + for (int i = 0; i < num; ++i) + m->removeValueAtEnd(p->container); + } + } + + QVariant toVariant() + { + const auto *p = d(); + return QVariant(p->typePrivate->typeId, p->container); + } + void init() { defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length); @@ -282,10 +224,10 @@ public: } loadReference(); } - if (index < size_t(d()->container->size())) { + if (index < size()) { if (hasProperty) *hasProperty = true; - return convertElementToValue(engine(), qAsConst(*(d()->container))[index]); + return engine()->fromVariant(at(index)); } if (hasProperty) *hasProperty = false; @@ -314,22 +256,20 @@ public: loadReference(); } - size_t count = size_t(d()->container->size()); - - typename Container::value_type element = convertValueToElement<typename Container::value_type>(value); + qsizetype count = size(); + const QMetaType valueMetaType = meta(d())->valueMetaType(); + const QVariant element = engine()->toVariant(value, valueMetaType.id(), false); if (index == count) { - d()->container->push_back(element); + append(element); } else if (index < count) { - (*d()->container)[index] = element; + replace(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); + while (index > count++) + append(QVariant(valueMetaType)); + append(element); } if (d()->isReference) @@ -349,7 +289,7 @@ public: return QV4::Attr_Invalid; loadReference(); } - return (index < size_t(d()->container->size())) ? QV4::Attr_Data : QV4::Attr_Invalid; + return (index < size()) ? QV4::Attr_Data : QV4::Attr_Invalid; } struct OwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator @@ -365,13 +305,13 @@ public: s->loadReference(); } - if (arrayIndex < static_cast<uint>(s->d()->container->size())) { + if (arrayIndex < s->size()) { uint index = arrayIndex; ++arrayIndex; if (attrs) *attrs = QV4::Attr_Data; if (pd) - pd->value = convertElementToValue(s->engine(), (*s->d()->container)[index]); + pd->value = s->engine()->fromVariant(s->at(index)); return PropertyKey::fromArrayIndex(index); } @@ -398,12 +338,12 @@ public: loadReference(); } - if (index >= size_t(d()->container->size())) + if (index >= size()) return false; /* according to ECMA262r3 it should be Undefined, */ /* but we cannot, so we insert a default-value instead. */ - (*d()->container)[index] = typename Container::value_type(); + replace(index, QVariant()); if (d()->isReference) storeReference(); @@ -415,7 +355,7 @@ public: { if (!other) return false; - QV4Sequence<Container> *otherSequence = other->as<QV4Sequence<Container> >(); + QV4Sequence *otherSequence = other->as<QV4Sequence>(); if (!otherSequence) return false; if (d()->isReference && otherSequence->d()->isReference) { @@ -428,9 +368,9 @@ public: struct DefaultCompareFunctor { - bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + bool operator()(const QVariant &lhs, const QVariant &rhs) { - return convertElementToString(lhs) < convertElementToString(rhs); + return lhs.toString() < rhs.toString(); } }; @@ -440,15 +380,15 @@ public: : m_v4(v4), m_compareFn(&compareFn) {} - bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + 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] = convertElementToValue(m_v4, lhs); - argv[1] = convertElementToValue(m_v4, rhs); + 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.engine->hasException) return false; @@ -470,13 +410,10 @@ public: loadReference(); } - 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); - } + if (argc == 1 && argv[0].as<FunctionObject>()) + sort(CompareFunctor(f->engine(), argv[0])); + else + sort(DefaultCompareFunctor()); if (d()->isReference) storeReference(); @@ -487,7 +424,7 @@ public: static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) { QV4::Scope scope(b); - QV4::Scoped<QV4Sequence<Container>> This(scope, thisObject->as<QV4Sequence<Container> >()); + QV4::Scoped<QV4Sequence> This(scope, thisObject->as<QV4Sequence>()); if (!This) THROW_TYPE_ERROR(); @@ -496,13 +433,13 @@ public: RETURN_RESULT(Encode(0)); This->loadReference(); } - RETURN_RESULT(Encode(qint32(This->d()->container->size()))); + RETURN_RESULT(Encode(qint32(This->size()))); } static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { QV4::Scope scope(f); - QV4::Scoped<QV4Sequence<Container>> This(scope, thisObject->as<QV4Sequence<Container> >()); + QV4::Scoped<QV4Sequence> This(scope, thisObject->as<QV4Sequence>()); if (!This) THROW_TYPE_ERROR(); @@ -524,23 +461,21 @@ public: } /* Determine whether we need to modify the sequence */ quint32 newCount = static_cast<quint32>(newLength); - quint32 count = static_cast<quint32>(This->d()->container->size()); + quint32 count = static_cast<quint32>(This->size()); if (newCount == count) { RETURN_UNDEFINED(); } else if (newCount > count) { + const QMetaType valueMetaType = meta(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->d()->container->reserve(newCount); - while (newCount > count++) { - This->d()->container->push_back(typename Container::value_type()); - } + while (newCount > count++) + This->append(QVariant(valueMetaType)); } 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()); - } + if (newCount < count) + This->removeLast(count - newCount); } /* write back if required. */ if (This->d()->isReference) { @@ -550,20 +485,6 @@ public: RETURN_UNDEFINED(); } - QVariant toVariant() const - { return QVariant::fromValue<Container>(*d()->container); } - - 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); - } - void* getRawContainerPtr() const { return d()->container; } @@ -589,59 +510,66 @@ public: { if (!id.isArrayIndex()) return Object::virtualGet(that, id, receiver, hasProperty); - return static_cast<const QV4Sequence<Container> *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty); + return static_cast<const QV4Sequence *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty); } static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver) { if (id.isArrayIndex()) - return static_cast<QV4Sequence<Container> *>(that)->containerPutIndexed(id.asArrayIndex(), value); + return static_cast<QV4Sequence *>(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 QV4Sequence<Container> *>(that)->containerQueryIndexed(index); } + { return static_cast<const QV4Sequence *>(that)->containerQueryIndexed(index); } static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id) { if (id.isArrayIndex()) { uint index = id.asArrayIndex(); - return static_cast<QV4Sequence<Container> *>(that)->containerDeleteIndexedProperty(index); + return static_cast<QV4Sequence *>(that)->containerDeleteIndexedProperty(index); } return Object::virtualDeleteProperty(that, id); } static bool virtualIsEqualTo(Managed *that, Managed *other) - { return static_cast<QV4Sequence<Container> *>(that)->containerIsEqualTo(other); } + { return static_cast<QV4Sequence *>(that)->containerIsEqualTo(other); } static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target) - { return static_cast<const QV4Sequence<Container> *>(m)->containerOwnPropertyKeys(m, target);} - + { return static_cast<const QV4Sequence *>(m)->containerOwnPropertyKeys(m, target);} }; -template <typename Container> -void Heap::QV4Sequence<Container>::init(const Container &container) +void Heap::QV4Sequence::init(const QQmlType &qmlType, const void *container) { Object::init(); - this->container = new Container(container); + + Q_ASSERT(qmlType.isSequentialContainer()); + typePrivate = qmlType.priv(); + QQmlType::refHandle(typePrivate); + + this->container = QMetaType(typePrivate->typeId).create(container); propertyIndex = -1; isReference = false; isReadOnly = false; object.init(); QV4::Scope scope(internalClass->engine); - QV4::Scoped<QV4::QV4Sequence<Container> > o(scope, this); + QV4::Scoped<QV4::QV4Sequence> o(scope, this); o->setArrayType(Heap::ArrayData::Custom); o->init(); } -template <typename Container> -void Heap::QV4Sequence<Container>::init(QObject *object, int propertyIndex, bool readOnly) +void Heap::QV4Sequence::init(QObject *object, int propertyIndex, const QQmlType &qmlType, + bool readOnly) { Object::init(); - this->container = new Container; + + Q_ASSERT(qmlType.isSequentialContainer()); + typePrivate = qmlType.priv(); + QQmlType::refHandle(typePrivate); + container = QMetaType(typePrivate->typeId).create(); this->propertyIndex = propertyIndex; isReference = true; this->isReadOnly = readOnly; this->object.init(object); QV4::Scope scope(internalClass->engine); - QV4::Scoped<QV4::QV4Sequence<Container> > o(scope, this); + QV4::Scoped<QV4::QV4Sequence> o(scope, this); o->setArrayType(Heap::ArrayData::Custom); o->loadReference(); o->init(); @@ -650,46 +578,42 @@ void Heap::QV4Sequence<Container>::init(QObject *object, int propertyIndex, bool } namespace QV4 { +DEFINE_OBJECT_VTABLE(QV4Sequence); +} + + +template<typename SequenceType> +void registerSequenceType() +{ + qRegisterMetaType<SequenceType>(); + qmlRegisterAnonymousSequentialContainer<SequenceType>("QML", 1); +} -typedef QV4Sequence<std::vector<int> > QV4IntStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4IntStdVectorList); -typedef QV4Sequence<std::vector<qreal> > QV4RealStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4RealStdVectorList); -typedef QV4Sequence<std::vector<bool> > QV4BoolStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4BoolStdVectorList); -typedef QV4Sequence<QStringList> QV4QStringList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4QStringList); -typedef QV4Sequence<std::vector<QString> > QV4StringStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4StringStdVectorList); -typedef QV4Sequence<QList<int> > QV4IntList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4IntList); -typedef QV4Sequence<QList<QUrl> > QV4UrlList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4UrlList); -typedef QV4Sequence<std::vector<QUrl> > QV4UrlStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4UrlStdVectorList); +bool SequencePrototype::registerDefaultTypes() +{ + registerSequenceType<std::vector<int>>(); + registerSequenceType<std::vector<qreal>>(); + registerSequenceType<std::vector<bool>>(); + registerSequenceType<QList<int>>(); + registerSequenceType<QList<qreal>>(); + registerSequenceType<QList<bool>>(); + registerSequenceType<QStringList>(); + registerSequenceType<std::vector<QString>>(); + registerSequenceType<QList<QUrl>>(); + registerSequenceType<std::vector<QUrl>>(); #if QT_CONFIG(qml_itemmodel) -typedef QV4Sequence<QModelIndexList> QV4QModelIndexList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4QModelIndexList); -typedef QV4Sequence<std::vector<QModelIndex> > QV4QModelIndexStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4QModelIndexStdVectorList); -typedef QV4Sequence<QItemSelection> QV4QItemSelectionRangeList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4QItemSelectionRangeList); + registerSequenceType<QModelIndexList>(); + registerSequenceType<std::vector<QModelIndex>>(); + registerSequenceType<QItemSelection>(); #endif -typedef QV4Sequence<QList<bool> > QV4BoolList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4BoolList); -typedef QV4Sequence<QList<qreal> > QV4RealList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QV4RealList); - + return true; } -#define REGISTER_QV4_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType); void SequencePrototype::init() { - FOREACH_QV4_SEQUENCE_TYPE(REGISTER_QV4_SEQUENCE_METATYPE) defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); } -#undef REGISTER_QV4_SEQUENCE_METATYPE ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int) { @@ -706,84 +630,64 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu if (argc >= 2) return o.asReturnedValue(); -#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ - if (QV4##SequenceElementTypeName##List *s = o->as<QV4##SequenceElementTypeName##List>()) { \ - if (!s->sort(b, thisObject, argv, argc)) \ - THROW_TYPE_ERROR(); \ - } else - - FOREACH_QV4_SEQUENCE_TYPE(CALL_SORT) + if (auto *s = o->as<QV4Sequence>()) { + 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_QV4_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<QV4##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) { 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_QV4_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return QV4::Encode::undefined(); } -} -#undef NEW_REFERENCE_SEQUENCE -#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ - if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QV4##ElementTypeName##List>(v.value<SequenceType >())); \ - return obj.asReturnedValue(); \ - } else -ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded) + const QQmlType qmlType = QQmlMetaType::qmlType( + sequenceType, QQmlMetaType::TypeIdCategory::MetaType); + if (qmlType.isSequentialContainer()) { + *succeeded = true; + QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QV4Sequence>( + object, propertyIndex, qmlType, readOnly)); + return obj.asReturnedValue(); + } + + *succeeded = false; + return Encode::undefined(); +} + +ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant &v, + bool *succeeded) { 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_QV4_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 (QV4##ElementTypeName##List *list = object->as<QV4##ElementTypeName##List>()) \ - return list->toVariant(); \ - else + const QQmlType qmlType = QQmlMetaType::qmlType( + v.userType(), QQmlMetaType::TypeIdCategory::MetaType); + if (qmlType.isSequentialContainer()) { + *succeeded = true; + QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QV4Sequence>( + qmlType, v.data())); + return obj.asReturnedValue(); + } + + *succeeded = false; + return Encode::undefined(); +} QVariant SequencePrototype::toVariant(Object *object) { Q_ASSERT(object->isListType()); - FOREACH_QV4_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); } + return object->as<QV4Sequence>()->toVariant(); } -#undef SEQUENCE_TO_VARIANT -#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ - if (typeHint == qMetaTypeId<SequenceType>()) { \ - return QV4##ElementTypeName##List::toVariant(a); \ - } else - QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, bool *succeeded) { *succeeded = true; @@ -795,37 +699,41 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, boo QV4::Scope scope(array.as<Object>()->engine()); QV4::ScopedArrayObject a(scope, array); - FOREACH_QV4_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } -} - -#undef SEQUENCE_TO_VARIANT + const QQmlType type = QQmlMetaType::qmlType(typeHint, QQmlMetaType::TypeIdCategory::MetaType); + if (type.isSequentialContainer()) { + const QMetaSequence *meta = type.priv()->extraData.ld; + const QMetaType containerMetaType(type.priv()->typeId); + QVariant result(containerMetaType); + quint32 length = a->getLength(); + QV4::ScopedValue v(scope); + for (quint32 i = 0; i < length; ++i) { + const QMetaType valueMetaType = meta->valueMetaType(); + QVariant variant = scope.engine->toVariant(a->get(i), valueMetaType.id(), false); + if (variant.metaType() != valueMetaType && !variant.convert(valueMetaType)) + variant = QVariant(valueMetaType); + meta->addValueAtEnd(result.data(), variant.constData()); + } + return result; + } -#define SEQUENCE_GET_RAWCONTAINERPTR(ElementType, ElementTypeName, SequenceType, unused) \ - if (const QV4##ElementTypeName##List *list = [&]() -> const QV4##ElementTypeName##List* \ - { if (typeHint == qMetaTypeId<SequenceType>()) return object->as<QV4##ElementTypeName##List>(); return nullptr;}()) \ - return list->getRawContainerPtr(); \ - else + *succeeded = false; + return QVariant(); +} void* SequencePrototype::getRawContainerPtr(const Object *object, int typeHint) { - FOREACH_QV4_SEQUENCE_TYPE(SEQUENCE_GET_RAWCONTAINERPTR) { /* else */ return nullptr; } + if (auto *s = object->as<QV4Sequence>()) { + if (s->d()->typePrivate->typeId.id() == typeHint) + return s->getRawContainerPtr(); + } + return nullptr; } -#undef SEQUENCE_GET_RAWCONTAINERPTR - -#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \ - if (object->as<QV4##ElementTypeName##List>()) { \ - return qMetaTypeId<SequenceType>(); \ - } else - int SequencePrototype::metaTypeForSequence(const QV4::Object *object) { - FOREACH_QV4_SEQUENCE_TYPE(MAP_META_TYPE) - /*else*/ { - return -1; - } + if (auto *s = object->as<QV4Sequence>()) + return s->d()->typePrivate->typeId.id(); + return -1; } -#undef MAP_META_TYPE - QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 886dfaa521..6229a4b5ec 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -68,12 +68,12 @@ namespace QV4 { struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object { V4_PROTOTYPE(arrayPrototype) + static bool registerDefaultTypes(); void init(); static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static bool isSequenceType(int sequenceTypeId); static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool readOnly, bool *succeeded); static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); static int metaTypeForSequence(const Object *object); |