diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-09-27 10:21:55 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-10-13 00:18:35 +0200 |
commit | e1a0596924766d62459740ed0271114a7bb99851 (patch) | |
tree | 5ac17c73668016ca0c3870fa799c3497537cf671 /src/qml | |
parent | 1a0c4094e0fce8693a4653a9b3cb80f832865495 (diff) |
V4: Refactor QQmlValueType{Wrapper|Reference} and QV4::Sequence
Every QQmlValueTypeWrapper is potentially a reference now. Since most
were already before, the overhead of checking the vtables at every step
was dubious at best.
Extract a common base class that handles the reading and writing of
object properties employed in both value type references and sequences.
Task-number: QTBUG-99766
Change-Id: Idf72d9a20a52366e5c2d0dcd4b3a18072c0ccc41
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 32 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4referenceobject.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4referenceobject_p.h | 182 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject.cpp | 176 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject_p.h | 33 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 202 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper_p.h | 63 |
8 files changed, 424 insertions, 275 deletions
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index ee70d67d2c..24c18daef5 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -229,6 +229,7 @@ qt_internal_add_qml_module(Qml jsruntime/qv4vme_moth.cpp jsruntime/qv4vme_moth_p.h jsruntime/qv4sequenceobject.cpp jsruntime/qv4sequenceobject_p.h jsruntime/qv4vtable_p.h + jsruntime/qv4referenceobject.cpp jsruntime/qv4referenceobject_p.h memory/qv4heap_p.h memory/qv4mm.cpp memory/qv4mm_p.h memory/qv4mmdefs_p.h diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 8103bb5ad9..0755dff34d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -2218,9 +2218,9 @@ ReturnedValue QObjectMethod::create( Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope); if (cloneFrom->valueTypeWrapper) { - Scoped<QQmlValueTypeReference> ref(valueScope, cloneFrom->valueTypeWrapper); + Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->valueTypeWrapper); if (ref) { - valueTypeWrapper = QQmlValueTypeReference::create(engine, ref->d(), object); + valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), object); } else { // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what // value we should operate on. Without knowledge of the property the value @@ -2271,14 +2271,8 @@ bool Heap::QObjectMethod::isDetached() const if (qObj.isValid()) return false; - if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) { - const VTable *vt = wrapper->internalClass->vtable; - while (vt && vt != QV4::QQmlValueTypeWrapper::staticVTable()) { - if (vt == QV4::QQmlValueTypeReference::staticVTable()) - return static_cast<Heap::QQmlValueTypeReference *>(wrapper)->object.isNull(); - vt = vt->parent; - } - } + if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) + return wrapper->object() == nullptr; return true; } @@ -2288,17 +2282,8 @@ bool Heap::QObjectMethod::isAttachedTo(QObject *o) const if (qObj.isValid() && qObj != o) return false; - if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) { - const VTable *vt = wrapper->internalClass->vtable; - while (vt && vt != QV4::QQmlValueTypeWrapper::staticVTable()) { - if (vt == QV4::QQmlValueTypeReference::staticVTable()) { - if (static_cast<Heap::QQmlValueTypeReference *>(wrapper)->object != o) - return false; - break; - } - vt = vt->parent; - } - } + if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) + return wrapper->object() == o; return true; } @@ -2455,10 +2440,9 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * // The method might change the value. const auto doCall = [&](const auto &call) { if (!method->isConstant()) { - Scoped<QQmlValueTypeReference> ref(scope, d()->valueTypeWrapper); - if (ref) { + if (d()->valueTypeWrapper && d()->valueTypeWrapper->isReference()) { ScopedValue rv(scope, call()); - ref->d()->writeBack(); + d()->valueTypeWrapper->writeBack(); return rv->asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4referenceobject.cpp b/src/qml/jsruntime/qv4referenceobject.cpp new file mode 100644 index 0000000000..af6ee60abc --- /dev/null +++ b/src/qml/jsruntime/qv4referenceobject.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2022 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 <private/qv4referenceobject_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_OBJECT_VTABLE(QV4::ReferenceObject); + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4referenceobject_p.h b/src/qml/jsruntime/qv4referenceobject_p.h new file mode 100644 index 0000000000..3dfea409db --- /dev/null +++ b/src/qml/jsruntime/qv4referenceobject_p.h @@ -0,0 +1,182 @@ +// Copyright (C) 2022 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 QV4REFERENCEOBJECT_P_H +#define QV4REFERENCEOBJECT_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> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace Heap { + + +#define ReferenceObjectMembers(class, Member) + +DECLARE_HEAP_OBJECT(ReferenceObject, Object) { + DECLARE_MARKOBJECTS(ReferenceObject); + + enum Flag : quint8 { + NoFlag = 0, + CanWriteBack = 1 << 0, + IsVariant = 1 << 1, + }; + Q_DECLARE_FLAGS(Flags, Flag); + + void init() + { + Object::init(); + m_object.init(); + } + + void destroy() + { + m_object.destroy(); + Object::destroy(); + } + + void setObject(QObject *object) { m_object = object; } + QObject *object() const { return m_object; } + + void setProperty(int property) { m_property = property; } + int property() const { return m_property; } + + void setCanWriteBack(bool canWriteBack) { setFlag(CanWriteBack, canWriteBack); } + bool canWriteBack() const { return hasFlag(CanWriteBack); } + + void setIsVariant(bool isVariant) { setFlag(IsVariant, isVariant); } + bool isVariant() const { return hasFlag(IsVariant); } + + bool isReference() const { return m_property >= 0; } + +private: + + bool hasFlag(Flag flag) const + { + return m_flags & quint8(flag); + } + + void setFlag(Flag flag, bool set) + { + m_flags = set ? (m_flags | quint8(flag)) : (m_flags & ~quint8(flag)); + } + + QV4QPointer<QObject> m_object; + int m_property; + quint8 m_flags; +}; + +} // namespace Heap + + +struct ReferenceObject : public Object +{ + V4_OBJECT2(ReferenceObject, Object) + V4_NEEDS_DESTROY + +private: + static bool doRead(QObject *o, int property, void **a) + { + return QMetaObject::metacall(o, QMetaObject::ReadProperty, property, a); + } + + template<typename HeapObject> + static bool doReadReference(const QMetaObject *metaObject, QObject *object, HeapObject *ref) + { + // A reference resource may be either a "true" reference (eg, to a QVector3D property) + // or a "variant" reference (eg, to a QVariant property which happens to contain + // a value-type). + QMetaProperty referenceProperty = metaObject->property(ref->property()); + ref->setCanWriteBack(referenceProperty.isWritable()); + + if (referenceProperty.userType() == QMetaType::QVariant) { + // variant-containing-value-type reference + QVariant variantReferenceValue; + + void *a[] = { &variantReferenceValue, nullptr }; + if (doRead(object, ref->property(), a) + && ref->setVariant(variantReferenceValue)) { + ref->setIsVariant(true); + return true; + } + return false; + } + + // value-type reference + void *args[] = { ref->storagePointer(), nullptr }; + return doRead(object, ref->property(), args); + } + + template<typename HeapObject> + static bool readObjectProperty(HeapObject *ref, QObject *object) + { + return doReadReference(object->metaObject(), object, ref); + } + + static bool doWrite(QObject *o, int property, void **a) + { + return QMetaObject::metacall(o, QMetaObject::WriteProperty, property, a); + } + + template<typename HeapObject> + static bool doWriteBack(const QMetaObject *metaObject, QObject *object, HeapObject *ref) + { + const QMetaProperty writebackProperty = metaObject->property(ref->property()); + Q_ASSERT(writebackProperty.isWritable()); + + int flags = 0; + int status = -1; + if (writebackProperty.metaType() == QMetaType::fromType<QVariant>()) { + QVariant variantReferenceValue = ref->toVariant(); + void *a[] = { &variantReferenceValue, nullptr, &status, &flags }; + return doWrite(object, ref->property(), a); + } + + void *a[] = { ref->storagePointer(), nullptr, &status, &flags }; + return doWrite(object, ref->property(), a); + } + + template<typename HeapObject> + static bool writeBackObjectProperty(HeapObject *ref, QObject *object) + { + return doWriteBack(object->metaObject(), object, ref); + } + +public: + + template<typename HeapObject> + static bool readReference(HeapObject *self) + { + if (!self->object()) + return false; + + return readObjectProperty(self, self->object()); + } + + template<typename HeapObject> + static bool writeBack(HeapObject *self) + { + if (!self->object() || !self->canWriteBack()) + return false; + + return writeBackObjectProperty(self, self->object()); + } +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4REFERENCEOBJECT_P_H diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 4415ca4d52..2558e376b5 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -23,16 +23,16 @@ DEFINE_OBJECT_VTABLE(Sequence); static const QMetaSequence *metaSequence(const Heap::Sequence *p) { - return p->typePrivate->extraData.ld; + return p->typePrivate()->extraData.ld; } template<typename Compare> void sortSequence(Sequence *sequence, const Compare &compare) { - const auto *p = sequence->d(); + auto *p = sequence->d(); const auto *m = metaSequence(p); - QSequentialIterable iterable(*m, p->typePrivate->listId, p->container); + QSequentialIterable iterable(*m, p->typePrivate()->listId, p->storagePointer()); if (iterable.canRandomAccessIterate()) { std::sort(QSequentialIterable::RandomAccessIterator(iterable.mutableBegin()), QSequentialIterable::RandomAccessIterator(iterable.mutableEnd()), @@ -69,8 +69,8 @@ struct SequenceOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator { const Sequence *s = static_cast<const Sequence *>(o); - if (s->d()->isReference) { - if (!s->d()->object) + if (s->d()->isReference()) { + if (!s->d()->object()) return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); s->loadReference(); } @@ -132,17 +132,15 @@ struct SequenceDefaultCompareFunctor void Heap::Sequence::init(const QQmlType &qmlType, const void *container) { - Object::init(); + ReferenceObject::init(); Q_ASSERT(qmlType.isSequentialContainer()); - typePrivate = qmlType.priv(); - QQmlType::refHandle(typePrivate); + m_typePrivate = qmlType.priv(); + QQmlType::refHandle(m_typePrivate); - this->container = typePrivate->listId.create(container); - propertyIndex = -1; - isReference = false; - isReadOnly = false; - object.init(); + m_container = m_typePrivate->listId.create(container); + setProperty(-1); + m_isReadOnly = false; QV4::Scope scope(internalClass->engine); QV4::Scoped<QV4::Sequence> o(scope, this); @@ -152,16 +150,15 @@ void Heap::Sequence::init(const QQmlType &qmlType, const void *container) void Heap::Sequence::init( QObject *object, int propertyIndex, const QQmlType &qmlType, bool readOnly) { - Object::init(); + ReferenceObject::init(); Q_ASSERT(qmlType.isSequentialContainer()); - typePrivate = qmlType.priv(); - QQmlType::refHandle(typePrivate); - container = QMetaType(typePrivate->listId).create(); - this->propertyIndex = propertyIndex; - isReference = true; - this->isReadOnly = readOnly; - this->object.init(object); + m_typePrivate = qmlType.priv(); + QQmlType::refHandle(m_typePrivate); + m_container = QMetaType(m_typePrivate->listId).create(); + setProperty(propertyIndex); + m_isReadOnly = readOnly; + setObject(object); QV4::Scope scope(internalClass->engine); QV4::Scoped<QV4::Sequence> o(scope, this); o->setArrayType(Heap::ArrayData::Custom); @@ -170,21 +167,49 @@ void Heap::Sequence::init( void Heap::Sequence::destroy() { - typePrivate->listId.destroy(container); - QQmlType::derefHandle(typePrivate); - object.destroy(); - Object::destroy(); + m_typePrivate->listId.destroy(m_container); + QQmlType::derefHandle(m_typePrivate); + ReferenceObject::destroy(); +} + +bool Heap::Sequence::setVariant(const QVariant &variant) +{ + const QMetaType variantReferenceType = variant.metaType(); + if (variantReferenceType != m_typePrivate->listId) { + // 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 + // possible, or return false if it is not a sequence. + const QQmlType newType = QQmlMetaType::qmlListType(variantReferenceType); + if (newType.isSequentialContainer()) { + 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()); + return true; + } else { + return false; + } + } + variantReferenceType.destruct(m_container); + variantReferenceType.construct(m_container, variant.constData()); + return true; +} +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 p->typePrivate()->typeId; } qsizetype Sequence::size() const { const auto *p = d(); - return metaSequence(p)->size(p->container); + return metaSequence(p)->size(p->storagePointer()); } QVariant Sequence::at(qsizetype index) const @@ -193,10 +218,10 @@ QVariant Sequence::at(qsizetype index) const const QMetaType v = valueMetaType(p); QVariant result; if (v == QMetaType::fromType<QVariant>()) { - metaSequence(p)->valueAtIndex(p->container, index, &result); + metaSequence(p)->valueAtIndex(p->storagePointer(), index, &result); } else { result = QVariant(v); - metaSequence(p)->valueAtIndex(p->container, index, result.data()); + metaSequence(p)->valueAtIndex(p->storagePointer(), index, result.data()); } return result; } @@ -219,18 +244,18 @@ void convertAndDo(const QVariant &item, const QMetaType v, Action action) void Sequence::append(const QVariant &item) { - const Heap::Sequence *p = d(); + Heap::Sequence *p = d(); convertAndDo(item, valueMetaType(p), [p](const void *data) { - metaSequence(p)->addValueAtEnd(p->container, data); + metaSequence(p)->addValueAtEnd(p->storagePointer(), data); }); } void Sequence::append(qsizetype num, const QVariant &item) { - const Heap::Sequence *p = d(); + Heap::Sequence *p = d(); convertAndDo(item, valueMetaType(p), [p, num](const void *data) { const QMetaSequence *m = metaSequence(p); - void *container = p->container; + void *container = p->storagePointer(); for (qsizetype i = 0; i < num; ++i) m->addValueAtEnd(container, data); }); @@ -238,40 +263,40 @@ void Sequence::append(qsizetype num, const QVariant &item) void Sequence::replace(qsizetype index, const QVariant &item) { - const Heap::Sequence *p = d(); + Heap::Sequence *p = d(); convertAndDo(item, valueMetaType(p), [p, index](const void *data) { - metaSequence(p)->setValueAtIndex(p->container, index, data); + metaSequence(p)->setValueAtIndex(p->storagePointer(), index, data); }); } void Sequence::removeLast(qsizetype num) { - const auto *p = d(); + auto *p = d(); const auto *m = metaSequence(p); if (m->canEraseRangeAtIterator() && m->hasRandomAccessIterator() && num > 1) { - void *i = m->end(p->container); + void *i = m->end(p->storagePointer()); m->advanceIterator(i, -num); - void *j = m->end(p->container); - m->eraseRangeAtIterator(p->container, i, j); + 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->container); + m->removeValueAtEnd(p->storagePointer()); } } QVariant Sequence::toVariant() const { const auto *p = d(); - return QVariant(p->typePrivate->listId, p->container); + return QVariant(p->typePrivate()->listId, p->storagePointer()); } ReturnedValue Sequence::containerGetIndexed(qsizetype index, bool *hasProperty) const { - if (d()->isReference) { - if (!d()->object) { + if (d()->isReference()) { + if (!d()->object()) { if (hasProperty) *hasProperty = false; return Encode::undefined(); @@ -293,13 +318,13 @@ bool Sequence::containerPutIndexed(qsizetype index, const Value &value) if (internalClass()->engine->hasException) return false; - if (d()->isReadOnly) { + if (d()->isReadOnly()) { engine()->throwTypeError(QLatin1String("Cannot insert into a readonly container")); return false; } - if (d()->isReference) { - if (!d()->object) + if (d()->isReference()) { + if (!d()->object()) return false; loadReference(); } @@ -323,7 +348,7 @@ bool Sequence::containerPutIndexed(qsizetype index, const Value &value) append(element); } - if (d()->isReference) + if (d()->isReference()) storeReference(); return true; } @@ -336,10 +361,10 @@ SequenceOwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value bool Sequence::containerDeleteIndexedProperty(qsizetype index) { - if (d()->isReadOnly) + if (d()->isReadOnly()) return false; - if (d()->isReference) { - if (!d()->object) + if (d()->isReference()) { + if (!d()->object()) return false; loadReference(); } @@ -351,7 +376,7 @@ bool Sequence::containerDeleteIndexedProperty(qsizetype index) /* but we cannot, so we insert a default-value instead. */ replace(index, QVariant()); - if (d()->isReference) + if (d()->isReference()) storeReference(); return true; @@ -364,9 +389,10 @@ bool Sequence::containerIsEqualTo(Managed *other) Sequence *otherSequence = other->as<Sequence>(); if (!otherSequence) return false; - if (d()->isReference && otherSequence->d()->isReference) { - return d()->object == otherSequence->d()->object && d()->propertyIndex == otherSequence->d()->propertyIndex; - } else if (!d()->isReference && !otherSequence->d()->isReference) { + if (d()->isReference() && otherSequence->d()->isReference()) { + return d()->object() == otherSequence->d()->object() + && d()->property() == otherSequence->d()->property(); + } else if (!d()->isReference() && !otherSequence->d()->isReference()) { return this == otherSequence; } return false; @@ -374,10 +400,10 @@ bool Sequence::containerIsEqualTo(Managed *other) bool Sequence::sort(const FunctionObject *f, const Value *, const Value *argv, int argc) { - if (d()->isReadOnly) + if (d()->isReadOnly()) return false; - if (d()->isReference) { - if (!d()->object) + if (d()->isReference()) { + if (!d()->object()) return false; loadReference(); } @@ -387,31 +413,27 @@ bool Sequence::sort(const FunctionObject *f, const Value *, const Value *argv, i else sortSequence(this, SequenceDefaultCompareFunctor()); - if (d()->isReference) + if (d()->isReference()) storeReference(); return true; } void *Sequence::getRawContainerPtr() const -{ return d()->container; } +{ return d()->storagePointer(); } void Sequence::loadReference() const { - Q_ASSERT(d()->object); - Q_ASSERT(d()->isReference); - void *a[] = { d()->container, nullptr }; - QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->propertyIndex, a); + Q_ASSERT(d()->object()); + Q_ASSERT(d()->isReference()); + QV4::ReferenceObject::readReference(d()); } void Sequence::storeReference() { - Q_ASSERT(d()->object); - Q_ASSERT(d()->isReference); - int status = -1; - QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding; - void *a[] = { d()->container, nullptr, &status, &flags }; - QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); + Q_ASSERT(d()->object()); + Q_ASSERT(d()->isReference()); + QV4::ReferenceObject::writeBack(d()); } ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty) @@ -471,8 +493,8 @@ static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value if (!This) THROW_TYPE_ERROR(); - if (This->d()->isReference) { - if (!This->d()->object) + if (This->d()->isReference()) { + if (!This->d()->object()) RETURN_RESULT(Encode(0)); This->loadReference(); } @@ -498,14 +520,14 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value RETURN_UNDEFINED(); } - if (This->d()->isReadOnly) + if (This->d()->isReadOnly()) THROW_TYPE_ERROR(); const qsizetype newCount = qsizetype(argv0); /* Read the sequence from the QObject property if we're a reference */ - if (This->d()->isReference) { - if (!This->d()->object) + if (This->d()->isReference()) { + if (!This->d()->object()) RETURN_UNDEFINED(); This->loadReference(); } @@ -528,7 +550,7 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value } /* write back if required. */ - if (This->d()->isReference) { + if (This->d()->isReference()) { /* write back. already checked that object is non-null, so skip that check here. */ This->storeReference(); } @@ -666,14 +688,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()->typePrivate()->listId == typeHint) return object->getRawContainerPtr(); return nullptr; } QMetaType SequencePrototype::metaTypeForSequence(const Sequence *object) { - return object->d()->typePrivate->listId; + return object->d()->typePrivate()->listId; } } // namespace QV4 diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 7280d563e3..6d0a85ded6 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -19,10 +19,9 @@ #include <QtCore/qvariant.h> #include <QtQml/qqml.h> -#include "qv4value_p.h" -#include "qv4object_p.h" -#include "qv4context_p.h" -#include "qv4string_p.h" +#include <private/qv4referenceobject_p.h> +#include <private/qv4value_p.h> +#include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -49,24 +48,32 @@ struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object namespace Heap { -struct Sequence : Object { +struct Sequence : ReferenceObject +{ void init(const QQmlType &qmlType, const void *container); void init(QObject *object, int propertyIndex, const QQmlType &qmlType, bool readOnly); void destroy(); - mutable void *container; - const QQmlTypePrivate *typePrivate; - QV4QPointer<QObject> object; - int propertyIndex; - bool isReference : 1; - bool isReadOnly : 1; + void *storagePointer() { return m_container; } + const void *storagePointer() const { return m_container; } + + bool setVariant(const QVariant &variant); + QVariant toVariant() const; + + const QQmlTypePrivate *typePrivate() const { return m_typePrivate; } + bool isReadOnly() const { return m_isReadOnly; } + +private: + void *m_container; + const QQmlTypePrivate *m_typePrivate; + bool m_isReadOnly; }; } -struct Q_QML_PRIVATE_EXPORT Sequence : public QV4::Object +struct Q_QML_PRIVATE_EXPORT Sequence : public QV4::ReferenceObject { - V4_OBJECT2(Sequence, QV4::Object) + V4_OBJECT2(Sequence, QV4::ReferenceObject) Q_MANAGED_TYPE(V4Sequence) V4_PROTOTYPE(sequencePrototype) V4_NEEDS_DESTROY diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index b30c3f426b..2e7eaf23f8 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -38,7 +38,6 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval) DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper); -DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeReference); namespace QV4 { @@ -48,7 +47,7 @@ void Heap::QQmlValueTypeWrapper::destroy() m_valueType->metaType.destruct(m_gadgetPtr); ::operator delete(m_gadgetPtr); } - Object::destroy(); + ReferenceObject::destroy(); } void Heap::QQmlValueTypeWrapper::setData(const void *data) const @@ -72,69 +71,68 @@ QVariant Heap::QQmlValueTypeWrapper::toVariant() const return QVariant(valueType()->metaType, gadgetPtr()); } -ReturnedValue QQmlValueTypeReference::create( - ExecutionEngine *engine, Heap::QQmlValueTypeReference *cloneFrom, QObject *object) +bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant) +{ + const QMetaType variantReferenceType = variant.metaType(); + if (variantReferenceType != valueType()->metaType) { + // This is a stale VariantReference. That is, the variant has been + // overwritten with a different type in the meantime. + // We need to modify this reference to the updated value type, if + // possible, or return false if it is not a value type. + if (QQmlMetaType::isValueType(variantReferenceType)) { + const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(variantReferenceType); + if (gadgetPtr()) { + valueType()->metaType.destruct(gadgetPtr()); + ::operator delete(gadgetPtr()); + } + setGadgetPtr(nullptr); + setMetaObject(mo); + setValueType(QQmlMetaType::valueType(variantReferenceType)); + if (!mo) + return false; + } else { + return false; + } + } + setValue(variant); + return true; +} + +void *Heap::QQmlValueTypeWrapper::storagePointer() +{ + if (!gadgetPtr()) { + setGadgetPtr(::operator new(valueType()->metaType.sizeOf())); + valueType()->metaType.construct(gadgetPtr(), nullptr); + } + return gadgetPtr(); +} + +bool Heap::QQmlValueTypeWrapper::readReference() +{ + return QV4::ReferenceObject::readReference(this); +} + +bool Heap::QQmlValueTypeWrapper::writeBack() +{ + return QV4::ReferenceObject::writeBack(this); +} + +ReturnedValue QQmlValueTypeWrapper::create( + ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *cloneFrom, QObject *object) { Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>()); - r->d()->object = object; - r->d()->property = cloneFrom->property; + Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); + r->d()->setObject(object); + r->d()->setProperty(cloneFrom->property()); + r->d()->setCanWriteBack(cloneFrom->canWriteBack()); r->d()->setMetaObject(cloneFrom->metaObject()); r->d()->setValueType(cloneFrom->valueType()); r->d()->setGadgetPtr(nullptr); return r->asReturnedValue(); } -bool QQmlValueTypeReference::readReferenceValue() const -{ - if (!d()->object) - return false; - // A reference resource may be either a "true" reference (eg, to a QVector3D property) - // or a "variant" reference (eg, to a QVariant property which happens to contain a value-type). - QMetaProperty writebackProperty = d()->object->metaObject()->property(d()->property); - if (writebackProperty.userType() == QMetaType::QVariant) { - // variant-containing-value-type reference - QVariant variantReferenceValue; - - void *a[] = { &variantReferenceValue, nullptr }; - QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->property, a); - - const QMetaType variantReferenceType = variantReferenceValue.metaType(); - if (variantReferenceType != type()) { - // This is a stale VariantReference. That is, the variant has been - // overwritten with a different type in the meantime. - // We need to modify this reference to the updated value type, if - // possible, or return false if it is not a value type. - if (QQmlMetaType::isValueType(variantReferenceType)) { - const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(variantReferenceType); - if (d()->gadgetPtr()) { - d()->valueType()->metaType.destruct(d()->gadgetPtr()); - ::operator delete(d()->gadgetPtr()); - } - d()->setGadgetPtr(nullptr); - d()->setMetaObject(mo); - d()->setValueType(QQmlMetaType::valueType(variantReferenceType)); - if (!mo) - return false; - } else { - return false; - } - } - d()->setValue(variantReferenceValue); - } else { - if (!d()->gadgetPtr()) { - d()->setGadgetPtr(::operator new(d()->valueType()->metaType.sizeOf())); - d()->valueType()->metaType.construct(d()->gadgetPtr(), nullptr); - } - // value-type reference - void *args[] = { d()->gadgetPtr(), nullptr }; - QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->property, args); - } - return true; -} - void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4) { if (v4->valueTypeWrapperPrototype()->d_unchecked()) @@ -151,9 +149,9 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>()); - r->d()->object = object; - r->d()->property = property; + Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); + r->d()->setObject(object); + r->d()->setProperty(property); r->d()->setMetaObject(metaObject); auto valueType = QQmlMetaType::valueType(type); if (!valueType) { @@ -166,14 +164,6 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj } ReturnedValue QQmlValueTypeWrapper::create( - ExecutionEngine *engine, const QVariant &value, const QMetaObject *metaObject, - QMetaType type) -{ - Q_ASSERT(value.metaType() == QQmlMetaType::valueType(type)->metaType); - return create(engine, value.constData(), metaObject, type); -} - -ReturnedValue QQmlValueTypeWrapper::create( ExecutionEngine *engine, const void *data, const QMetaObject *metaObject, QMetaType type) { Scope scope(engine); @@ -186,6 +176,7 @@ ReturnedValue QQmlValueTypeWrapper::create( return engine->throwTypeError(QLatin1String("Type %1 is not a value type") .arg(QString::fromUtf8(type.name()))); } + r->d()->setProperty(-1); r->d()->setValueType(valueType); r->d()->setGadgetPtr(nullptr); r->d()->setData(data); @@ -194,17 +185,15 @@ ReturnedValue QQmlValueTypeWrapper::create( QVariant QQmlValueTypeWrapper::toVariant() const { - if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) - if (!ref->readReferenceValue()) - return QVariant(); + if (d()->isReference() && !readReferenceValue()) + return QVariant(); return d()->toVariant(); } bool QQmlValueTypeWrapper::toGadget(void *data) const { - if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) - if (!ref->readReferenceValue()) - return false; + if (d()->isReference() && !readReferenceValue()) + return false; const QMetaType type = d()->valueType()->metaType; type.destruct(data); type.construct(data, d()->gadgetPtr()); @@ -374,8 +363,7 @@ PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, if (!p) return Attr_Data; // Property exists, but we're not interested in the value - const QQmlValueTypeReference *ref = r->as<const QQmlValueTypeReference>(); - if (!ref || ref->readReferenceValue()) { + if (!r->d()->isReference() || r->readReferenceValue()) { // Property exists, and we can retrieve it p->value = getGadgetProperty( r->engine(), r->d(), result.propType(), result.coreIndex(), @@ -402,10 +390,8 @@ struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o); - if (const QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) { - if (!ref->readReferenceValue()) - return PropertyKey::invalid(); - } + if (that->d()->isReference() && !that->readReferenceValue()) + return PropertyKey::invalid(); const QMetaObject *mo = that->d()->metaObject(); // We don't return methods, ie. they are not visible when iterating @@ -437,9 +423,8 @@ OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Objec bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const { - if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) - if (!ref->readReferenceValue()) - return false; + if (d()->isReference() && !readReferenceValue()) + return false; int id1 = value.metaType().id(); QVariant v = d()->toVariant(); int id2 = v.metaType().id(); @@ -499,14 +484,14 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const { bool destructGadgetOnExit = false; Q_ALLOCA_DECLARE(void, gadget); - if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) { + if (d()->isReference()) { if (!d()->gadgetPtr()) { Q_ALLOCA_ASSIGN(void, gadget, d()->valueType()->metaType.sizeOf()); d()->setGadgetPtr(gadget); d()->valueType()->metaType.construct(d()->gadgetPtr(), nullptr); destructGadgetOnExit = true; } - if (!ref->readReferenceValue()) + if (!readReferenceValue()) return false; } @@ -549,9 +534,8 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con if (!w) return b->engine()->throwTypeError(); - if (const QQmlValueTypeReference *ref = w->as<QQmlValueTypeReference>()) - if (!ref->readReferenceValue()) - RETURN_UNDEFINED(); + if (w->d()->isReference() && !w->readReferenceValue()) + RETURN_UNDEFINED(); QString result; if (!QMetaType::convert(w->d()->valueType()->metaType, w->d()->gadgetPtr(), @@ -586,10 +570,8 @@ ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *obj ScopedString name(scope, id.asStringOrSymbol()); // Note: readReferenceValue() can change the reference->type. - if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { - if (!reference->readReferenceValue()) - return Value::undefinedValue().asReturnedValue(); - } + if (r->d()->isReference() && !r->readReferenceValue()) + return Value::undefinedValue().asReturnedValue(); QQmlPropertyData result = r->dataForPropertyKey(id); if (!result.isValid()) @@ -625,11 +607,8 @@ ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine if (valueTypeWrapper->metaObject() != reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1)) return revertLookup(); - if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) { - Scope scope(engine); - Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper); - referenceWrapper->readReferenceValue(); - } + if (valueTypeWrapper->isReference()) + valueTypeWrapper->readReference(); return getGadgetProperty(engine, valueTypeWrapper, QMetaType(lookup->qgadgetLookup.metaType), lookup->qgadgetLookup.coreIndex, lookup->qgadgetLookup.isFunction, lookup->qgadgetLookup.isEnum); } @@ -657,10 +636,8 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, QV4::ExecutionEngine *v4 = r->engine(); // Note: readReferenceValue() can change the reference->type. - if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { - if (!reference->readReferenceValue()) - return Value::undefinedValue().asReturnedValue(); - } + if (r->d()->isReference() && !r->readReferenceValue()) + return Value::undefinedValue().asReturnedValue(); QQmlPropertyData result = r->dataForPropertyKey(id); if (!result.isValid()) @@ -684,17 +661,11 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v return false; Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m)); - Scoped<QQmlValueTypeReference> reference(scope, m->d()); - - QMetaType writeBackPropertyType; - - if (reference) { - QMetaProperty writebackProperty = reference->d()->object->metaObject()->property(reference->d()->property); - - if (!writebackProperty.isWritable() || !reference->readReferenceValue()) + QObject *referenceObject = nullptr; + if (r->d()->isReference()) { + referenceObject = r->d()->object(); + if (!r->readReferenceValue() || !r->d()->canWriteBack()) return false; - - writeBackPropertyType = writebackProperty.metaType(); } const QMetaObject *metaObject = r->d()->metaObject(); @@ -702,10 +673,9 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v if (!pd.isValid()) return false; - if (reference) { + if (referenceObject) { QV4::ScopedFunctionObject f(scope, value); - const QV4QPointer<QObject> &referenceObject = reference->d()->object; - const int referencePropertyIndex = reference->d()->property; + const int referencePropertyIndex = r->d()->property(); if (f) { if (!f->isBinding()) { @@ -716,6 +686,10 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v return false; } + const QMetaProperty writebackProperty + = referenceObject->metaObject()->property(referencePropertyIndex); + const QMetaType writeBackPropertyType = writebackProperty.metaType(); + QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext(); QQmlPropertyData cacheData; @@ -764,8 +738,8 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v void *gadget = r->d()->gadgetPtr(); property.writeOnGadget(gadget, v); - if (reference) - reference->d()->writeBack(); + if (referenceObject) + r->d()->writeBack(); return true; } diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 9d96d68c50..c57e385a00 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -20,6 +20,7 @@ #include <private/qv4value_p.h> #include <private/qv4object_p.h> +#include <private/qv4referenceobject_p.h> #include <private/qqmlpropertycache_p.h> QT_BEGIN_NAMESPACE @@ -30,8 +31,11 @@ namespace QV4 { namespace Heap { -struct QQmlValueTypeWrapper : Object { - void init() { Object::init(); } +#define QQmlValueTypeWrapperMembers(class, Member) + +DECLARE_HEAP_OBJECT(QQmlValueTypeWrapper, ReferenceObject) { + DECLARE_MARKOBJECTS(QQmlValueTypeWrapper); + void destroy(); void setValueType(QQmlValueType *valueType) @@ -69,56 +73,30 @@ struct QQmlValueTypeWrapper : Object { void setValue(const QVariant &value) const; QVariant toVariant() const; + void *storagePointer(); + bool setVariant(const QVariant &variant); + + bool readReference(); + bool writeBack(); + private: mutable void *m_gadgetPtr; QQmlValueType *m_valueType; const QMetaObject *m_metaObject; }; -struct QQmlValueTypeReference : QQmlValueTypeWrapper -{ - void init() { - QQmlValueTypeWrapper::init(); - object.init(); - } - void destroy() { - object.destroy(); - QQmlValueTypeWrapper::destroy(); - } - - void writeBack() { - const QMetaProperty writebackProperty = object->metaObject()->property(property); - if (!writebackProperty.isWritable()) - return; - - int flags = 0; - int status = -1; - if (writebackProperty.metaType() == QMetaType::fromType<QVariant>()) { - QVariant variantReferenceValue = toVariant(); - void *a[] = { &variantReferenceValue, nullptr, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, property, a); - } else { - void *a[] = { gadgetPtr(), nullptr, &status, &flags }; - QMetaObject::metacall(object, QMetaObject::WriteProperty, property, a); - } - } - - QV4QPointer<QObject> object; - int property; -}; - } -struct Q_QML_EXPORT QQmlValueTypeWrapper : Object +struct Q_QML_EXPORT QQmlValueTypeWrapper : public ReferenceObject { - V4_OBJECT2(QQmlValueTypeWrapper, Object) + V4_OBJECT2(QQmlValueTypeWrapper, ReferenceObject) V4_PROTOTYPE(valueTypeWrapperPrototype) V4_NEEDS_DESTROY public: static ReturnedValue create(ExecutionEngine *engine, QObject *, int, const QMetaObject *metaObject, QMetaType type); - static ReturnedValue create(ExecutionEngine *engine, const QVariant &, const QMetaObject *metaObject, QMetaType type); + static ReturnedValue create(ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *cloneFrom, QObject *object); static ReturnedValue create(ExecutionEngine *engine, const void *, const QMetaObject *metaObject, QMetaType type); QVariant toVariant() const; @@ -127,6 +105,7 @@ public: int typeId() const; QMetaType type() const; bool write(QObject *target, int propertyIndex) const; + bool readReferenceValue() const { return d()->readReference(); } QQmlPropertyData dataForPropertyKey(PropertyKey id) const; @@ -146,16 +125,6 @@ public: static void initProto(ExecutionEngine *v4); }; -struct QQmlValueTypeReference : public QQmlValueTypeWrapper -{ - V4_OBJECT2(QQmlValueTypeReference, QQmlValueTypeWrapper) - V4_NEEDS_DESTROY - - static ReturnedValue create(ExecutionEngine *engine, Heap::QQmlValueTypeReference *cloneFrom, QObject *object); - - bool readReferenceValue() const; -}; - } QT_END_NAMESPACE |