diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/qml/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 48 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 2 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 6 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4variantassociationobject.cpp | 377 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4variantassociationobject_p.h | 120 | ||||
| -rw-r--r-- | src/qml/qml/qqmlvmemetaobject.cpp | 7 | ||||
| -rw-r--r-- | src/qml/qqmlbuiltins_p.h | 8 |
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 |
