aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/qml/CMakeLists.txt1
-rw-r--r--src/qml/jsruntime/qv4engine.cpp48
-rw-r--r--src/qml/jsruntime/qv4engine_p.h2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp6
-rw-r--r--src/qml/jsruntime/qv4variantassociationobject.cpp377
-rw-r--r--src/qml/jsruntime/qv4variantassociationobject_p.h120
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp7
-rw-r--r--src/qml/qqmlbuiltins_p.h8
8 files changed, 539 insertions, 30 deletions
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index 3a8e2cf99b..33bf22c671 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -219,6 +219,7 @@ qt_internal_add_qml_module(Qml
jsruntime/qv4typedarray.cpp jsruntime/qv4typedarray_p.h
jsruntime/qv4urlobject.cpp jsruntime/qv4urlobject_p.h
jsruntime/qv4value.cpp jsruntime/qv4value_p.h
+ jsruntime/qv4variantassociationobject.cpp jsruntime/qv4variantassociationobject_p.h
jsruntime/qv4variantobject.cpp jsruntime/qv4variantobject_p.h
jsruntime/qv4vme_moth.cpp jsruntime/qv4vme_moth_p.h
jsruntime/qv4vtable_p.h
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 0b6464f094..1be562b86b 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -59,6 +59,7 @@
#include "qv4atomics_p.h"
#include "qv4urlobject_p.h"
#include "qv4variantobject_p.h"
+#include "qv4variantassociationobject_p.h"
#include "qv4sequenceobject_p.h"
#include "qv4qobjectwrapper_p.h"
#include "qv4qmetaobjectwrapper_p.h"
@@ -323,6 +324,8 @@ void ExecutionEngine::initializeStaticMembers()
if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>())
QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantHash>())
+ QMetaType::registerConverter<QJSValue, QVariantHash>(convertJSValueToVariantType<QVariantHash>);
if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>())
QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
@@ -625,6 +628,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d());
+ jsObjects[VariantAssociationProto] = memoryManager->allocate<VariantAssociationPrototype>();
+ Q_ASSERT(variantAssociationPrototype()->getPrototypeOf() == objectPrototype()->d());
+
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
@@ -1521,11 +1527,6 @@ static QObject *qtObjectFromJS(const QV4::Value &value);
static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr,
JSToQVariantConversionBehavior behavior = JSToQVariantConversionBehavior::Safish);
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
-static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap);
-static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value)
-{
- return v4->metaTypeToJS(value.metaType(), value.constData());
-}
static QVariant toVariant(const QV4::Value &value, QMetaType metaType, JSToQVariantConversionBehavior conversionBehavior,
V4ObjectSet *visitedObjects)
@@ -1578,6 +1579,10 @@ static QVariant toVariant(const QV4::Value &value, QMetaType metaType, JSToQVari
// Otherwise produce the "natural" type of the sequence.
return QV4::SequencePrototype::toVariant(s);
+ } else if (auto association = object->as<QV4::VariantAssociationObject>()) {
+ if (conversionBehavior == JSToQVariantConversionBehavior::Never)
+ return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(association->asReturnedValue()));
+ return association->d()->toVariant();
}
}
@@ -1847,7 +1852,15 @@ QV4::ReturnedValue ExecutionEngine::fromData(
case QMetaType::QVariantList:
return createSequence(QMetaSequence::fromContainer<QVariantList>());
case QMetaType::QVariantMap:
- return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr));
+ return VariantAssociationPrototype::fromQVariantMap(
+ this,
+ *reinterpret_cast<const QVariantMap *>(ptr),
+ container, property, Heap::ReferenceObject::Flags(flags));
+ case QMetaType::QVariantHash:
+ return VariantAssociationPrototype::fromQVariantHash(
+ this,
+ *reinterpret_cast<const QVariantHash *>(ptr),
+ container, property, Heap::ReferenceObject::Flags(flags));
case QMetaType::QJsonValue:
return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(ptr));
case QMetaType::QJsonObject:
@@ -1973,29 +1986,6 @@ QVariantMap ExecutionEngine::variantMapFromJS(const Object *o)
return objectToVariantMap(o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
}
-// 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
-// the QVariantMap converted to JS, recursively.
-static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap)
-{
- QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, v4->newObject());
- QV4::ScopedString s(scope);
- QV4::ScopedPropertyKey key(scope);
- QV4::ScopedValue v(scope);
- for (QVariantMap::const_iterator it = vmap.constBegin(), cend = vmap.constEnd(); it != cend; ++it) {
- s = v4->newIdentifier(it.key());
- key = s->propertyKey();
- v = variantToJS(v4, it.value());
- if (key->isArrayIndex())
- o->arraySet(key->asArrayIndex(), v);
- else
- o->insertMember(s, v);
- }
- return o.asReturnedValue();
-}
-
// Converts the meta-type defined by the given type and data to JS.
// Returns the value if conversion succeeded, an empty handle otherwise.
QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType type, const void *data)
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 23106dabe7..0e98e94651 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -207,6 +207,7 @@ public:
URIErrorProto,
PromiseProto,
VariantProto,
+ VariantAssociationProto,
SequenceProto,
SharedArrayBufferProto,
ArrayBufferProto,
@@ -329,6 +330,7 @@ public:
Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); }
Object *promisePrototype() const { return reinterpret_cast<Object *>(jsObjects + PromiseProto); }
Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); }
+ Object *variantAssociationPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantAssociationProto); }
Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); }
Object *sharedArrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + SharedArrayBufferProto); }
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index b771a938e4..bd357cb2cb 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -228,6 +228,12 @@ static ReturnedValue loadProperty(
return scope.engine->fromData(
propMetaType, &v, wrapper, property.coreIndex(), referenceFlags(v4, property));
}
+ case QMetaType::QVariantHash: {
+ QVariantHash v;
+ property.readProperty(object, &v);
+ return scope.engine->fromData(
+ propMetaType, &v, wrapper, property.coreIndex(), referenceFlags(v4, property));
+ }
case QMetaType::QJsonValue: {
QJsonValue v;
property.readProperty(object, &v);
diff --git a/src/qml/jsruntime/qv4variantassociationobject.cpp b/src/qml/jsruntime/qv4variantassociationobject.cpp
new file mode 100644
index 0000000000..29ce23cf13
--- /dev/null
+++ b/src/qml/jsruntime/qv4variantassociationobject.cpp
@@ -0,0 +1,377 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qv4variantassociationobject_p.h"
+
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+template<typename Return, typename MapCallable, typename HashCallable>
+Return visitVariantAssociation(
+ const QV4::Heap::VariantAssociationObject* association,
+ MapCallable&& mapCallable,
+ HashCallable&& hashCallable
+) {
+ switch (association->m_type) {
+ case QV4::Heap::VariantAssociationObject::AssociationType::VariantMap:
+ return std::invoke(
+ std::forward<MapCallable>(mapCallable),
+ reinterpret_cast<const QVariantMap *>(&association->m_variantAssociation));
+ case QV4::Heap::VariantAssociationObject::AssociationType::VariantHash:
+ return std::invoke(
+ std::forward<HashCallable>(hashCallable),
+ reinterpret_cast<const QVariantHash *>(&association->m_variantAssociation));
+ default: Q_UNREACHABLE();
+ };
+}
+
+template<typename Return, typename MapCallable, typename HashCallable>
+Return visitVariantAssociation(
+ QV4::Heap::VariantAssociationObject* association,
+ MapCallable&& mapCallable,
+ HashCallable&& hashCallable
+) {
+ switch (association->m_type) {
+ case QV4::Heap::VariantAssociationObject::AssociationType::VariantMap:
+ return std::invoke(
+ std::forward<MapCallable>(mapCallable),
+ reinterpret_cast<QVariantMap *>(&association->m_variantAssociation));
+ case QV4::Heap::VariantAssociationObject::AssociationType::VariantHash:
+ return std::invoke(
+ std::forward<HashCallable>(hashCallable),
+ reinterpret_cast<QVariantHash *>(&association->m_variantAssociation));
+ default: Q_UNREACHABLE();
+ };
+}
+
+template<typename Return, typename Callable>
+Return visitVariantAssociation(
+ const QV4::Heap::VariantAssociationObject* association,
+ Callable&& callable
+) {
+ return visitVariantAssociation<Return>(
+ association,
+ std::forward<Callable>(callable),
+ std::forward<Callable>(callable));
+}
+
+template<typename Return, typename Callable>
+Return visitVariantAssociation(
+ QV4::Heap::VariantAssociationObject* association,
+ Callable&& callable
+) {
+ return visitVariantAssociation<Return>(
+ association,
+ std::forward<Callable>(callable),
+ std::forward<Callable>(callable));
+}
+
+static void mapPropertyKey(std::vector<QString>& mapping, const QString& key) {
+ auto it = std::find(mapping.cbegin(), mapping.cend(), key);
+ if (it == mapping.cend())
+ mapping.emplace_back(key);
+}
+
+static int keyToIndex(const std::vector<QString>& mapping, const QString& key) {
+ auto it = std::find(mapping.cbegin(), mapping.cend(), key);
+ return (it == mapping.cend()) ? -1 : std::distance(mapping.cbegin(), it);
+}
+
+static const QString& indexToKey(const std::vector<QString>& mapping, int index) {
+ Q_ASSERT(index >= 0);
+ Q_ASSERT(index < static_cast<int>(mapping.size()));
+ return mapping[index];
+}
+
+namespace QV4 {
+
+ DEFINE_OBJECT_VTABLE(VariantAssociationObject);
+
+ ReturnedValue VariantAssociationPrototype::fromQVariantMap(
+ ExecutionEngine *engine,
+ const QVariantMap& variantMap,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags)
+ {
+ return engine->memoryManager->allocate<VariantAssociationObject>(
+ variantMap, container, property, flags)->asReturnedValue();
+ }
+
+ ReturnedValue VariantAssociationPrototype::fromQVariantHash(
+ ExecutionEngine *engine,
+ const QVariantHash& variantHash,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags)
+ {
+ return engine->memoryManager->allocate<VariantAssociationObject>(
+ variantHash, container, property, flags)->asReturnedValue();
+ }
+
+ namespace Heap {
+ void VariantAssociationObject::init(
+ const QVariantMap& variantMap,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags)
+ {
+ ReferenceObject::init(container, property, flags);
+
+ new(m_variantAssociation) QVariantMap(variantMap);
+ m_type = AssociationType::VariantMap;
+
+ propertyIndexMapping = new std::vector<QString>(variantMap.keyBegin(), variantMap.keyEnd());
+ }
+
+ void VariantAssociationObject::init(
+ const QVariantHash& variantHash,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags)
+ {
+ ReferenceObject::init(container, property, flags);
+
+ new(m_variantAssociation) QVariantHash(variantHash);
+ m_type = AssociationType::VariantHash;
+
+ propertyIndexMapping = new std::vector<QString>(variantHash.keyBegin(), variantHash.keyEnd());
+ }
+
+ void VariantAssociationObject::destroy() {
+ visitVariantAssociation<void>(
+ this,
+ std::destroy_at<QVariantMap>,
+ std::destroy_at<QVariantHash>);
+ delete propertyIndexMapping;
+ ReferenceObject::destroy();
+ }
+
+ QVariant VariantAssociationObject::toVariant() const
+ {
+ return visitVariantAssociation<QVariant>(
+ this, [](auto association){ return QVariant(*association); });
+ }
+
+ bool VariantAssociationObject::setVariant(const QVariant &variant)
+ {
+ auto metatypeId = variant.metaType().id();
+
+ if (metatypeId != QMetaType::QVariantMap && metatypeId != QMetaType::QVariantHash)
+ return false;
+
+ if (metatypeId == QMetaType::QVariantMap && m_type == AssociationType::VariantMap) {
+ *reinterpret_cast<QVariantMap *>(&m_variantAssociation) = variant.toMap();
+ } else if (metatypeId == QMetaType::QVariantMap && m_type == AssociationType::VariantHash) {
+ std::destroy_at(reinterpret_cast<QVariantHash *>(&m_variantAssociation));
+ new(m_variantAssociation) QVariantMap(variant.toMap());
+ m_type = AssociationType::VariantMap;
+ } else if (metatypeId == QMetaType::QVariantHash && m_type == AssociationType::VariantHash) {
+ *reinterpret_cast<QVariantHash *>(&m_variantAssociation) = variant.toHash();
+ } else if (metatypeId == QMetaType::QVariantHash && m_type == AssociationType::VariantMap) {
+ std::destroy_at(reinterpret_cast<QVariantMap *>(&m_variantAssociation));
+ new(m_variantAssociation) QVariantHash(variant.toHash());
+ m_type = AssociationType::VariantHash;
+ }
+
+ auto keys = visitVariantAssociation<QStringList>(
+ this, [](auto* association){ return association->keys(); });
+ for (const QString& key : keys)
+ mapPropertyKey(*propertyIndexMapping, key);
+
+ return true;
+ }
+
+ VariantAssociationObject *VariantAssociationObject::detached() const
+ {
+ return visitVariantAssociation<VariantAssociationObject*>(
+ this,
+ [engine = internalClass->engine](auto association){
+ return engine->memoryManager->allocate<QV4::VariantAssociationObject>(
+ *association, nullptr, -1, ReferenceObject::Flag::NoFlag);
+ }
+ );
+ }
+
+ } // namespace Heap
+
+ ReturnedValue VariantAssociationObject::virtualGet(const Managed *that, PropertyKey id, const Value *, bool * hasProperty)
+ {
+ QString key = id.toQString();
+ return static_cast<const VariantAssociationObject *>(that)->getElement(key, hasProperty);
+
+ }
+
+ bool VariantAssociationObject::virtualPut(Managed *that, PropertyKey id, const Value &value, Value *)
+ {
+ QString key = id.toQString();
+ return static_cast<VariantAssociationObject *>(that)->putElement(key, value);
+ }
+
+ bool VariantAssociationObject::virtualDeleteProperty(Managed *that, PropertyKey id)
+ {
+ QString key = id.toQString();
+ return static_cast<VariantAssociationObject *>(that)->deleteElement(key);
+ }
+
+ OwnPropertyKeyIterator *VariantAssociationObject::virtualOwnPropertyKeys(
+ const Object *m, Value *target
+ ) {
+ struct VariantAssociationOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+ {
+ QStringList keys;
+
+ ~VariantAssociationOwnPropertyKeyIterator() override = default;
+
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override
+ {
+ const VariantAssociationObject *variantAssociation =
+ static_cast<const VariantAssociationObject *>(o);
+
+ if (memberIndex == 0) {
+ keys = variantAssociation->keys();
+ keys.sort();
+ }
+
+ if (static_cast<qsizetype>(memberIndex) < keys.count()) {
+ Scope scope(variantAssociation->engine());
+ ScopedString propertyName(scope, scope.engine->newString(keys[memberIndex]));
+ ScopedPropertyKey id(scope, propertyName->toPropertyKey());
+
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = variantAssociation->getElement(keys[memberIndex]);
+
+ ++memberIndex;
+
+ return id;
+ }
+
+ return PropertyKey::invalid();
+ }
+ };
+
+ QV4::ReferenceObject::readReference(static_cast<const VariantAssociationObject *>(m)->d());
+
+ *target = *m;
+ return new VariantAssociationOwnPropertyKeyIterator;
+ }
+
+ PropertyAttributes VariantAssociationObject::virtualGetOwnProperty(
+ const Managed *m, PropertyKey id, Property *p
+ ) {
+ auto variantAssociation = static_cast<const VariantAssociationObject *>(m);
+
+ bool hasElement = false;
+ Scope scope(variantAssociation->engine());
+ ScopedValue element(scope, variantAssociation->getElement(id.toQString(), &hasElement));
+
+ if (!hasElement)
+ return Attr_Invalid;
+
+ if (p)
+ p->value = element->asReturnedValue();
+
+ return Attr_Data;
+ }
+
+ int VariantAssociationObject::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
+ {
+ VariantAssociationObject *variantAssociation = static_cast<VariantAssociationObject *>(object);
+ Q_ASSERT(variantAssociation);
+
+ Heap::VariantAssociationObject *heapAssociation = variantAssociation->d();
+
+ switch (call) {
+ case QMetaObject::ReadProperty: {
+ QV4::ReferenceObject::readReference(heapAssociation);
+
+ if (index < 0 || index >= static_cast<int>(heapAssociation->propertyIndexMapping->size()))
+ return 0;
+
+ const QString& key = indexToKey(*heapAssociation->propertyIndexMapping, index);
+
+ if (!visitVariantAssociation<bool>(heapAssociation, [key](auto association) {
+ return association->contains(key);
+ })) {
+ return 0;
+ }
+
+ visitVariantAssociation<void>(heapAssociation, [a, key](auto association) {
+ *static_cast<QVariant*>(a[0]) = association->value(key);
+ });
+
+ break;
+ }
+ case QMetaObject::WriteProperty: {
+ if (index < 0 || index >= static_cast<int>(heapAssociation->propertyIndexMapping->size()))
+ return 0;
+
+ const QString& key = indexToKey(*heapAssociation->propertyIndexMapping, index);
+
+ visitVariantAssociation<void>(heapAssociation, [a, key](auto association){
+ if (association->contains(key))
+ association->insert(key, *static_cast<QVariant*>(a[0]));
+ });
+
+ QV4::ReferenceObject::writeBack(heapAssociation);
+
+ break;
+ }
+ default:
+ return 0; // not supported
+ }
+
+ return -1;
+ }
+
+ QV4::ReturnedValue VariantAssociationObject::getElement(const QString& key, bool *hasProperty) const {
+ QV4::ReferenceObject::readReference(d());
+
+ return visitVariantAssociation<QV4::ReturnedValue>(
+ d(),
+ [engine = engine(), this, key, hasProperty](auto* association) {
+ bool hasElement = association->contains(key);
+ if (hasProperty)
+ *hasProperty = hasElement;
+ return engine->fromVariant(
+ association->value(key),
+ d(), hasElement ? keyToIndex(*d()->propertyIndexMapping, key) : -1,
+ Heap::ReferenceObject::Flag::CanWriteBack |
+ Heap::ReferenceObject::Flag::IsVariant);
+ }
+ );
+ }
+
+ bool VariantAssociationObject::putElement(const QString& key, const Value& value) {
+ Heap::VariantAssociationObject *heapAssociation = d();
+
+ visitVariantAssociation<void>(heapAssociation, [engine = engine(), value, key](auto association){
+ association->insert(key, engine->toVariant(value, QMetaType{}, false));
+ });
+
+ mapPropertyKey(*heapAssociation->propertyIndexMapping, key);
+
+ QV4::ReferenceObject::writeBack(heapAssociation);
+ return true;
+ }
+
+ bool VariantAssociationObject::deleteElement(const QString& key) {
+ bool result = visitVariantAssociation<bool>(d(), [key](auto association) {
+ return association->remove(key);
+ });
+
+ if (result)
+ QV4::ReferenceObject::writeBack(d());
+
+ return result;
+ }
+
+ QStringList VariantAssociationObject::keys() const {
+ return visitVariantAssociation<QStringList>(d(), [](auto association){
+ return association->keys();
+ });
+ }
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#include "moc_qv4variantassociationobject_p.cpp"
diff --git a/src/qml/jsruntime/qv4variantassociationobject_p.h b/src/qml/jsruntime/qv4variantassociationobject_p.h
new file mode 100644
index 0000000000..32c58aed2c
--- /dev/null
+++ b/src/qml/jsruntime/qv4variantassociationobject_p.h
@@ -0,0 +1,120 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QV4VARIANTASSOCIATIONOBJECT_P_H_
+#define QV4VARIANTASSOCIATIONOBJECT_P_H_
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4object_p.h>
+#include <private/qv4referenceobject_p.h>
+#include <private/qv4value_p.h>
+
+#include <QtCore/QVariantMap>
+#include <QtCore/QVariantHash>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+ struct Q_QML_EXPORT VariantAssociationPrototype : public QV4::Object
+ {
+ V4_PROTOTYPE(objectPrototype);
+
+ static ReturnedValue fromQVariantMap(
+ ExecutionEngine *engine,
+ const QVariantMap& variantMap,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags);
+
+ static ReturnedValue fromQVariantHash(
+ ExecutionEngine *engine,
+ const QVariantHash& variantHash,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags);
+ };
+
+ namespace Heap {
+
+ struct VariantAssociationObject : ReferenceObject
+ {
+ enum class AssociationType: quint8 {
+ VariantMap,
+ VariantHash
+ };
+
+ void init(
+ const QVariantMap& variantMap,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags);
+
+ void init(
+ const QVariantHash& variantHash,
+ QV4::Heap::Object* container,
+ int property, Heap::ReferenceObject::Flags flags);
+
+ void destroy();
+
+ void *storagePointer() { return &m_variantAssociation; }
+
+ QVariant toVariant() const;
+ bool setVariant(const QVariant &variant);
+
+ VariantAssociationObject *detached() const;
+
+ // The alignment calculation needs to be out of the
+ // `alignas` due to a GCC 8.3 bug (that at the time of
+ // writing is used on the QNX 7.1 platform).
+ // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94929
+ static constexpr auto alignment =
+ std::max(alignof(QVariantMap), alignof(QVariantHash));
+ alignas(alignment)
+ std::byte m_variantAssociation[std::max(sizeof(QVariantMap), sizeof(QVariantHash))];
+
+ std::vector<QString>* propertyIndexMapping;
+ AssociationType m_type;
+ };
+
+ } // namespace Heap
+
+ struct Q_QML_EXPORT VariantAssociationObject : public QV4::ReferenceObject
+ {
+ V4_OBJECT2(VariantAssociationObject, QV4::ReferenceObject);
+ V4_PROTOTYPE(variantAssociationPrototype);
+ V4_NEEDS_DESTROY
+
+ static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value,
+ Value *receiver);
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id,
+ const Value *receiver, bool *hasProperty);
+
+ static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id);
+
+ static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
+
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id,
+ Property *p);
+
+ static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a);
+
+ QV4::ReturnedValue getElement(const QString& id, bool *hasProperty = nullptr) const;
+ bool putElement(const QString& key, const Value& value);
+ bool deleteElement(const QString& key);
+
+ QStringList keys() const;
+ };
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4VARIANTASSOCIATIONOBJECT_P_H_
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 8388b64ae3..e0a11ac4aa 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -12,6 +12,7 @@
#include <private/qv4object_p.h>
#include <private/qv4variantobject_p.h>
+#include <private/qv4variantassociationobject_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4scopedvalue_p.h>
#include <private/qv4jscall_p.h>
@@ -1280,8 +1281,12 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
md->set(engine, id, QV4::ReferenceObject::detached(wrapper->d()));
} else if (const QV4::DateObject *date = value.as<QV4::DateObject>()) {
md->set(engine, id, QV4::ReferenceObject::detached(date->d()));
+ } else if (const QV4::VariantAssociationObject *association = value.as<QV4::VariantAssociationObject>()) {
+ md->set(engine, id, QV4::ReferenceObject::detached(association->d()));
} else {
- // TODO: We should have a virtualDetach if that list gets longer.
+ // TODO: We should have a virtualDetach to reduce the
+ // boilerplate and avoid having to add new cases when a new
+ // reference object is added.
Q_ASSERT(!value.as<QV4::ReferenceObject>());
md->set(engine, id, value);
}
diff --git a/src/qml/qqmlbuiltins_p.h b/src/qml/qqmlbuiltins_p.h
index 0dc12410df..c423f9894b 100644
--- a/src/qml/qqmlbuiltins_p.h
+++ b/src/qml/qqmlbuiltins_p.h
@@ -215,6 +215,14 @@ struct QQmlQVariantMapForeign
QML_EXTENDED_JAVASCRIPT(Object)
};
+struct QQmlQVariantHashForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QVariantHash)
+ QML_EXTENDED_JAVASCRIPT(Object)
+};
+
struct QQmlQint8Foreign
{
Q_GADGET