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>
This commit is contained in:
Lars Knoll 2018-08-02 15:44:12 +02:00
parent 5764095932
commit 0754f55287
25 changed files with 547 additions and 117 deletions

View File

@ -47,19 +47,50 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v) 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); QV4::ExecutionEngine *e = QJSValuePrivate::engine(&v);
if (!e) if (!e)
return; return;
QV4::Object *o = QJSValuePrivate::getValue(&v)->objectValue();
if (!o)
return;
QV4::Scope scope(e); engine = e;
QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&v)); object = o;
iterator.set(e, e->newForInIteratorObject(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 \class QJSValueIterator
@ -98,17 +129,6 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v)
QJSValueIterator::QJSValueIterator(const QJSValue& object) QJSValueIterator::QJSValueIterator(const QJSValue& object)
: d_ptr(new QJSValueIteratorPrivate(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 bool QJSValueIterator::hasNext() const
{ {
QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value); if (!d_ptr->isValid())
if (!val || !val->isObject())
return false; 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() bool QJSValueIterator::next()
{ {
QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value); if (!d_ptr->isValid())
if (!val || !val->isObject())
return false; return false;
d_ptr->currentName = d_ptr->nextName; d_ptr->next();
d_ptr->currentIndex = d_ptr->nextIndex; return QV4::PropertyKey::fromId(d_ptr->currentKey.value()).isValid();
QV4::ExecutionEngine *v4 = d_ptr->iterator.engine();
if (!v4)
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;
} }
/*! /*!
@ -170,14 +176,14 @@ bool QJSValueIterator::next()
*/ */
QString QJSValueIterator::name() const QString QJSValueIterator::name() const
{ {
QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value); if (!d_ptr->isValid())
if (!val || !val->isObject())
return QString(); return QString();
if (QV4::String *s = d_ptr->currentName.as<QV4::String>()) QV4::Scope scope(d_ptr->engine);
return s->toQString(); QV4::ScopedPropertyKey key(scope, QV4::PropertyKey::fromId(d_ptr->currentKey.value()));
if (d_ptr->currentIndex < UINT_MAX) if (!key->isValid())
return QString::number(d_ptr->currentIndex); return QString();
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 QJSValue QJSValueIterator::value() const
{ {
QV4::ExecutionEngine *engine = d_ptr->iterator.engine(); if (!d_ptr->isValid())
if (!engine)
return QJSValue(); return QJSValue();
QV4::Scope scope(engine); QV4::Scope scope(d_ptr->engine);
QV4::ScopedObject obj(scope, QJSValuePrivate::getValue(&d_ptr->value)); QV4::ScopedPropertyKey key(scope, QV4::PropertyKey::fromId(d_ptr->currentKey.value()));
if (!obj) if (!key->isValid())
return QJSValue(); return QJSValue();
if (!d_ptr->currentName.as<QV4::String>() && d_ptr->currentIndex == UINT_MAX) QV4::ScopedObject obj(scope, d_ptr->object.asManaged());
return QJSValue(); 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()) { if (scope.hasException()) {
engine->catchException(); scope.engine->catchException();
return QJSValue(); 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) QJSValueIterator& QJSValueIterator::operator=(QJSValue& object)
{ {
d_ptr->value = object; d_ptr->init(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());
return *this; return *this;
} }

View File

@ -61,12 +61,16 @@ class QJSValueIteratorPrivate
public: public:
QJSValueIteratorPrivate(const QJSValue &v); QJSValueIteratorPrivate(const QJSValue &v);
QJSValue value; void init(const QJSValue &v);
QV4::PersistentValue iterator; bool isValid() const;
QV4::PersistentValue currentName;
uint currentIndex; void next();
QV4::PersistentValue nextName;
uint nextIndex; QV4::ExecutionEngine *engine = nullptr;
QV4::PersistentValue object;
QScopedPointer<QV4::OwnPropertyKeyIterator> iterator;
QV4::PersistentValue currentKey;
QV4::PersistentValue nextKey;
}; };

View File

@ -212,3 +212,9 @@ qint64 ArgumentsObject::virtualGetLength(const Managed *m)
const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m); const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m);
return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength(); 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);
}

View File

