diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 223 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4jscall_p.h | 17 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 12 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4sequenceobject.cpp | 178 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4sequenceobject_p.h | 21 | ||||
| -rw-r--r-- | src/qml/qml/qqmlvmemetaobject.cpp | 18 |
6 files changed, 223 insertions, 246 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index b2d74c8b5d..f73d881885 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1485,8 +1485,6 @@ static QVariant toVariant( static QObject *qtObjectFromJS(const QV4::Value &value); static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr); static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result); -static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst); -static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst); static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap); static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value) { @@ -1759,6 +1757,18 @@ QV4::ReturnedValue ExecutionEngine::fromData( QMetaType metaType, const void *ptr, QV4::Heap::Object *container, int property, uint flags) { + const auto createSequence = [&](const QMetaSequence metaSequence) { + QV4::Scope scope(this); + QV4::Scoped<Sequence> sequence(scope); + if (container) { + return QV4::SequencePrototype::newSequence( + this, metaType, metaSequence, ptr, + container, property, Heap::ReferenceObject::Flags(flags)); + } else { + return QV4::SequencePrototype::fromData(this, metaType, metaSequence, ptr); + } + }; + const int type = metaType.id(); if (type < QMetaType::User) { switch (QMetaType::Type(type)) { @@ -1823,15 +1833,9 @@ QV4::ReturnedValue ExecutionEngine::fromData( case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); case QMetaType::QStringList: - { - QV4::Scope scope(this); - QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromData(this, metaType, ptr)); - if (!retn->isUndefined()) - return retn->asReturnedValue(); - return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr))); - } + return createSequence(QMetaSequence::fromContainer<QStringList>()); case QMetaType::QVariantList: - return variantListToJS(this, *reinterpret_cast<const QVariantList *>(ptr)); + return createSequence(QMetaSequence::fromContainer<QVariantList>()); case QMetaType::QVariantMap: return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr)); case QMetaType::QJsonValue: @@ -1851,105 +1855,95 @@ QV4::ReturnedValue ExecutionEngine::fromData( default: break; } + } - if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) { - if (container) { - return QV4::QQmlValueTypeWrapper::create( - this, ptr, vtmo, metaType, - container, property, Heap::ReferenceObject::Flags(flags)); - } else { - return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType); - } - } + if (metaType.flags() & QMetaType::IsEnumeration) + return fromData(metaType.underlyingType(), ptr, container, property, flags); - } else if (!(metaType.flags() & QMetaType::IsEnumeration)) { - QV4::Scope scope(this); - if (metaType == QMetaType::fromType<QQmlListReference>()) { - typedef QQmlListReferencePrivate QDLRP; - QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr)); - if (p->object) - return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType); - else - return QV4::Encode::null(); - } else if (auto flags = metaType.flags(); flags & QMetaType::IsQmlList) { - // casting to QQmlListProperty<QObject> is slightly nasty, but it's the - // same QQmlListReference does. - const auto *p = static_cast<const QQmlListProperty<QObject> *>(ptr); - if (p->object) - return QV4::QmlListWrapper::create(scope.engine, *p, metaType); - else - return QV4::Encode::null(); - } else if (metaType == QMetaType::fromType<QJSValue>()) { - return QJSValuePrivate::convertToReturnedValue( - this, *reinterpret_cast<const QJSValue *>(ptr)); - } else if (metaType == QMetaType::fromType<QList<QObject *> >()) { - // XXX Can this be made more by using Array as a prototype and implementing - // directly against QList<QObject*>? - const QList<QObject *> &list = *(const QList<QObject *>*)ptr; - QV4::ScopedArrayObject a(scope, newArrayObject()); - a->arrayReserve(list.size()); - QV4::ScopedValue v(scope); - for (int ii = 0; ii < list.size(); ++ii) - a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii)))); - a->setArrayLengthUnchecked(list.size()); - return a.asReturnedValue(); - } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) { - if (flags.testFlag(QMetaType::IsConst)) - return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr)); - else - return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); - } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) { - const QJSPrimitiveValue *primitive = static_cast<const QJSPrimitiveValue *>(ptr); - switch (primitive->type()) { - case QJSPrimitiveValue::Boolean: - return Encode(primitive->asBoolean()); - case QJSPrimitiveValue::Integer: - return Encode(primitive->asInteger()); - case QJSPrimitiveValue::String: - return newString(primitive->asString())->asReturnedValue(); - case QJSPrimitiveValue::Undefined: - return Encode::undefined(); - case QJSPrimitiveValue::Null: - return Encode::null(); - case QJSPrimitiveValue::Double: - return Encode(primitive->asDouble()); - } + QV4::Scope scope(this); + if (metaType == QMetaType::fromType<QQmlListReference>()) { + typedef QQmlListReferencePrivate QDLRP; + QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr)); + if (p->object) + return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType); + else + return QV4::Encode::null(); + } else if (auto flags = metaType.flags(); flags & QMetaType::IsQmlList) { + // casting to QQmlListProperty<QObject> is slightly nasty, but it's the + // same QQmlListReference does. + const auto *p = static_cast<const QQmlListProperty<QObject> *>(ptr); + if (p->object) + return QV4::QmlListWrapper::create(scope.engine, *p, metaType); + else + return QV4::Encode::null(); + } else if (metaType == QMetaType::fromType<QJSValue>()) { + return QJSValuePrivate::convertToReturnedValue( + this, *reinterpret_cast<const QJSValue *>(ptr)); + } else if (metaType == QMetaType::fromType<QList<QObject *> >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList<QObject*>? + const QList<QObject *> &list = *(const QList<QObject *>*)ptr; + QV4::ScopedArrayObject a(scope, newArrayObject()); + a->arrayReserve(list.size()); + QV4::ScopedValue v(scope); + for (int ii = 0; ii < list.size(); ++ii) + a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii)))); + a->setArrayLengthUnchecked(list.size()); + return a.asReturnedValue(); + } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) { + if (flags.testFlag(QMetaType::IsConst)) + return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr)); + else + return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); + } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) { + const QJSPrimitiveValue *primitive = static_cast<const QJSPrimitiveValue *>(ptr); + switch (primitive->type()) { + case QJSPrimitiveValue::Boolean: + return Encode(primitive->asBoolean()); + case QJSPrimitiveValue::Integer: + return Encode(primitive->asInteger()); + case QJSPrimitiveValue::String: + return newString(primitive->asString())->asReturnedValue(); + case QJSPrimitiveValue::Undefined: + return Encode::undefined(); + case QJSPrimitiveValue::Null: + return Encode::null(); + case QJSPrimitiveValue::Double: + return Encode(primitive->asDouble()); } + } - QV4::Scoped<Sequence> sequence(scope); + if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) { if (container) { - sequence = QV4::SequencePrototype::newSequence( - this, metaType, ptr, - container, property, Heap::ReferenceObject::Flags(flags)); + return QV4::QQmlValueTypeWrapper::create( + this, ptr, vtmo, metaType, + container, property, Heap::ReferenceObject::Flags(flags)); } else { - sequence = QV4::SequencePrototype::fromData(this, metaType, ptr); + return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType); } - if (!sequence->isUndefined()) - return sequence->asReturnedValue(); + } - if (QMetaType::canConvert(metaType, QMetaType::fromType<QSequentialIterable>())) { - QSequentialIterable lst; - QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &lst); - return sequentialIterableToJS(this, lst); - } + const QQmlType listType = QQmlMetaType::qmlListType(metaType); + if (listType.isSequentialContainer()) + return createSequence(listType.listMetaSequence()); - if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) { - if (container) { - return QV4::QQmlValueTypeWrapper::create( - this, ptr, vtmo, metaType, - container, property, Heap::ReferenceObject::Flags(flags)); - } else { - return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType); - } - } - } + QSequentialIterable iterable; + if (QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &iterable)) { - // XXX TODO: To be compatible, we still need to handle: - // + QObjectList - // + QList<int> + // If the resulting iterable is useful for anything, turn it into a QV4::Sequence. + const QMetaSequence sequence = iterable.metaContainer(); + if (sequence.hasSize() && sequence.canGetValueAtIndex()) + return createSequence(sequence); - if (metaType.flags() & QMetaType::IsEnumeration) - return fromData(metaType.underlyingType(), ptr, container, property, flags); + // As a last resort, try to read the contents of the container via an iterator + // and build a JS array from them. + if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) { + QV4::ScopedArrayObject a(scope, newArrayObject()); + for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it) + a->push_back(fromVariant(*it)); + return a.asReturnedValue(); + } + } return QV4::Encode(newVariantObject(metaType, ptr)); } @@ -1970,39 +1964,6 @@ QVariantMap ExecutionEngine::variantMapFromJS(const Object *o) return objectToVariant(o).toMap(); } - -// Converts a QVariantList to JS. -// The result is a new Array object with length equal to the length -// of the QVariantList, and the elements being the QVariantList's -// elements converted to JS, recursively. -static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst) -{ - QV4::Scope scope(v4); - QV4::ScopedArrayObject a(scope, v4->newArrayObject()); - a->arrayReserve(lst.size()); - QV4::ScopedValue v(scope); - for (int i = 0; i < lst.size(); i++) - a->arrayPut(i, (v = variantToJS(v4, lst.at(i)))); - a->setArrayLengthUnchecked(lst.size()); - return a.asReturnedValue(); -} - -// Converts a QSequentialIterable to JS. -// The result is a new Array object with length equal to the length -// of the QSequentialIterable, and the elements being the QSequentialIterable's -// elements converted to JS, recursively. -static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst) -{ - QV4::Scope scope(v4); - QV4::ScopedArrayObject a(scope, v4->newArrayObject()); - a->arrayReserve(lst.size()); - QV4::ScopedValue v(scope); - for (int i = 0; i < lst.size(); i++) - a->arrayPut(i, (v = variantToJS(v4, lst.at(i)))); - a->setArrayLengthUnchecked(lst.size()); - return a.asReturnedValue(); -} - // Converts a QVariantMap to JS. // The result is a new Object object with property names being // the keys of the QVariantMap, and values being the values of diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index e824863b29..7bb38a5fe9 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -233,9 +233,17 @@ inline ReturnedValue coerceListType( ExecutionEngine *engine, const Value &value, const QQmlType &qmlType) { QMetaType type = qmlType.qListTypeId(); + const auto metaSequence = [&]() { + // TODO: We should really add the metasequence to the same QQmlType that holds + // all the other type information. Then we can get rid of the extra + // QQmlMetaType::qmlListType() here. + return qmlType.isSequentialContainer() + ? qmlType.listMetaSequence() + : QQmlMetaType::qmlListType(type).listMetaSequence(); + }; + if (const QV4::Sequence *sequence = value.as<QV4::Sequence>()) { - const QQmlTypePrivate *typePrivate = sequence->d()->typePrivate(); - if (typePrivate->listId == type) + if (sequence->d()->listType() == type) return value.asReturnedValue(); } @@ -256,7 +264,7 @@ inline ReturnedValue coerceListType( if (!array) { return (listValueType.flags() & QMetaType::PointerToQObject) ? QmlListWrapper::create(engine, listValueType) - : SequencePrototype::fromData(engine, type, nullptr); + : SequencePrototype::fromData(engine, type, metaSequence(), nullptr); } if (listValueType.flags() & QMetaType::PointerToQObject) { @@ -273,7 +281,8 @@ inline ReturnedValue coerceListType( return newList->asReturnedValue(); } - QV4::Scoped<Sequence> sequence(scope, SequencePrototype::fromData(engine, type, nullptr)); + QV4::Scoped<Sequence> sequence( + scope, SequencePrototype::fromData(engine, type, metaSequence(), nullptr)); const qsizetype length = array->getLength(); for (qsizetype i = 0; i < length; ++i) sequence->containerPutIndexed(i, array->get(i)); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 6a0142a46e..38b9226c9f 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -205,12 +205,12 @@ static ReturnedValue loadProperty( // See if it's a sequence type. // Pass nullptr as data. It's lazy-loaded. - QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence( - v4, propMetaType, nullptr, - wrapper, property.coreIndex(), - referenceFlags(scope.engine, property))); - if (!retn->isUndefined()) - return retn->asReturnedValue(); + const QQmlType qmlType = QQmlMetaType::qmlListType(propMetaType); + if (qmlType.isSequentialContainer()) { + return QV4::SequencePrototype::newSequence( + v4, propMetaType, qmlType.listMetaSequence(), nullptr, + wrapper, property.coreIndex(), referenceFlags(scope.engine, property)); + } QVariant v(propMetaType); property.readProperty(object, v.data()); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 4472a5411a..dc894fb670 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -27,9 +27,9 @@ static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) { Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::EnforcesLocation; - if (s->d()->typePrivate()->extraData.ld->canSetValueAtIndex()) + if (s->d()->metaSequence().canSetValueAtIndex()) flags |= Heap::ReferenceObject::CanWriteBack; - if (Sequence::valueMetaType(s->d()) == QMetaType::fromType<QVariant>()) + if (s->d()->valueMetaType() == QMetaType::fromType<QVariant>()) flags |= Heap::ReferenceObject::IsVariant; QV4::ScopedValue v(scope, scope.engine->fromVariant( @@ -42,18 +42,12 @@ static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) { return v->asReturnedValue(); } -static const QMetaSequence *metaSequence(const Heap::Sequence *p) -{ - return p->typePrivate()->extraData.ld; -} - template<typename Compare> void sortSequence(Sequence *sequence, const Compare &compare) { - auto *p = sequence->d(); - const auto *m = metaSequence(p); + /* non-const */ Heap::Sequence *p = sequence->d(); - QSequentialIterable iterable(*m, p->typePrivate()->listId, p->storagePointer()); + QSequentialIterable iterable(p->metaSequence(), p->listType(), p->storagePointer()); if (iterable.canRandomAccessIterate()) { std::sort(QSequentialIterable::RandomAccessIterator(iterable.mutableBegin()), QSequentialIterable::RandomAccessIterator(iterable.mutableEnd()), @@ -148,37 +142,35 @@ struct SequenceDefaultCompareFunctor } }; -void Heap::Sequence::init(const QQmlType &qmlType, const void *container) +void Heap::Sequence::initTypes(QMetaType listType, QMetaSequence metaSequence) { - ReferenceObject::init(nullptr, -1, NoFlag); - - Q_ASSERT(qmlType.isSequentialContainer()); - m_typePrivate = qmlType.priv(); - QQmlType::refHandle(m_typePrivate); - - m_container = m_typePrivate->listId.create(container); - + 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); } +void Heap::Sequence::init(QMetaType listType, QMetaSequence metaSequence, const void *container) +{ + ReferenceObject::init(nullptr, -1, NoFlag); + initTypes(listType, metaSequence); + m_container = listType.create(container); +} + void Heap::Sequence::init( - const QQmlType &qmlType, const void *container, - Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) + QMetaType listType, QMetaSequence metaSequence, const void *container, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) { ReferenceObject::init(object, propertyIndex, flags); + initTypes(listType, metaSequence); - Q_ASSERT(qmlType.isSequentialContainer()); - m_typePrivate = qmlType.priv(); - QQmlType::refHandle(m_typePrivate); - QV4::Scope scope(internalClass->engine); - QV4::Scoped<QV4::Sequence> o(scope, this); - o->setArrayType(Heap::ArrayData::Custom); - if (CppStackFrame *frame = scope.engine->currentStackFrame) + if (CppStackFrame *frame = internalClass->engine->currentStackFrame) setLocation(frame->v4Function, frame->statementNumber()); if (container) - m_container = QMetaType(m_typePrivate->listId).create(container); + m_container = listType.create(container); else if (flags & EnforcesLocation) QV4::ReferenceObject::readReference(this); } @@ -186,28 +178,27 @@ void Heap::Sequence::init( Heap::Sequence *Heap::Sequence::detached() const { return internalClass->engine->memoryManager->allocate<QV4::Sequence>( - QQmlType(m_typePrivate), m_container); + QMetaType(m_listType), QMetaSequence(m_metaSequence), m_container); } void Heap::Sequence::destroy() { if (m_container) - m_typePrivate->listId.destroy(m_container); - QQmlType::derefHandle(m_typePrivate); + listType().destroy(m_container); ReferenceObject::destroy(); } void *Heap::Sequence::storagePointer() { if (!m_container) - m_container = m_typePrivate->listId.create(); + m_container = listType().create(); return m_container; } bool Heap::Sequence::setVariant(const QVariant &variant) { const QMetaType variantReferenceType = variant.metaType(); - if (variantReferenceType != m_typePrivate->listId) { + 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 @@ -215,11 +206,10 @@ bool Heap::Sequence::setVariant(const QVariant &variant) const QQmlType newType = QQmlMetaType::qmlListType(variantReferenceType); if (newType.isSequentialContainer()) { if (m_container) - m_typePrivate->listId.destroy(m_container); - QQmlType::derefHandle(m_typePrivate); - m_typePrivate = newType.priv(); - QQmlType::refHandle(m_typePrivate); - m_container = m_typePrivate->listId.create(variant.constData()); + 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; @@ -235,32 +225,27 @@ bool Heap::Sequence::setVariant(const QVariant &variant) } QVariant Heap::Sequence::toVariant() const { - return QVariant(m_typePrivate->listId, m_container); -} - -const QMetaType Sequence::valueMetaType(const Heap::Sequence *p) -{ - return p->typePrivate()->typeId; + return QVariant(listType(), m_container); } qsizetype Sequence::size() const { const auto *p = d(); Q_ASSERT(p->storagePointer()); // Must readReference() before - return metaSequence(p)->size(p->storagePointer()); + return p->metaSequence().size(p->storagePointer()); } QVariant Sequence::at(qsizetype index) const { const auto *p = d(); Q_ASSERT(p->storagePointer()); // Must readReference() before - const QMetaType v = valueMetaType(p); + const QMetaType v = p->valueMetaType(); QVariant result; if (v == QMetaType::fromType<QVariant>()) { - metaSequence(p)->valueAtIndex(p->storagePointer(), index, &result); + p->metaSequence().valueAtIndex(p->storagePointer(), index, &result); } else { result = QVariant(v); - metaSequence(p)->valueAtIndex(p->storagePointer(), index, result.data()); + p->metaSequence().valueAtIndex(p->storagePointer(), index, result.data()); } return result; } @@ -284,45 +269,45 @@ void convertAndDo(const QVariant &item, const QMetaType v, Action action) void Sequence::append(const QVariant &item) { Heap::Sequence *p = d(); - convertAndDo(item, valueMetaType(p), [p](const void *data) { - metaSequence(p)->addValueAtEnd(p->storagePointer(), data); + convertAndDo(item, p->valueMetaType(), [p](const void *data) { + p->metaSequence().addValueAtEnd(p->storagePointer(), data); }); } void Sequence::append(qsizetype num, const QVariant &item) { Heap::Sequence *p = d(); - convertAndDo(item, valueMetaType(p), [p, num](const void *data) { - const QMetaSequence *m = metaSequence(p); + 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); + m.addValueAtEnd(container, data); }); } void Sequence::replace(qsizetype index, const QVariant &item) { Heap::Sequence *p = d(); - convertAndDo(item, valueMetaType(p), [p, index](const void *data) { - metaSequence(p)->setValueAtIndex(p->storagePointer(), index, data); + convertAndDo(item, p->valueMetaType(), [p, index](const void *data) { + p->metaSequence().setValueAtIndex(p->storagePointer(), index, data); }); } void Sequence::removeLast(qsizetype num) { auto *p = d(); - const auto *m = metaSequence(p); - - 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); + const QMetaSequence m = p->metaSequence(); + + 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()); + m.removeValueAtEnd(p->storagePointer()); } } @@ -355,7 +340,7 @@ bool Sequence::containerPutIndexed(qsizetype index, const Value &value) return false; const qsizetype count = size(); - const QMetaType valueType = valueMetaType(d()); + const QMetaType valueType = d()->valueMetaType(); const QVariant element = ExecutionEngine::toVariant(value, valueType, false); if (index < 0) @@ -518,25 +503,25 @@ int Sequence::virtualMetacall(Object *object, QMetaObject::Call call, int index, switch (call) { case QMetaObject::ReadProperty: { - const QMetaType valueType = valueMetaType(sequence->d()); + const QMetaType valueType = sequence->d()->valueMetaType(); if (!sequence->loadReference()) return 0; - const QMetaSequence *metaSequence = sequence->d()->typePrivate()->extraData.ld; - if (metaSequence->valueMetaType() != valueType) + 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)) + if (index < 0 || index >= metaSequence.size(storagePointer)) return 0; - metaSequence->valueAtIndex(storagePointer, index, a[0]); + metaSequence.valueAtIndex(storagePointer, index, a[0]); break; } case QMetaObject::WriteProperty: { void *storagePointer = sequence->d()->storagePointer(); - const QMetaSequence *metaSequence = sequence->d()->typePrivate()->extraData.ld; - if (index < 0 || index >= metaSequence->size(storagePointer)) + const QMetaSequence metaSequence = sequence->d()->metaSequence(); + if (index < 0 || index >= metaSequence.size(storagePointer)) return 0; - metaSequence->setValueAtIndex(storagePointer, index, a[0]); + metaSequence.setValueAtIndex(storagePointer, index, a[0]); sequence->storeReference(); break; } @@ -592,7 +577,7 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value if (newCount == count) { RETURN_UNDEFINED(); } else if (newCount > count) { - const QMetaType valueMetaType = metaSequence(This->d())->valueMetaType(); + 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. */ @@ -642,40 +627,43 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu } ReturnedValue SequencePrototype::newSequence( - QV4::ExecutionEngine *engine, QMetaType sequenceType, const void *data, - Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) + QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) { // This function is called when the property is a QObject Q_PROPERTY of // 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. - const QQmlType qmlType = QQmlMetaType::qmlListType(sequenceType); - if (qmlType.isSequentialContainer()) { - return engine->memoryManager->allocate<Sequence>( - qmlType, data, object, propertyIndex, flags)->asReturnedValue(); - } - - return Encode::undefined(); + return engine->memoryManager->allocate<Sequence>( + type, metaSequence, data, object, propertyIndex, flags)->asReturnedValue(); } ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant &v) { - return fromData(engine, v.metaType(), v.constData()); + 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()); + } + + return Encode::undefined(); } -ReturnedValue SequencePrototype::fromData(ExecutionEngine *engine, QMetaType type, const void *data) +ReturnedValue SequencePrototype::fromData( + ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data) { // 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. - const QQmlType qmlType = QQmlMetaType::qmlListType(type); - if (qmlType.isSequentialContainer()) - return engine->memoryManager->allocate<Sequence>(qmlType, data)->asReturnedValue(); - - return Encode::undefined(); + return engine->memoryManager->allocate<Sequence>(type, metaSequence, data)->asReturnedValue(); } QVariant SequencePrototype::toVariant(const Sequence *object) @@ -692,7 +680,7 @@ QVariant SequencePrototype::toVariant(const Sequence *object) if (!p->hasData()) return QVariant(); - return QVariant(p->typePrivate()->listId, p->storagePointer()); + return QVariant(p->listType(), p->storagePointer()); } QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHint) @@ -747,14 +735,14 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHin void *SequencePrototype::getRawContainerPtr(const Sequence *object, QMetaType typeHint) { - if (object->d()->typePrivate()->listId == typeHint) + if (object->d()->listType() == typeHint) return object->getRawContainerPtr(); return nullptr; } QMetaType SequencePrototype::metaTypeForSequence(const Sequence *object) { - return object->d()->typePrivate()->listId; + return object->d()->listType(); } } // namespace QV4 diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index c7bd96eb97..2ea20b0466 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -37,10 +37,11 @@ struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue newSequence( - QV4::ExecutionEngine *engine, QMetaType sequenceType, const void *data, - Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags); + QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags); static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant &vd); - static ReturnedValue fromData(QV4::ExecutionEngine *engine, QMetaType type, const void *data); + static ReturnedValue fromData( + QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data); static QMetaType metaTypeForSequence(const Sequence *object); static QVariant toVariant(const Sequence *object); @@ -52,8 +53,8 @@ namespace Heap { struct Sequence : ReferenceObject { - void init(const QQmlType &qmlType, const void *container); - void init(const QQmlType &qmlType, const void *container, + void init(QMetaType listType, QMetaSequence metaSequence, const void *container); + void init(QMetaType listType, QMetaSequence metaSequence, const void *container, Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags); Sequence *detached() const; @@ -68,11 +69,16 @@ struct Sequence : ReferenceObject bool setVariant(const QVariant &variant); QVariant toVariant() const; - const QQmlTypePrivate *typePrivate() const { return m_typePrivate; } + QMetaType listType() const { return QMetaType(m_listType); } + QMetaType valueMetaType() const { return QMetaType(m_metaSequence->valueMetaType); } + QMetaSequence metaSequence() const { return QMetaSequence(m_metaSequence); } private: + void initTypes(QMetaType listType, QMetaSequence metaSequence); + void *m_container; - const QQmlTypePrivate *m_typePrivate; + const QtPrivate::QMetaTypeInterface *m_listType; + const QtMetaContainerPrivate::QMetaSequenceInterface *m_metaSequence; }; } @@ -84,7 +90,6 @@ struct Q_QML_PRIVATE_EXPORT Sequence : public QV4::ReferenceObject V4_PROTOTYPE(sequencePrototype) V4_NEEDS_DESTROY public: - static const QMetaType valueMetaType(const Heap::Sequence *p); static QV4::ReturnedValue virtualGet( const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty); static qint64 virtualGetLength(const Managed *m); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 08e89f7e91..fb90e3e800 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -21,6 +21,8 @@ #include <private/qqmlpropertycachemethodarguments_p.h> #include <private/qqmlvaluetypewrapper_p.h> +#include <QtCore/qsequentialiterable.h> + #include <climits> // for CHAR_BIT QT_BEGIN_NAMESPACE @@ -864,8 +866,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * needActivate = true; } } else { - QV4::ScopedValue sequence(scope, QV4::SequencePrototype::fromData( - engine, propType, a[0])); + if (const QQmlType type = QQmlMetaType::qmlListType(propType); + type.isSequentialContainer()) { + sequence = QV4::SequencePrototype::fromData( + engine, propType, type.listMetaSequence(), a[0]); + } else if (QSequentialIterable iterable; + QMetaType::convert( + propType, a[0], + QMetaType::fromType<QSequentialIterable>(), + &iterable)) { + sequence = QV4::SequencePrototype::fromData( + engine, propType, iterable.metaContainer(), a[0]); + } else { + sequence = QV4::Encode::undefined(); + } md->set(engine, id, sequence); if (sequence->isUndefined()) { qmlWarning(object) |
