QtQml: Move Sequence's {load|store}Reference into the heap object

It's fine to call them from Sequence and its helper classes, but it
shouldn't be called from outside.

Pick-to: 6.10 6.9 6.8
Task-number: QTBUG-129972
Task-number: QTBUG-139025
Change-Id: I0bfe5d813ec8fdee6c814269df069935fe304ccd
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
This commit is contained in:
Ulf Hermann 2025-08-27 13:18:04 +02:00
parent 1f2c87f754
commit 48fc94ac74
2 changed files with 58 additions and 42 deletions

View File

@ -72,8 +72,9 @@ struct SequenceOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override
{
const Sequence *s = static_cast<const Sequence *>(o);
Heap::Sequence *p = s->d();
if (s->d()->isReference() && !s->loadReference())
if (p->isReference() && !p->loadReference())
return PropertyKey::invalid();
const qsizetype size = s->size();
@ -296,7 +297,8 @@ void Sequence::removeLast(qsizetype num)
ReturnedValue Sequence::containerGetIndexed(qsizetype index, bool *hasProperty) const
{
if (d()->isReference() && !loadReference())
Heap::Sequence *p = d();
if (p->isReference() && !p->loadReference())
return Encode::undefined();
if (index >= 0 && index < size()) {
@ -311,19 +313,20 @@ ReturnedValue Sequence::containerGetIndexed(qsizetype index, bool *hasProperty)
bool Sequence::containerPutIndexed(qsizetype index, const Value &value)
{
if (internalClass()->engine->hasException)
Heap::Sequence *p = d();
if (p->internalClass->engine->hasException)
return false;
if (d()->isReadOnly()) {
if (p->isReadOnly()) {
engine()->throwTypeError(QLatin1String("Cannot insert into a readonly container"));
return false;
}
if (d()->isReference() && !loadReference())
if (p->isReference() && !p->loadReference())
return false;
const qsizetype count = size();
const QMetaType valueType = d()->valueMetaType();
const QMetaType valueType = p->valueMetaType();
const QVariant element = ExecutionEngine::toVariant(value, valueType, false);
if (index < 0)
@ -341,16 +344,17 @@ bool Sequence::containerPutIndexed(qsizetype index, const Value &value)
append(element);
}
if (d()->object())
storeReference();
if (p->object())
p->storeReference();
return true;
}
bool Sequence::containerDeleteIndexedProperty(qsizetype index)
{
if (d()->isReadOnly())
Heap::Sequence *p = d();
if (p->isReadOnly())
return false;
if (d()->isReference() && !loadReference())
if (p->isReference() && !p->loadReference())
return false;
if (index < 0 || index >= size())
return false;
@ -359,8 +363,8 @@ bool Sequence::containerDeleteIndexedProperty(qsizetype index)
/* but we cannot, so we insert a default-value instead. */
replace(index, QVariant());
if (d()->object())
storeReference();
if (p->object())
p->storeReference();
return true;
}
@ -381,17 +385,17 @@ bool Sequence::containerIsEqualTo(Managed *other)
return false;
}
bool Sequence::loadReference() const
bool Heap::Sequence::loadReference()
{
Q_ASSERT(d()->object());
Q_ASSERT(object());
// If locations are enforced we only read once
return d()->enforcesLocation() || QV4::ReferenceObject::readReference(d());
return enforcesLocation() || QV4::ReferenceObject::readReference(this);
}
bool Sequence::storeReference()
bool Heap::Sequence::storeReference()
{
Q_ASSERT(d()->object());
return d()->isAttachedToProperty() && QV4::ReferenceObject::writeBack(d());
Q_ASSERT(object());
return isAttachedToProperty() && QV4::ReferenceObject::writeBack(this);
}
ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty)
@ -411,7 +415,8 @@ ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Va
qint64 Sequence::virtualGetLength(const Managed *m)
{
const Sequence *s = static_cast<const Sequence *>(m);
if (s->d()->isReference() && !s->loadReference())
Heap::Sequence *p = s->d();
if (p->isReference() && !p->loadReference())
return 0;
return s->size();
}
@ -455,32 +460,32 @@ OwnPropertyKeyIterator *Sequence::virtualOwnPropertyKeys(const Object *m, Value
int Sequence::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
{
Sequence *sequence = static_cast<Sequence *>(object);
Q_ASSERT(sequence);
Heap::Sequence *p = static_cast<Sequence *>(object)->d();
Q_ASSERT(p);
switch (call) {
case QMetaObject::ReadProperty: {
const QMetaType valueType = sequence->d()->valueMetaType();
if (sequence->d()->isReference() && !sequence->loadReference())
const QMetaType valueType = p->valueMetaType();
if (p->isReference() && !p->loadReference())
return 0;
const QMetaSequence metaSequence = sequence->d()->metaSequence();
const QMetaSequence metaSequence = p->metaSequence();
if (metaSequence.valueMetaType() != valueType)
return 0; // value metatype is not what the caller expects anymore.
const void *storagePointer = sequence->d()->storagePointer();
const void *storagePointer = p->storagePointer();
if (index < 0 || index >= metaSequence.size(storagePointer))
return 0;
metaSequence.valueAtIndex(storagePointer, index, a[0]);
break;
}
case QMetaObject::WriteProperty: {
void *storagePointer = sequence->d()->storagePointer();
const QMetaSequence metaSequence = sequence->d()->metaSequence();
void *storagePointer = p->storagePointer();
const QMetaSequence metaSequence = p->metaSequence();
if (index < 0 || index >= metaSequence.size(storagePointer))
return 0;
metaSequence.setValueAtIndex(storagePointer, index, a[0]);
if (sequence->d()->isReference())
sequence->storeReference();
if (p->isReference())
p->storeReference();
break;
}
default:
@ -498,7 +503,9 @@ QV4::ReturnedValue SequencePrototype::method_getLength(
if (!This)
THROW_TYPE_ERROR();
if (This->d()->isReference() && !This->loadReference())
Heap::Sequence *p = This->d();
if (p->isReference() && !p->loadReference())
return Encode::undefined();
const qsizetype size = This->size();
@ -516,6 +523,8 @@ QV4::ReturnedValue SequencePrototype::method_setLength(
if (!This)
THROW_TYPE_ERROR();
Heap::Sequence *p = This->d();
bool ok = false;
const quint32 argv0 = argc ? argv[0].asArrayLength(&ok) : 0;
if (!ok || !qIsAtMostSizetypeLimit(argv0)) {
@ -523,13 +532,13 @@ QV4::ReturnedValue SequencePrototype::method_setLength(
RETURN_UNDEFINED();
}
if (This->d()->isReadOnly())
if (p->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() && !This->loadReference())
if (p->isReference() && !p->loadReference())
RETURN_UNDEFINED();
/* Determine whether we need to modify the sequence */
@ -537,7 +546,7 @@ QV4::ReturnedValue SequencePrototype::method_setLength(
if (newCount == count) {
RETURN_UNDEFINED();
} else if (newCount > count) {
const QMetaType valueMetaType = This->d()->valueMetaType();
const QMetaType valueMetaType = p->valueMetaType();
/* according to ECMA262r3 we need to insert */
/* undefined values increasing length to newLength. */
/* We cannot, so we insert default-values instead. */
@ -550,8 +559,8 @@ QV4::ReturnedValue SequencePrototype::method_setLength(
}
/* write back if required. */
if (This->d()->object())
This->storeReference();
if (p->object())
p->storeReference();
RETURN_UNDEFINED();
}
@ -576,7 +585,9 @@ ReturnedValue SequencePrototype::method_shift(
if (!s)
return ArrayPrototype::method_shift(b, thisObject, argv, argc);
if (s->d()->isReference() && !s->loadReference())
Heap::Sequence *p = s->d();
if (p->isReference() && !p->loadReference())
RETURN_UNDEFINED();
const qsizetype len = s->size();
@ -585,8 +596,8 @@ ReturnedValue SequencePrototype::method_shift(
ScopedValue result(scope, scope.engine->fromVariant(s->shift()));
if (s->d()->object())
s->storeReference();
if (p->object())
p->storeReference();
return result->asReturnedValue();
}
@ -639,14 +650,14 @@ ReturnedValue SequencePrototype::fromData(
QVariant SequencePrototype::toVariant(const Sequence *object)
{
Q_ASSERT(object->isV4SequenceType());
const auto *p = object->d();
Heap::Sequence *p = object->d();
// Note: For historical reasons, we ignore the result of loadReference()
// here. This allows us to retain sequences whose objects have vaninshed
// as "var" properties. It comes at the price of potentially returning
// outdated data. This is the behavior sequences have always shown.
if (p->isReference())
object->loadReference();
p->loadReference();
if (!p->hasData())
return QVariant();

View File

@ -28,6 +28,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct Sequence;
struct SequenceOwnPropertyKeyIterator;
struct Q_QML_EXPORT SequencePrototype : public QV4::Object
{
V4_PROTOTYPE(arrayPrototype)
@ -90,10 +92,15 @@ struct Sequence : ReferenceObject
QMetaSequence metaSequence() const { return QMetaSequence(m_metaSequence); }
private:
friend struct QV4::Sequence;
friend struct QV4::SequencePrototype;
friend struct QV4::SequenceOwnPropertyKeyIterator;
void initTypes(QMetaType listType, QMetaSequence metaSequence);
bool loadReference();
bool storeReference();
void *m_container;
const QtPrivate::QMetaTypeInterface *m_listType;
const QtMetaContainerPrivate::QMetaSequenceInterface *m_metaSequence;
@ -129,8 +136,6 @@ public:
bool containerPutIndexed(qsizetype index, const QV4::Value &value);
bool containerDeleteIndexedProperty(qsizetype index);
bool containerIsEqualTo(Managed *other);
bool loadReference() const;
bool storeReference();
};
}