@ -106,6 +106,7 @@ struct ArgumentsObject: Object {
static bool virtualDeleteProperty(Managed *m, PropertyKey id); static bool virtualDeleteProperty(Managed *m, PropertyKey id);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
static qint64 virtualGetLength(const Managed *m); static qint64 virtualGetLength(const Managed *m);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
void fullyCreate(); void fullyCreate();

View File

@ -142,3 +142,8 @@ bool Managed::virtualIsEqualTo(Managed *, Managed *)
{ {
return false; return false;
} }
OwnPropertyKeyIterator::~OwnPropertyKeyIterator()
{
}

View File

@ -408,6 +408,78 @@ void Object::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name,
*attrs = PropertyAttributes(); *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 // Section 8.12.3
ReturnedValue Object::internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const ReturnedValue Object::internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const
{ {

View File

@ -280,8 +280,8 @@ public:
} }
void initSparseArray(); void initSparseArray();
SparseArrayNode *sparseBegin() { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->begin() : nullptr; } SparseArrayNode *sparseBegin() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->begin() : nullptr; }
SparseArrayNode *sparseEnd() { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->end() : nullptr; } SparseArrayNode *sparseEnd() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->end() : nullptr; }
inline bool protoHasArray() { inline bool protoHasArray() {
Scope scope(engine()); Scope scope(engine());
@ -355,6 +355,8 @@ public:
{ return vtable()->deleteProperty(this, id); } { return vtable()->deleteProperty(this, id); }
void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{ vtable()->advanceIterator(this, it, name, index, p, attributes); } { vtable()->advanceIterator(this, it, name, index, p, attributes); }
OwnPropertyKeyIterator *ownPropertyKeys() const
{ return vtable()->ownPropertyKeys(this); }
qint64 getLength() const { return vtable()->getLength(this); } qint64 getLength() const { return vtable()->getLength(this); }
ReturnedValue instanceOf(const Value &var) const ReturnedValue instanceOf(const Value &var) const
{ return vtable()->instanceOf(this, var); } { return vtable()->instanceOf(this, var); }
@ -376,6 +378,7 @@ protected:
static Heap::Object *virtualGetPrototypeOf(const Managed *); static Heap::Object *virtualGetPrototypeOf(const Managed *);
static bool virtualSetPrototypeOf(Managed *, const Object *); static bool virtualSetPrototypeOf(Managed *, const Object *);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); 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 qint64 virtualGetLength(const Managed *m);
static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var); static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
@ -390,6 +393,16 @@ private:
friend struct ObjectPrototype; 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 { namespace Heap {
struct BooleanObject : Object { struct BooleanObject : Object {

View File

@ -43,6 +43,7 @@
#include "qv4argumentsobject_p.h" #include "qv4argumentsobject_p.h"
#include "qv4string_p.h" #include "qv4string_p.h"
#include "qv4iterator_p.h" #include "qv4iterator_p.h"
#include "qv4propertykey_p.h"
using namespace QV4; using namespace QV4;
@ -51,20 +52,6 @@ void ForInIteratorPrototype::init(ExecutionEngine *)
defineDefaultProperty(QStringLiteral("next"), method_next, 0); 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) void ObjectIterator::init(const Object *o)
{ {
object->setM(o ? o->m() : nullptr); object->setM(o ? o->m() : nullptr);
@ -200,7 +187,73 @@ DEFINE_OBJECT_VTABLE(ForInIteratorObject);
void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
{ {
ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that); ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that);
o->object->mark(markStack);
o->workArea[0].mark(markStack); o->workArea[0].mark(markStack);
o->workArea[1].mark(markStack); o->workArea[1].mark(markStack);
Object::markObjects(that, 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();
}

View File

@ -111,14 +111,18 @@ private:
}; };
namespace Heap { 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); void init(QV4::Object *o);
ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); }
Value workArea[2]; Value workArea[2];
static void markObjects(Heap::Base *that, MarkStack *markStack); static void markObjects(Heap::Base *that, MarkStack *markStack);
private: void destroy();
ObjectIteratorData itData;
}; };
} }
@ -135,16 +139,20 @@ struct ForInIteratorObject: Object {
V4_OBJECT2(ForInIteratorObject, Object) V4_OBJECT2(ForInIteratorObject, Object)
Q_MANAGED_TYPE(ForInIterator) Q_MANAGED_TYPE(ForInIterator)
V4_PROTOTYPE(forInIteratorPrototype) V4_PROTOTYPE(forInIteratorPrototype)
V4_NEEDS_DESTROY
ReturnedValue nextPropertyName() const { return d()->it().nextPropertyNameAsString(); } PropertyKey nextProperty() const;
}; };
inline inline
void Heap::ForInIteratorObject::init(QV4::Object *o) void Heap::ForInIteratorObject::init(QV4::Object *o)
{ {
Object::init(); Object::init();
it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o, if (!o)
ObjectIterator::EnumerableOnly | ObjectIterator::WithProtoChain); return;
object.set(o->engine(), o->d());
current.set(o->engine(), o->d());
iterator = o->ownPropertyKeys();
} }

