aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-09-27 10:21:55 +0200
committerUlf Hermann <ulf.hermann@qt.io>2022-10-13 00:18:35 +0200
commite1a0596924766d62459740ed0271114a7bb99851 (patch)
tree5ac17c73668016ca0c3870fa799c3497537cf671 /src/qml
parent1a0c4094e0fce8693a4653a9b3cb80f832865495 (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.txt1
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp32
-rw-r--r--src/qml/jsruntime/qv4referenceobject.cpp10
-rw-r--r--src/qml/jsruntime/qv4referenceobject_p.h182
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp176
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h33
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp202
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h63
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