diff options
Diffstat (limited to 'src')
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 |