aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-08-02 15:44:12 +0200
committerLars Knoll <lars.knoll@qt.io>2018-08-02 20:25:41 +0000
commit0754f55287f4652382332bce42cd8c7d27846ef1 (patch)
tree490bbd3c5d66586a1427ba21dde76b2af65230dc /src/qml
parent57640959320c87850315eae44c783803d113dc85 (diff)
Introduce a new mechanism to iterate over object properties
The old advanceIterator schema was extremely ugly and in addition not flexible enough to support the requirements for Proxy.ownKeys and some of the methods in Object Implemented a new scheme through a OwnPropertyKeys method in the Object VTable that creates and returns an iterator object. Ported QJSValueIterator and for-in to use the new mechanism. There's still many places where we use the old ObjectIterator (that relies on advanceIterator). Those will be ported in subsequent commits. Change-Id: I091a9bea9ff6b2b63630cc336814700757a718be Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/jsapi/qjsvalueiterator.cpp132
-rw-r--r--src/qml/jsapi/qjsvalueiterator_p.h16
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4managed.cpp5
-rw-r--r--src/qml/jsruntime/qv4object.cpp72
-rw-r--r--src/qml/jsruntime/qv4object_p.h17
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp81
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h22
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp12
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h3
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp68
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h6
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp34
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp56
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h9
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp30
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h1
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp40
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h1
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp9
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h1
-rw-r--r--src/qml/types/qqmllistmodel.cpp38
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h3
25 files changed, 547 insertions, 117 deletions
diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp
index 35c6ecc668..5add9d4b0a 100644
--- a/src/qml/jsapi/qjsvalueiterator.cpp
+++ b/src/qml/jsapi/qjsvalueiterator.cpp
@@ -47,19 +47,50 @@
QT_BEGIN_NAMESPACE
QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v)
- : value(v)
- , currentIndex(UINT_MAX)
- , nextIndex(UINT_MAX)
{
+ init(v);
+}
+
+void QJSValueIteratorPrivate::init(const QJSValue &v)
+{
+ engine = nullptr;
+
QV4::ExecutionEngine *e = QJSValuePrivate::engine(&v);
if (!e)
return;
+ QV4::Object *o = QJSValuePrivate::getValue(&v)->objectValue();
+ if (!o)
+ return;
- QV4::Scope scope(e);
- QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&v));
- iterator.set(e, e->newForInIteratorObject(o));
+ engine = e;
+ object = o;
+ iterator.reset(o->ownPropertyKeys());
+ next();
}
+void QJSValueIteratorPrivate::next()
+{
+ QV4::Object *o = object.as<QV4::Object>();
+ if (!o)
+ return;
+
+ QV4::PropertyKey key;
+ while (1) {
+ key = iterator->next(o);
+ if (!key.isSymbol())
+ break;
+ }
+ currentKey = nextKey;
+ nextKey.set(engine, key.id());
+}
+
+bool QJSValueIteratorPrivate::isValid() const
+{
+ if (!engine)
+ return false;
+ QV4::Value *val = object.valueRef();
+ return (val && val->isObject());
+}
/*!
\class QJSValueIterator
@@ -98,17 +129,6 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v)
QJSValueIterator::QJSValueIterator(const QJSValue& object)
: d_ptr(new QJSValueIteratorPrivate(object))
{
- QV4::ExecutionEngine *v4 = d_ptr->iterator.engine();
- if (!v4)
- return;
- QV4::Scope scope(v4);
- QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value());
- it->d()->it().flags = QV4::ObjectIterator::NoFlags;
- QV4::ScopedString nm(scope);
- QV4::Property nextProperty;
- QV4::PropertyAttributes nextAttributes;
- it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
- d_ptr->nextName.set(v4, nm.asReturnedValue());
}
/*!
@@ -127,10 +147,9 @@ QJSValueIterator::~QJSValueIterator()
*/
bool QJSValueIterator::hasNext() const
{
- QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value);
- if (!val || !val->isObject())
+ if (!d_ptr->isValid())
return false;
- return d_ptr->nextName.as<QV4::String>() || d_ptr->nextIndex != UINT_MAX;
+ return QV4::PropertyKey::fromId(d_ptr->nextKey.value()).isValid();
}
/*!
@@ -143,23 +162,10 @@ bool QJSValueIterator::hasNext() const
*/
bool QJSValueIterator::next()
{
- QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value);
- if (!val || !val->isObject())
- return false;
- d_ptr->currentName = d_ptr->nextName;
- d_ptr->currentIndex = d_ptr->nextIndex;
-
- QV4::ExecutionEngine *v4 = d_ptr->iterator.engine();
- if (!v4)
+ if (!d_ptr->isValid())
return false;
- QV4::Scope scope(v4);
- QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value());
- QV4::ScopedString nm(scope);
- QV4::Property nextProperty;
- QV4::PropertyAttributes nextAttributes;
- it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
- d_ptr->nextName.set(v4, nm.asReturnedValue());
- return d_ptr->currentName.as<QV4::String>() || d_ptr->currentIndex != UINT_MAX;
+ d_ptr->next();
+ return QV4::PropertyKey::fromId(d_ptr->currentKey.value()).isValid();
}
/*!
@@ -170,14 +176,14 @@ bool QJSValueIterator::next()
*/
QString QJSValueIterator::name() const
{
- QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value);
- if (!val || !val->isObject())
+ if (!d_ptr->isValid())
return QString();
- if (QV4::String *s = d_ptr->currentName.as<QV4::String>())
- return s->toQString();
- if (d_ptr->currentIndex < UINT_MAX)
- return QString::number(d_ptr->currentIndex);
- return QString();
+ QV4::Scope scope(d_ptr->engine);
+ QV4::ScopedPropertyKey key(scope, QV4::PropertyKey::fromId(d_ptr->currentKey.value()));
+ if (!key->isValid())
+ return QString();
+ Q_ASSERT(!key->isSymbol());
+ return key->toStringOrSymbol(d_ptr->engine)->toQString();
}
@@ -189,23 +195,21 @@ QString QJSValueIterator::name() const
*/
QJSValue QJSValueIterator::value() const
{
- QV4::ExecutionEngine *engine = d_ptr->iterator.engine();
- if (!engine)
+ if (!d_ptr->isValid())
return QJSValue();
- QV4::Scope scope(engine);
- QV4::ScopedObject obj(scope, QJSValuePrivate::getValue(&d_ptr->value));
- if (!obj)
+ QV4::Scope scope(d_ptr->engine);
+ QV4::ScopedPropertyKey key(scope, QV4::PropertyKey::fromId(d_ptr->currentKey.value()));
+ if (!key->isValid())
return QJSValue();
- if (!d_ptr->currentName.as<QV4::String>() && d_ptr->currentIndex == UINT_MAX)
- return QJSValue();
+ QV4::ScopedObject obj(scope, d_ptr->object.asManaged());
+ QV4::ScopedValue val(scope, obj->get(key));
- QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->get(d_ptr->currentIndex));
if (scope.hasException()) {
- engine->catchException();
+ scope.engine->catchException();
return QJSValue();
}
- return QJSValue(engine, v->asReturnedValue());
+ return QJSValue(scope.engine, val->asReturnedValue());
}
@@ -216,27 +220,7 @@ QJSValue QJSValueIterator::value() const
*/
QJSValueIterator& QJSValueIterator::operator=(QJSValue& object)
{
- d_ptr->value = object;
- d_ptr->currentIndex = UINT_MAX;
- d_ptr->nextIndex = UINT_MAX;
- d_ptr->currentName.clear();
- d_ptr->nextName.clear();
- QV4::ExecutionEngine *v4 = d_ptr->iterator.engine();
- if (!v4) {
- d_ptr->iterator.clear();
- return *this;
- }
-
- QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&object));
- d_ptr->iterator.set(v4, v4->newForInIteratorObject(o));
- QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value());
- it->d()->it().flags = QV4::ObjectIterator::NoFlags;
- QV4::ScopedString nm(scope);
- QV4::Property nextProperty;
- QV4::PropertyAttributes nextAttributes;
- it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
- d_ptr->nextName.set(v4, nm.asReturnedValue());
+ d_ptr->init(object);
return *this;
}
diff --git a/src/qml/jsapi/qjsvalueiterator_p.h b/src/qml/jsapi/qjsvalueiterator_p.h
index 474a98b9fa..a870850c11 100644
--- a/src/qml/jsapi/qjsvalueiterator_p.h
+++ b/src/qml/jsapi/qjsvalueiterator_p.h
@@ -61,12 +61,16 @@ class QJSValueIteratorPrivate
public:
QJSValueIteratorPrivate(const QJSValue &v);
- QJSValue value;
- QV4::PersistentValue iterator;
- QV4::PersistentValue currentName;
- uint currentIndex;
- QV4::PersistentValue nextName;
- uint nextIndex;
+ void init(const QJSValue &v);
+ bool isValid() const;
+
+ void next();
+
+ QV4::ExecutionEngine *engine = nullptr;
+ QV4::PersistentValue object;
+ QScopedPointer<QV4::OwnPropertyKeyIterator> iterator;
+ QV4::PersistentValue currentKey;
+ QV4::PersistentValue nextKey;
};
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index cc2cb83b6b..6c1c4a6eea 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -212,3 +212,9 @@ qint64 ArgumentsObject::virtualGetLength(const Managed *m)
const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m);
return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength();
}
+
+OwnPropertyKeyIterator *ArgumentsObject::virtualOwnPropertyKeys(const Object *m)
+{
+ static_cast<ArgumentsObject *>(const_cast<Object *>(m))->fullyCreate();
+ return Object::virtualOwnPropertyKeys(m);
+}
diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h
index 85939071ed..6be1ab923f 100644
--- a/src/qml/jsruntime/qv4argumentsobject_p.h
+++ b/src/qml/jsruntime/qv4argumentsobject_p.h
@@ -106,6 +106,7 @@ struct ArgumentsObject: Object {
static bool virtualDeleteProperty(Managed *m, PropertyKey id);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
static qint64 virtualGetLength(const Managed *m);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
void fullyCreate();
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index bb7b8086e4..d51b03d90b 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -142,3 +142,8 @@ bool Managed::virtualIsEqualTo(Managed *, Managed *)
{
return false;
}
+
+
+OwnPropertyKeyIterator::~OwnPropertyKeyIterator()
+{
+}
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 701c22331d..f4604dbce7 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -408,6 +408,78 @@ void Object::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name,
*attrs = PropertyAttributes();
}
+PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ if (arrayIndex != UINT_MAX && o->arrayData()) {
+ if (!arrayIndex)
+ arrayNode = o->sparseBegin();
+
+ // sparse arrays
+ if (arrayNode) {
+ while (arrayNode != o->sparseEnd()) {
+ uint k = arrayNode->key();
+ uint pidx = arrayNode->value;
+ Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>();
+ const Property *p = reinterpret_cast<const Property *>(sa->values.data() + pidx);
+ arrayNode = arrayNode->nextNode();
+ PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data;
+ arrayIndex = k + 1;
+ if (pd)
+ pd->copy(p, a);
+ if (attrs)
+ *attrs = a;
+ return PropertyKey::fromArrayIndex(k);
+ }
+ arrayNode = nullptr;
+ arrayIndex = UINT_MAX;
+ }
+ // dense arrays
+ while (arrayIndex < o->d()->arrayData->values.size) {
+ Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>();
+ const Value &val = sa->data(arrayIndex);
+ PropertyAttributes a = o->arrayData()->attributes(arrayIndex);
+ int index = arrayIndex;
+ ++arrayIndex;
+ if (!val.isEmpty()) {
+ if (pd)
+ pd->value = val;
+ if (attrs)
+ *attrs = a;
+ return PropertyKey::fromArrayIndex(index);
+ }
+ }
+ arrayIndex = UINT_MAX;
+ }
+
+ while (memberIndex < o->internalClass()->size) {
+ PropertyKey n = o->internalClass()->nameMap.at(memberIndex);
+ if (!n.isStringOrSymbol()) {
+ // accessor properties have a dummy entry with n == 0
+ ++memberIndex;
+ continue;
+ }
+
+ uint index = memberIndex;
+ PropertyAttributes a = o->internalClass()->propertyData[memberIndex];
+ ++memberIndex;
+ if (pd) {
+ pd->value = *o->propertyData(index);
+ if (a.isAccessor())
+ pd->set = *o->propertyData(index + Object::SetterOffset);
+ }
+ if (attrs)
+ *attrs = a;
+ return n;
+ }
+
+ return PropertyKey::invalid();
+}
+
+OwnPropertyKeyIterator *Object::virtualOwnPropertyKeys(const Object *)
+{
+ return new ObjectOwnPropertyKeyIterator;
+}
+
// Section 8.12.3
ReturnedValue Object::internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const
{
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index a9ad926289..3f1eb2d537 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -280,8 +280,8 @@ public:
}
void initSparseArray();
- SparseArrayNode *sparseBegin() { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->begin() : nullptr; }
- SparseArrayNode *sparseEnd() { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->end() : nullptr; }
+ SparseArrayNode *sparseBegin() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->begin() : nullptr; }
+ SparseArrayNode *sparseEnd() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->end() : nullptr; }
inline bool protoHasArray() {
Scope scope(engine());
@@ -355,6 +355,8 @@ public:
{ return vtable()->deleteProperty(this, id); }
void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{ vtable()->advanceIterator(this, it, name, index, p, attributes); }
+ OwnPropertyKeyIterator *ownPropertyKeys() const
+ { return vtable()->ownPropertyKeys(this); }
qint64 getLength() const { return vtable()->getLength(this); }
ReturnedValue instanceOf(const Value &var) const
{ return vtable()->instanceOf(this, var); }
@@ -376,6 +378,7 @@ protected:
static Heap::Object *virtualGetPrototypeOf(const Managed *);
static bool virtualSetPrototypeOf(Managed *, const Object *);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
static qint64 virtualGetLength(const Managed *m);
static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
@@ -390,6 +393,16 @@ private:
friend struct ObjectPrototype;
};
+struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
+{
+ uint arrayIndex = 0;
+ uint memberIndex = 0;
+ SparseArrayNode *arrayNode = nullptr;
+ ~ObjectOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
namespace Heap {
struct BooleanObject : Object {
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 73c09a864a..d4e07e3d77 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -43,6 +43,7 @@
#include "qv4argumentsobject_p.h"
#include "qv4string_p.h"
#include "qv4iterator_p.h"
+#include "qv4propertykey_p.h"
using namespace QV4;
@@ -51,20 +52,6 @@ void ForInIteratorPrototype::init(ExecutionEngine *)
defineDefaultProperty(QStringLiteral("next"), method_next, 0);
}
-ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int)
-{
- const ForInIteratorObject *forIn = thisObject->as<ForInIteratorObject>();
- Q_ASSERT(forIn);
- Scope scope(b->engine());
- ScopedValue n(scope, forIn->nextPropertyName());
- bool done = false;
- if (n->asReturnedValue() == Encode::null()) {
- done = true;
- n = Primitive::undefinedValue();
- }
- return IteratorPrototype::createIterResultObject(scope.engine, n, done);
-}
-
void ObjectIterator::init(const Object *o)
{
object->setM(o ? o->m() : nullptr);
@@ -200,7 +187,73 @@ DEFINE_OBJECT_VTABLE(ForInIteratorObject);
void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
{
ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that);
+ o->object->mark(markStack);
o->workArea[0].mark(markStack);
o->workArea[1].mark(markStack);
Object::markObjects(that, markStack);
}
+
+void Heap::ForInIteratorObject::destroy()
+{
+ delete iterator;
+}
+
+ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ const ForInIteratorObject *forIn = static_cast<const ForInIteratorObject *>(thisObject);
+ Q_ASSERT(forIn);
+ Scope scope(b);
+
+ ScopedPropertyKey key(scope, forIn->nextProperty());
+ bool done = false;
+ if (!key->isValid())
+ done = true;
+ ScopedStringOrSymbol s(scope, key->toStringOrSymbol(scope.engine));
+ return IteratorPrototype::createIterResultObject(scope.engine, s, done);
+}
+
+
+PropertyKey ForInIteratorObject::nextProperty() const
+{
+ if (!d()->current)
+ return PropertyKey::invalid();
+
+ Scope scope(this);
+ ScopedObject c(scope, d()->current);
+ ScopedObject o(scope);
+ ScopedPropertyKey key(scope);
+ PropertyAttributes attrs;
+
+ while (1) {
+ while (1) {
+ key = d()->iterator->next(c, nullptr, &attrs);
+ if (!key->isValid())
+ break;
+ if (!attrs.isEnumerable() || key->isSymbol())
+ continue;
+ // check the property is not already defined earlier in the proto chain
+ if (d()->current != d()->object) {
+ o = d()->object;
+ bool shadowed = false;
+ while (o->d() != c->heapObject()) {
+ if (o->getOwnProperty(key) != Attr_Invalid) {
+ shadowed = true;
+ break;
+ }
+ o = o->getPrototypeOf();
+ }
+ if (shadowed)
+ continue;
+ }
+ return key;
+ }
+
+ c = c->getPrototypeOf();
+ d()->current.set(scope.engine, c->d());
+ if (!c)
+ break;
+ delete d()->iterator;
+ d()->iterator = c->ownPropertyKeys();
+ }
+ return PropertyKey::invalid();
+}
diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h
index 1e7000ad1f..20eb918e12 100644
--- a/src/qml/jsruntime/qv4objectiterator_p.h
+++ b/src/qml/jsruntime/qv4objectiterator_p.h
@@ -111,14 +111,18 @@ private:
};
namespace Heap {
-struct ForInIteratorObject : Object {
+
+#define ForInIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, object) \
+ Member(class, Pointer, Object *, current) \
+ Member(class, NoMark, OwnPropertyKeyIterator *, iterator)
+
+DECLARE_HEAP_OBJECT(ForInIteratorObject, Object) {
void init(QV4::Object *o);
- ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); }
Value workArea[2];
static void markObjects(Heap::Base *that, MarkStack *markStack);
-private:
- ObjectIteratorData itData;
+ void destroy();
};
}
@@ -135,16 +139,20 @@ struct ForInIteratorObject: Object {
V4_OBJECT2(ForInIteratorObject, Object)
Q_MANAGED_TYPE(ForInIterator)
V4_PROTOTYPE(forInIteratorPrototype)
+ V4_NEEDS_DESTROY
- ReturnedValue nextPropertyName() const { return d()->it().nextPropertyNameAsString(); }
+ PropertyKey nextProperty() const;
};
inline
void Heap::ForInIteratorObject::init(QV4::Object *o)
{
Object::init();
- it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o,
- ObjectIterator::EnumerableOnly | ObjectIterator::WithProtoChain);
+ if (!o)
+ return;
+ object.set(o->engine(), o->d());
+ current.set(o->engine(), o->d());
+ iterator = o->ownPropertyKeys();
}
diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp
index f8bc28160e..79c372348f 100644
--- a/src/qml/jsruntime/qv4persistent.cpp
+++ b/src/qml/jsruntime/qv4persistent.cpp
@@ -302,6 +302,10 @@ PersistentValue &PersistentValue::operator=(const PersistentValue &other)
return *this;
val = other.engine()->memoryManager->m_persistentValues->allocate();
}
+ if (!other.val) {
+ *val = Encode::undefined();
+ return *this;
+ }
Q_ASSERT(engine() == other.engine());
@@ -316,6 +320,10 @@ PersistentValue &PersistentValue::operator=(const WeakValue &other)
return *this;
val = other.engine()->memoryManager->m_persistentValues->allocate();
}
+ if (!other.valueRef()) {
+ *val = Encode::undefined();
+ return *this;
+ }
Q_ASSERT(engine() == other.engine());
@@ -379,6 +387,10 @@ WeakValue &WeakValue::operator=(const WeakValue &other)
return *this;
allocVal(other.engine());
}
+ if (!other.val) {
+ *val = Encode::undefined();
+ return *this;
+ }
Q_ASSERT(engine() == other.engine());
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
index 14f8e961ea..7134b06c6d 100644
--- a/src/qml/jsruntime/qv4propertykey_p.h
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -130,6 +130,9 @@ public:
Q_QML_EXPORT QString toQString() const;
Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e);
quint64 id() const { return val; }
+ static PropertyKey fromId(quint64 id) {
+ PropertyKey key; key.val = id; return key;
+ }
enum FunctionNamePrefix {
None,
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index a17de5d94d..61678acdad 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -814,6 +814,74 @@ void QObjectWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Valu
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
+struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ int propertyIndex = 0;
+ ~QObjectWrapperOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
+ static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
+ static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
+ static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
+
+ const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
+
+ QObject *thatObject = that->d()->object();
+ if (thatObject && !QQmlData::wasDeleted(thatObject)) {
+ const QMetaObject *mo = thatObject->metaObject();
+ // These indices don't apply to gadgets, so don't block them.
+ const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
+ const int propertyCount = mo->propertyCount();
+ if (propertyIndex < propertyCount) {
+ ExecutionEngine *thatEngine = that->engine();
+ Scope scope(thatEngine);
+ const QMetaProperty property = mo->property(propertyIndex);
+ ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
+ ++propertyIndex;
+ if (attrs)
+ *attrs= QV4::Attr_Data;
+ if (pd) {
+ QQmlPropertyData local;
+ local.load(property);
+ pd->value = that->getProperty(thatEngine, thatObject, &local);
+ }
+ return propName->toPropertyKey();
+ }
+ const int methodCount = mo->methodCount();
+ while (propertyIndex < propertyCount + methodCount) {
+ Q_ASSERT(propertyIndex >= propertyCount);
+ int index = propertyIndex - propertyCount;
+ const QMetaMethod method = mo->method(index);
+ ++propertyIndex;
+ if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
+ continue;
+ ExecutionEngine *thatEngine = that->engine();
+ Scope scope(thatEngine);
+ ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd) {
+ QQmlPropertyData local;
+ local.load(method);
+ pd->value = that->getProperty(thatEngine, thatObject, &local);
+ }
+ return methodName->toPropertyKey();
+ }
+ }
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *)
+{
+ return new QObjectWrapperOwnPropertyKeyIterator;
+}
+
namespace QV4 {
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 7843289d33..203bbfa151 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -182,12 +182,11 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
void destroyObject(bool lastCall);
-protected:
- static bool virtualIsEqualTo(Managed *that, Managed *o);
-
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true);
+protected:
static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value);
+ static bool virtualIsEqualTo(Managed *that, Managed *o);
static ReturnedValue create(ExecutionEngine *engine, QObject *object);
static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local);
@@ -197,6 +196,7 @@ protected:
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 1ba889ee82..ab05a819c1 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -365,6 +365,38 @@ public:
QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs);
}
+ struct OwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+ {
+ ~OwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override
+ {
+ const QQmlSequence *s = static_cast<const QQmlSequence *>(o);
+
+ if (s->d()->isReference) {
+ if (!s->d()->object)
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+ s->loadReference();
+ }
+
+ if (arrayIndex < static_cast<uint>(s->d()->container->size())) {
+ uint index = arrayIndex;
+ ++arrayIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = convertElementToValue(s->engine(), s->d()->container->at(index));
+ return PropertyKey::fromArrayIndex(index);
+ }
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+ }
+ };
+
+ static OwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *)
+ {
+ return new OwnPropertyKeyIterator;
+ }
+
bool containerDeleteIndexedProperty(uint index)
{
/* Qt containers have int (rather than uint) allowable indexes. */
@@ -589,6 +621,8 @@ public:
{ return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); }
static void virtualAdvanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
{ return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, p, attrs); }
+ static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m)
+ { return static_cast<const QQmlSequence<Container> *>(m)->containerOwnPropertyKeys(m);}
};
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index f2d0d52013..12fed62bcc 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -137,24 +137,58 @@ void StringObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value
return Object::virtualAdvanceIterator(m, it, name, index, p, attrs);
}
+
+struct StringObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ ~StringObjectOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey StringObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ const StringObject *s = static_cast<const StringObject *>(o);
+ uint slen = s->d()->string->toQString().length();
+ if (arrayIndex < slen) {
+ uint index = arrayIndex;
+ ++arrayIndex;
+ if (attrs)
+ *attrs = Attr_NotConfigurable|Attr_NotWritable;
+ if (pd)
+ pd->value = s->getIndex(index);
+ return PropertyKey::fromArrayIndex(index);
+ } else if (arrayIndex == slen) {
+ if (s->arrayData()) {
+ arrayNode = s->sparseBegin();
+ // iterate until we're past the end of the string
+ while (arrayNode && arrayNode->key() < slen)
+ arrayNode = arrayNode->nextNode();
+ }
+ }
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *StringObject::virtualOwnPropertyKeys(const Object *)
+{
+ return new StringObjectOwnPropertyKeyIterator;
+}
+
PropertyAttributes StringObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
{
PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
if (attributes != Attr_Invalid)
return attributes;
- Object *o = static_cast<Object *>(m);
- if (id.isArrayIndex()) {
- uint index = id.asArrayIndex();
- if (o->isStringObject()) {
- if (index >= static_cast<const StringObject *>(m)->length())
- return Attr_Invalid;
- if (p)
- p->value = static_cast<StringObject *>(o)->getIndex(index);
- return Attr_NotConfigurable|Attr_NotWritable;
- }
+ StringObject *s = static_cast<StringObject *>(m);
+ uint slen = s->d()->string->toQString().length();
+ uint index = id.asArrayIndex();
+ if (index < slen) {
+ if (p)
+ p->value = static_cast<StringObject *>(s)->getIndex(index);
+ return Attr_NotConfigurable|Attr_NotWritable;
}
- return Attr_Invalid;
+ return Object::virtualGetOwnProperty(m, id, p);
}
DEFINE_OBJECT_VTABLE(StringCtor);
diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h
index 2d37e36b34..4dd114e06a 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -102,6 +102,7 @@ struct StringObject: Object {
protected:
static bool virtualDeleteProperty(Managed *m, PropertyKey id);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
};
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index b4e868d45d..aff8eb83ac 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -56,6 +56,11 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+struct OwnPropertyKeyIterator {
+ virtual ~OwnPropertyKeyIterator() = 0;
+ virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
+};
+
struct VTable
{
typedef void (*Destroy)(Heap::Base *);
@@ -74,6 +79,7 @@ struct VTable
typedef bool (*SetPrototypeOf)(Managed *, const Object *);
typedef qint64 (*GetLength)(const Managed *m);
typedef void (*AdvanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ typedef OwnPropertyKeyIterator *(*OwnPropertyKeys)(const Object *m);
typedef ReturnedValue (*InstanceOf)(const Object *typeObject, const Value &var);
typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -109,6 +115,7 @@ struct VTable
SetPrototypeOf setPrototypeOf;
GetLength getLength;
AdvanceIterator advanceIterator;
+ OwnPropertyKeys ownPropertyKeys;
InstanceOf instanceOf;
Call call;
@@ -133,6 +140,7 @@ protected:
static constexpr VTable::SetPrototypeOf virtualSetPrototypeOf = nullptr;
static constexpr VTable::GetLength virtualGetLength = nullptr;
static constexpr VTable::AdvanceIterator virtualAdvanceIterator = nullptr;
+ static constexpr VTable::OwnPropertyKeys virtualOwnPropertyKeys = nullptr;
static constexpr VTable::InstanceOf virtualInstanceOf = nullptr;
static constexpr VTable::Call virtualCall = nullptr;
@@ -172,6 +180,7 @@ protected:
classname::virtualSetPrototypeOf, \
classname::virtualGetLength, \
classname::virtualAdvanceIterator, \
+ classname::virtualOwnPropertyKeys, \
classname::virtualInstanceOf, \
\
classname::virtualCall, \
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 7c35f73e6c..d491cd6166 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -157,6 +157,36 @@ void QmlListWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Valu
return QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attrs);
}
+struct QmlListWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ ~QmlListWrapperOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o);
+
+ quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
+ if (arrayIndex < count) {
+ uint index = arrayIndex;
+ ++arrayIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), index));
+ return PropertyKey::fromArrayIndex(index);
+ }
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *QmlListWrapper::virtualOwnPropertyKeys(const Object *)
+{
+ return new QmlListWrapperOwnPropertyKeyIterator;
+}
+
void PropertyListPrototype::init(ExecutionEngine *)
{
defineDefaultProperty(QStringLiteral("push"), method_push, 1);
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index 2d6a0880e3..e78b779344 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -96,6 +96,7 @@ struct Q_QML_EXPORT QmlListWrapper : Object
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
};
struct PropertyListPrototype : Object
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 1fde5945ba..29f37fc217 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -282,6 +282,46 @@ void QQmlValueTypeWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
+struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ int propertyIndex = 0;
+ ~QQmlValueTypeWrapperOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+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()->propertyCache()) {
+ const QMetaObject *mo = that->d()->propertyCache()->createMetaObject();
+ const int propertyCount = mo->propertyCount();
+ if (propertyIndex < propertyCount) {
+ Scope scope(that->engine());
+ ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(propertyIndex).name())));
+ ++propertyIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = that->QV4::Object::get(propName);
+ return propName->toPropertyKey();
+ }
+ }
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+
+OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Object *)
+{
+ return new QQmlValueTypeWrapperOwnPropertyKeyIterator;
+}
+
bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
{
if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>())
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index ff8ab98bb6..2df4dbb257 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -111,6 +111,7 @@ public:
static bool virtualIsEqualTo(Managed *m, Managed *other);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index e6f68a30b3..1ad4a5aa9e 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -229,6 +229,15 @@ void QtObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *nam
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
+OwnPropertyKeyIterator *QtObject::virtualOwnPropertyKeys(const Object *m)
+{
+ auto that = static_cast<const QtObject*>(m);
+ if (!that->d()->isComplete())
+ const_cast<QtObject *>(that)->addAll();
+
+ return Object::virtualOwnPropertyKeys(m);
+}
+
/*!
\qmlmethod bool Qt::isQtObject(object)
Returns true if \c object is a valid reference to a Qt or QML object, otherwise false.
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index 4b3814c8b8..896a9960b9 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -95,6 +95,7 @@ struct QtObject : Object
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
static ReturnedValue method_isQtObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_rgba(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index 1b64a38bd6..c6597b49f3 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -1633,6 +1633,44 @@ void ModelObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
+struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ int roleNameIndex = 0;
+ ~ModelObjectOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ const ModelObject *that = static_cast<const ModelObject *>(o);
+
+ ExecutionEngine *v4 = that->engine();
+ if (roleNameIndex < that->listModel()->roleCount()) {
+ Scope scope(that->engine());
+ const ListLayout::Role &role = that->listModel()->getExistingRole(roleNameIndex);
+ ++roleNameIndex;
+ ScopedString roleName(scope, v4->newString(role.name));
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd) {
+ QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index);
+ pd->value = v4->fromVariant(value);
+ }
+ return roleName->toPropertyKey();
+ }
+
+ // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add
+ // unnecessary entries that relate to the roles used. These just create extra work
+ // later on as they will just be ignored.
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *ModelObject::virtualOwnPropertyKeys(const Object *)
+{
+ return new ModelObjectOwnPropertyKeyIterator;
+}
+
DEFINE_OBJECT_VTABLE(ModelObject);
} // namespace QV4
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
index e4a850e8a5..dbd0b0a2a2 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -176,10 +176,13 @@ struct ModelObject : public QObjectWrapper
V4_OBJECT2(ModelObject, QObjectWrapper)
V4_NEEDS_DESTROY
+ ListModel *listModel() const { return d()->m_model->m_listModel; }
+
protected:
static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
};
} // namespace QV4