View File

@ -302,6 +302,10 @@ PersistentValue &PersistentValue::operator=(const PersistentValue &other)
return *this; return *this;
val = other.engine()->memoryManager->m_persistentValues->allocate(); val = other.engine()->memoryManager->m_persistentValues->allocate();
} }
if (!other.val) {
*val = Encode::undefined();
return *this;
}
Q_ASSERT(engine() == other.engine()); Q_ASSERT(engine() == other.engine());
@ -316,6 +320,10 @@ PersistentValue &PersistentValue::operator=(const WeakValue &other)
return *this; return *this;
val = other.engine()->memoryManager->m_persistentValues->allocate(); val = other.engine()->memoryManager->m_persistentValues->allocate();
} }
if (!other.valueRef()) {
*val = Encode::undefined();
return *this;
}
Q_ASSERT(engine() == other.engine()); Q_ASSERT(engine() == other.engine());
@ -379,6 +387,10 @@ WeakValue &WeakValue::operator=(const WeakValue &other)
return *this; return *this;
allocVal(other.engine()); allocVal(other.engine());
} }
if (!other.val) {
*val = Encode::undefined();
return *this;
}
Q_ASSERT(engine() == other.engine()); Q_ASSERT(engine() == other.engine());

View File

@ -130,6 +130,9 @@ public:
Q_QML_EXPORT QString toQString() const; Q_QML_EXPORT QString toQString() const;
Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e); Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e);
quint64 id() const { return val; } quint64 id() const { return val; }
static PropertyKey fromId(quint64 id) {
PropertyKey key; key.val = id; return key;
}
enum FunctionNamePrefix { enum FunctionNamePrefix {
None, None,

View File

@ -814,6 +814,74 @@ void QObjectWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Valu
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); 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 { namespace QV4 {
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase

View File

@ -182,12 +182,11 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
void destroyObject(bool lastCall); void destroyObject(bool lastCall);
protected:
static bool virtualIsEqualTo(Managed *that, Managed *o);
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); 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 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 ReturnedValue create(ExecutionEngine *engine, QObject *object);
static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local); 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 bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); 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 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_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); static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);

View File

@ -365,6 +365,38 @@ public:
QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs); 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) bool containerDeleteIndexedProperty(uint index)
{ {
/* Qt containers have int (rather than uint) allowable indexes. */ /* Qt containers have int (rather than uint) allowable indexes. */
@ -589,6 +621,8 @@ public:
{ return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); }
static void virtualAdvanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) 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); } { 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);}
}; };

View File

@ -137,24 +137,58 @@ void StringObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value
return Object::virtualAdvanceIterator(m, it, name, index, p, attrs); 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 StringObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
{ {
PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p); PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
if (attributes != Attr_Invalid) if (attributes != Attr_Invalid)
return attributes; return attributes;
Object *o = static_cast<Object *>(m); StringObject *s = static_cast<StringObject *>(m);
if (id.isArrayIndex()) { uint slen = s->d()->string->toQString().length();
uint index = id.asArrayIndex(); uint index = id.asArrayIndex();
if (o->isStringObject()) { if (index < slen) {
if (index >= static_cast<const StringObject *>(m)->length()) if (p)
return Attr_Invalid; p->value = static_cast<StringObject *>(s)->getIndex(index);
if (p) return Attr_NotConfigurable|Attr_NotWritable;
p->value = static_cast<StringObject *>(o)->getIndex(index);
return Attr_NotConfigurable|Attr_NotWritable;
}
} }
return Attr_Invalid; return Object::virtualGetOwnProperty(m, id, p);
} }
DEFINE_OBJECT_VTABLE(StringCtor); DEFINE_OBJECT_VTABLE(StringCtor);

View File

@ -102,6 +102,7 @@ struct StringObject: Object {
protected: protected:
static bool virtualDeleteProperty(Managed *m, PropertyKey id); static bool virtualDeleteProperty(Managed *m, PropertyKey id);
static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); 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); static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
}; };

View File

