aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
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