aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-06-19 09:29:34 +0200
committerUlf Hermann <ulf.hermann@qt.io>2023-06-30 10:47:17 +0200
commitb9bfdea0e2c6721d2306af0ecc44f88da9988957 (patch)
tree2cfc6b8f9b43a221d0cdb4c92d0bd868696ab952
parent975a6bff84815f536abf1324394193b8180edeaa (diff)
QML: Un-specialcase QStringList and QVariantList conversion
Those are just regular sequences these days. They can be written back. Drop some now-dead code and deduplicate the value type conversion code in the process. We should try the (more common) value type conversion before the sequence conversion, but after all the "simple" conversions. [ChangeLog][QtQml][Important Behavior Changes] Converting a QVariantList to a QJSValue via, for example QJSEngine::toScriptValue() does not produce a JavaScript array anymore, but rather a better suited sequence object that behaves almost like a JavaScript array. The only difference is that its QJSValue::isArray() will return false now. Fixes: QTBUG-113690 Change-Id: Ib176c34d59c45a6b5cff68d029c4b1b87d7aa192 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qml/jsruntime/qv4engine.cpp223
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h17
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp12
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp178
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h21
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp18
-rw-r--r--tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp9
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.cpp12
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h5
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp8
12 files changed, 251 insertions, 262 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)
diff --git a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp
index f11ae73a59..7386fe0bb2 100644
--- a/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp
+++ b/tests/auto/qml/qjsmanagedvalue/tst_qjsmanagedvalue.cpp
@@ -542,7 +542,7 @@ void tst_QJSManagedValue::toVariant()
}
- // array
+ // variant list
{
auto handler = qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &, const QString &) {
if (type == QtMsgType::QtWarningMsg)
@@ -553,12 +553,10 @@ void tst_QJSManagedValue::toVariant()
QVariantList listIn;
listIn << 123 << QStringLiteral("hello");
QJSManagedValue array(eng.toManagedValue(listIn));
- QVERIFY(array.isArray());
QCOMPARE(array.property(QStringLiteral("length")).toInt(), 2);
QVariant retained = array.toVariant();
- QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
- QVERIFY(QJSManagedValue(retained.value<QJSValue>(), &eng).strictlyEquals(array));
+ QCOMPARE(retained.metaType(), QMetaType::fromType<QVariantList>());
QVariantList listOut = retained.toList();
QCOMPARE(listOut.size(), listIn.size());
@@ -566,7 +564,6 @@ void tst_QJSManagedValue::toVariant()
QCOMPARE(listOut.at(i), listIn.at(i));
// round-trip conversion
QJSManagedValue array2(eng.toManagedValue(retained));
- QVERIFY(array2.isArray());
QCOMPARE(array2.property(QStringLiteral("length")).toInt(), array.property(QStringLiteral("length")).toInt());
for (int i = 0; i < array.property(QStringLiteral("length")).toInt(); ++i)
QVERIFY(array2.property(i).strictlyEquals(array.property(i)));
@@ -1525,8 +1522,6 @@ void tst_QJSManagedValue::strictlyEquals()
{
QJSManagedValue var1(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
QJSManagedValue var2(eng.toManagedValue(QVariant(QStringList() << QStringLiteral("a"))));
- QVERIFY(var1.isArray());
- QVERIFY(var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
{
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index 516abed9e8..1090ed9716 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -1085,13 +1085,8 @@ void tst_QJSValue::toVariant()
QVariantList listIn;
listIn << 123 << "hello";
QJSValue array = eng.toScriptValue(listIn);
- QVERIFY(array.isArray());
QCOMPARE(array.property("length").toInt(), 2);
- QVariant retained = array.toVariant(QJSValue::RetainJSObjects);
- QCOMPARE(retained.metaType(), QMetaType::fromType<QJSValue>());
- QVERIFY(retained.value<QJSValue>().strictlyEquals(array));
-
QVariant ret = array.toVariant();
QCOMPARE(ret.typeId(), QMetaType::QVariantList);
QVariantList listOut = ret.toList();
@@ -1100,7 +1095,6 @@ void tst_QJSValue::toVariant()
QCOMPARE(listOut.at(i), listIn.at(i));
// round-trip conversion
QJSValue array2 = eng.toScriptValue(ret);
- QVERIFY(array2.isArray());
QCOMPARE(array2.property("length").toInt(), array.property("length").toInt());
for (int i = 0; i < array.property("length").toInt(); ++i)
QVERIFY(array2.property(i).strictlyEquals(array.property(i)));
@@ -2345,8 +2339,6 @@ void tst_QJSValue::strictlyEquals()
{
QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a"));
QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a"));
- QVERIFY(var1.isArray());
- QVERIFY(var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
{
diff --git a/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml
index b303f091b7..6047323f31 100644
--- a/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml
+++ b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml
@@ -10,9 +10,11 @@ QtObject {
Component.onCompleted: {
gadget.gadgetStringList = ["Element1", "Element2", "Element3"]
+ gadget.gadgetVariantList = [1, "foo", null, true]
object.qobjectStringList = ["Element1", "Element2", "Element3"]
gadget.gadgetStringList[0] = "Completely new Element"
+ gadget.gadgetVariantList[0] = "Completely new Element"
object.qobjectStringList[0] = "Completely new Element"
}
}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp
index 303214d23e..85999ebc8b 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.cpp
+++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp
@@ -450,6 +450,18 @@ void ListPropertyAssignment_Gadget::setGadgetStringList(const QStringList &list)
m_gadgetStringList = list;
}
+QVariantList ListPropertyAssignment_Gadget::gadgetVariantList() const
+{
+ return m_gadgetVariantList;
+}
+
+void ListPropertyAssignment_Gadget::setGadgetVariantList(const QVariantList &list)
+{
+ if (m_gadgetVariantList == list)
+ return;
+ m_gadgetVariantList = list;
+}
+
ListPropertyAssignment_Object::ListPropertyAssignment_Object(QObject *parent)
: QObject{ parent } { }
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index 2dbd925c23..2f9ded4a9d 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -2072,14 +2072,19 @@ class ListPropertyAssignment_Gadget
{
Q_GADGET
Q_PROPERTY(QStringList gadgetStringList READ gadgetStringList WRITE setGadgetStringList)
+ Q_PROPERTY(QVariantList gadgetVariantList READ gadgetVariantList WRITE setGadgetVariantList)
QML_VALUE_TYPE(listPropertyAssignment_Gadget)
public:
ListPropertyAssignment_Gadget();
QStringList gadgetStringList() const;
void setGadgetStringList(const QStringList &list);
+ QVariantList gadgetVariantList() const;
+ void setGadgetVariantList(const QVariantList &list);
+
private:
QStringList m_gadgetStringList;
+ QVariantList m_gadgetVariantList;
};
class ListPropertyAssignment_Object : public QObject
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 53d068c855..c6d73dabb0 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -10452,9 +10452,15 @@ void tst_qqmlecmascript::assignListPropertyByIndexOnGadget()
QVERIFY(object);
QStringList expected{ "Completely new Element", "Element2", "Element3" };
+ QVariantList variants {
+ u"Completely new Element"_s,
+ u"foo"_s,
+ QVariant::fromValue<std::nullptr_t>(nullptr),
+ QVariant::fromValue<bool>(true)
+ };
- QEXPECT_FAIL("", "Assigning to an index of a list property on a gadget doesn't work.", Continue);
QCOMPARE(gadget.gadgetStringList(), expected);
+ QCOMPARE(gadget.gadgetVariantList(), variants);
QCOMPARE(object->qobjectStringList(), expected);
}