| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | #include "qv4objectiterator_p.h" |
| 4 | #include "qv4object_p.h" |
| 5 | #include "qv4iterator_p.h" |
| 6 | #include "qv4propertykey_p.h" |
| 7 | #include <QtQml/private/qv4functionobject_p.h> |
| 8 | |
| 9 | using namespace QV4; |
| 10 | |
| 11 | void ForInIteratorPrototype::init(ExecutionEngine *) |
| 12 | { |
| 13 | defineDefaultProperty(QStringLiteral("next" ), code: method_next, argumentCount: 0); |
| 14 | } |
| 15 | |
| 16 | PropertyKey ObjectIterator::next(Property *pd, PropertyAttributes *attrs) |
| 17 | { |
| 18 | if (!object || !iterator) |
| 19 | return PropertyKey::invalid(); |
| 20 | |
| 21 | Scope scope(engine); |
| 22 | ScopedPropertyKey key(scope); |
| 23 | |
| 24 | while (1) { |
| 25 | key = iterator->next(o: object, p: pd, attrs); |
| 26 | if (!key->isValid()) { |
| 27 | object = nullptr; |
| 28 | return key; |
| 29 | } |
| 30 | if ((!(flags & WithSymbols) && key->isSymbol()) || |
| 31 | ((flags & EnumerableOnly) && !attrs->isEnumerable())) |
| 32 | continue; |
| 33 | return key; |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | ReturnedValue ObjectIterator::nextPropertyName(Value *value) |
| 38 | { |
| 39 | if (!object) |
| 40 | return Encode::null(); |
| 41 | |
| 42 | PropertyAttributes attrs; |
| 43 | Scope scope(engine); |
| 44 | ScopedProperty p(scope); |
| 45 | ScopedPropertyKey key(scope, next(pd: p, attrs: &attrs)); |
| 46 | if (!key->isValid()) |
| 47 | return Encode::null(); |
| 48 | |
| 49 | *value = object->getValue(v: p->value, attrs); |
| 50 | if (key->isArrayIndex()) |
| 51 | return Encode(key->asArrayIndex()); |
| 52 | Q_ASSERT(key->isStringOrSymbol()); |
| 53 | return key->asStringOrSymbol()->asReturnedValue(); |
| 54 | } |
| 55 | |
| 56 | ReturnedValue ObjectIterator::nextPropertyNameAsString(Value *value) |
| 57 | { |
| 58 | if (!object) |
| 59 | return Encode::null(); |
| 60 | |
| 61 | PropertyAttributes attrs; |
| 62 | Scope scope(engine); |
| 63 | ScopedProperty p(scope); |
| 64 | ScopedPropertyKey key(scope, next(pd: p, attrs: &attrs)); |
| 65 | if (!key->isValid()) |
| 66 | return Encode::null(); |
| 67 | |
| 68 | *value = object->getValue(v: p->value, attrs); |
| 69 | |
| 70 | return key->toStringOrSymbol(e: engine)->asReturnedValue(); |
| 71 | } |
| 72 | |
| 73 | ReturnedValue ObjectIterator::nextPropertyNameAsString() |
| 74 | { |
| 75 | if (!object) |
| 76 | return Encode::null(); |
| 77 | |
| 78 | PropertyAttributes attrs; |
| 79 | Scope scope(engine); |
| 80 | ScopedPropertyKey key(scope, next(pd: nullptr, attrs: &attrs)); |
| 81 | if (!key->isValid()) |
| 82 | return Encode::null(); |
| 83 | |
| 84 | return key->toStringOrSymbol(e: engine)->asReturnedValue(); |
| 85 | } |
| 86 | |
| 87 | |
| 88 | DEFINE_OBJECT_VTABLE(ForInIteratorObject); |
| 89 | |
| 90 | void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) |
| 91 | { |
| 92 | ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that); |
| 93 | if (o->object) |
| 94 | o->object->mark(markStack); |
| 95 | if (o->current) |
| 96 | o->current->mark(markStack); |
| 97 | if (o->target) |
| 98 | o->target->mark(markStack); |
| 99 | o->workArea[0].mark(markStack); |
| 100 | o->workArea[1].mark(markStack); |
| 101 | Object::markObjects(base: that, stack: markStack); |
| 102 | } |
| 103 | |
| 104 | void Heap::ForInIteratorObject::destroy() |
| 105 | { |
| 106 | delete iterator; |
| 107 | } |
| 108 | |
| 109 | ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int) |
| 110 | { |
| 111 | const ForInIteratorObject *forIn = static_cast<const ForInIteratorObject *>(thisObject); |
| 112 | Q_ASSERT(forIn); |
| 113 | Scope scope(b); |
| 114 | |
| 115 | ScopedPropertyKey key(scope, forIn->nextProperty()); |
| 116 | bool done = false; |
| 117 | if (!key->isValid()) |
| 118 | done = true; |
| 119 | ScopedStringOrSymbol s(scope, key->toStringOrSymbol(e: scope.engine)); |
| 120 | return IteratorPrototype::createIterResultObject(engine: scope.engine, value: s, done); |
| 121 | } |
| 122 | |
| 123 | |
| 124 | PropertyKey ForInIteratorObject::nextProperty() const |
| 125 | { |
| 126 | if (!d()->current) |
| 127 | return PropertyKey::invalid(); |
| 128 | |
| 129 | Scope scope(this); |
| 130 | ScopedObject c(scope, d()->current); |
| 131 | ScopedObject t(scope, d()->target); |
| 132 | ScopedObject o(scope); |
| 133 | ScopedProperty p(scope); |
| 134 | ScopedPropertyKey key(scope); |
| 135 | PropertyAttributes attrs; |
| 136 | |
| 137 | while (1) { |
| 138 | while (1) { |
| 139 | key = d()->iterator->next(o: t, p, attrs: &attrs); |
| 140 | if (!key->isValid()) |
| 141 | break; |
| 142 | if (!attrs.isEnumerable() || key->isSymbol()) |
| 143 | continue; |
| 144 | // check the property is not already defined earlier in the proto chain |
| 145 | if (d()->current != d()->object) { |
| 146 | o = d()->object; |
| 147 | bool shadowed = false; |
| 148 | while (o && o->d() != c->heapObject()) { |
| 149 | if (o->getOwnProperty(id: key) != Attr_Invalid) { |
| 150 | shadowed = true; |
| 151 | break; |
| 152 | } |
| 153 | o = o->getPrototypeOf(); |
| 154 | } |
| 155 | if (shadowed) |
| 156 | continue; |
| 157 | } |
| 158 | return key; |
| 159 | } |
| 160 | |
| 161 | c = c->getPrototypeOf(); |
| 162 | d()->current.set(e: scope.engine, newVal: c->d()); |
| 163 | if (!c) |
| 164 | break; |
| 165 | delete d()->iterator; |
| 166 | d()->iterator = c->ownPropertyKeys(target: t.getRef()); |
| 167 | d()->target.set(e: scope.engine, newVal: t->d()); |
| 168 | if (!d()->iterator) { |
| 169 | scope.engine->throwTypeError(); |
| 170 | return PropertyKey::invalid(); |
| 171 | } |
| 172 | } |
| 173 | return PropertyKey::invalid(); |
| 174 | } |
| 175 | |