@ -56,6 +56,11 @@ QT_BEGIN_NAMESPACE
namespace QV4 { namespace QV4 {
struct OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
};
struct VTable struct VTable
{ {
typedef void (*Destroy)(Heap::Base *); typedef void (*Destroy)(Heap::Base *);
@ -74,6 +79,7 @@ struct VTable
typedef bool (*SetPrototypeOf)(Managed *, const Object *); typedef bool (*SetPrototypeOf)(Managed *, const Object *);
typedef qint64 (*GetLength)(const Managed *m); typedef qint64 (*GetLength)(const Managed *m);
typedef void (*AdvanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); 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 (*InstanceOf)(const Object *typeObject, const Value &var);
typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@ -109,6 +115,7 @@ struct VTable
SetPrototypeOf setPrototypeOf; SetPrototypeOf setPrototypeOf;
GetLength getLength; GetLength getLength;
AdvanceIterator advanceIterator; AdvanceIterator advanceIterator;
OwnPropertyKeys ownPropertyKeys;
InstanceOf instanceOf; InstanceOf instanceOf;
Call call; Call call;
@ -133,6 +140,7 @@ protected:
static constexpr VTable::SetPrototypeOf virtualSetPrototypeOf = nullptr; static constexpr VTable::SetPrototypeOf virtualSetPrototypeOf = nullptr;
static constexpr VTable::GetLength virtualGetLength = nullptr; static constexpr VTable::GetLength virtualGetLength = nullptr;
static constexpr VTable::AdvanceIterator virtualAdvanceIterator = nullptr; static constexpr VTable::AdvanceIterator virtualAdvanceIterator = nullptr;
static constexpr VTable::OwnPropertyKeys virtualOwnPropertyKeys = nullptr;
static constexpr VTable::InstanceOf virtualInstanceOf = nullptr; static constexpr VTable::InstanceOf virtualInstanceOf = nullptr;
static constexpr VTable::Call virtualCall = nullptr; static constexpr VTable::Call virtualCall = nullptr;
@ -172,6 +180,7 @@ protected:
classname::virtualSetPrototypeOf, \ classname::virtualSetPrototypeOf, \
classname::virtualGetLength, \ classname::virtualGetLength, \
classname::virtualAdvanceIterator, \ classname::virtualAdvanceIterator, \
classname::virtualOwnPropertyKeys, \
classname::virtualInstanceOf, \ classname::virtualInstanceOf, \
\ \
classname::virtualCall, \ classname::virtualCall, \

View File

@ -157,6 +157,36 @@ void QmlListWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Valu
return QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attrs); 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 *) void PropertyListPrototype::init(ExecutionEngine *)
{ {
defineDefaultProperty(QStringLiteral("push"), method_push, 1); defineDefaultProperty(QStringLiteral("push"), method_push, 1);

View File

@ -96,6 +96,7 @@ struct Q_QML_EXPORT QmlListWrapper : Object
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); 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 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 void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
}; };
struct PropertyListPrototype : Object struct PropertyListPrototype : Object

View File

@ -282,6 +282,46 @@ void QQmlValueTypeWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); 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 bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
{ {
if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>())

View File

@ -111,6 +111,7 @@ public:
static bool virtualIsEqualTo(Managed *m, Managed *other); static bool virtualIsEqualTo(Managed *m, Managed *other);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); 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 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); static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);

View File

@ -229,6 +229,15 @@ void QtObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *nam
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); 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) \qmlmethod bool Qt::isQtObject(object)
Returns true if \c object is a valid reference to a Qt or QML object, otherwise false. Returns true if \c object is a valid reference to a Qt or QML object, otherwise false.

View File

@ -95,6 +95,7 @@ struct QtObject : Object
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); 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 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_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); static ReturnedValue method_rgba(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);

View File

@ -1633,6 +1633,44 @@ void ModelObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *
QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); 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); DEFINE_OBJECT_VTABLE(ModelObject);
} // namespace QV4 } // namespace QV4

View File

@ -176,10 +176,13 @@ struct ModelObject : public QObjectWrapper
V4_OBJECT2(ModelObject, QObjectWrapper) V4_OBJECT2(ModelObject, QObjectWrapper)
V4_NEEDS_DESTROY V4_NEEDS_DESTROY
ListModel *listModel() const { return d()->m_model->m_listModel; }
protected: protected:
static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); 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 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 void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m);
}; };
} // namespace QV4 } // namespace QV4