diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-08-03 10:06:52 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-08-04 09:16:33 +0000 |
commit | 895a3f08b4feade6b377c1818a7fff9b0b1052c6 (patch) | |
tree | 9dfef5030e3e7720282791e4585c396db095be02 | |
parent | 18376523d495597bbe009cf20d783816c9f3a44a (diff) |
Implement support for ownKeys in Proxy objects
Change-Id: I7edee88e4252e6ed0d2666c3f633bb595cb0e831
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r-- | src/qml/jsapi/qjsvalueiterator.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4objectiterator.cpp | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4proxy.cpp | 154 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4proxy_p.h | 1 | ||||
-rw-r--r-- | tests/auto/qml/ecmascripttests/TestExpectations | 18 |
5 files changed, 162 insertions, 21 deletions
diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp index 5add9d4b0a..89caf705df 100644 --- a/src/qml/jsapi/qjsvalueiterator.cpp +++ b/src/qml/jsapi/qjsvalueiterator.cpp @@ -71,7 +71,7 @@ void QJSValueIteratorPrivate::init(const QJSValue &v) void QJSValueIteratorPrivate::next() { QV4::Object *o = object.as<QV4::Object>(); - if (!o) + if (!o || !iterator) return; QV4::PropertyKey key; @@ -86,7 +86,7 @@ void QJSValueIteratorPrivate::next() bool QJSValueIteratorPrivate::isValid() const { - if (!engine) + if (!engine || !iterator) return false; QV4::Value *val = object.valueRef(); return (val && val->isObject()); diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 89880d86dd..3d193455e9 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -54,7 +54,7 @@ void ForInIteratorPrototype::init(ExecutionEngine *) PropertyKey ObjectIterator::next(Property *pd, PropertyAttributes *attrs) { - if (!object) + if (!object || !iterator) return PropertyKey::invalid(); Scope scope(engine); @@ -196,6 +196,10 @@ PropertyKey ForInIteratorObject::nextProperty() const break; delete d()->iterator; d()->iterator = c->ownPropertyKeys(); + if (!d()->iterator) { + scope.engine->throwTypeError(); + return PropertyKey::invalid(); + } } return PropertyKey::invalid(); } diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp index 794aee8c24..e26c51b473 100644 --- a/src/qml/jsruntime/qv4proxy.cpp +++ b/src/qml/jsruntime/qv4proxy.cpp @@ -42,6 +42,8 @@ #include "qv4symbol_p.h" #include "qv4jscall_p.h" #include "qv4objectproto_p.h" +#include "qv4persistent_p.h" +#include "qv4objectiterator_p.h" using namespace QV4; @@ -477,6 +479,158 @@ bool ProxyObject::virtualSetPrototypeOf(Managed *m, const Object *p) return true; } +struct ProxyObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator +{ + PersistentValue ownKeys; + uint index = 0; + uint len = 0; + + ProxyObjectOwnPropertyKeyIterator(ArrayObject *keys); + ~ProxyObjectOwnPropertyKeyIterator() override = default; + PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; + +}; + +ProxyObjectOwnPropertyKeyIterator::ProxyObjectOwnPropertyKeyIterator(ArrayObject *keys) +{ + ownKeys = keys; + len = keys->getLength(); +} + +PropertyKey ProxyObjectOwnPropertyKeyIterator::next(const Object *m, Property *pd, PropertyAttributes *attrs) +{ + if (index >= len) + return PropertyKey::invalid(); + + Scope scope(m); + ScopedObject keys(scope, ownKeys.asManaged()); + PropertyKey key = PropertyKey::fromId(keys->get(PropertyKey::fromArrayIndex(index))); + ++index; + + if (pd || attrs) { + ScopedProperty p(scope); + PropertyAttributes a = const_cast<Object *>(m)->getOwnProperty(key, pd ? pd : p); + if (attrs) + *attrs = a; + } + + return key; +} + +static bool removeAllOccurrences(ArrayObject *target, ReturnedValue val) { + uint len = target->getLength(); + bool found = false; + for (uint i = 0; i < len; ++i) { + ReturnedValue v = target->get(i); + if (v == val) { + found = true; + target->put(i, Primitive::undefinedValue()); + } + } + return found; +} + +OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return nullptr; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("ownKeys"))); + ScopedValue trap(scope, handler->get(name)); + + if (scope.hasException()) + return nullptr; + if (trap->isUndefined()) + return target->ownPropertyKeys(); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return nullptr; + } + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + ScopedObject trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult) { + scope.engine->throwTypeError(); + return nullptr; + } + + uint len = trapResult->getLength(); + ScopedArrayObject trapKeys(scope, scope.engine->newArrayObject()); + ScopedStringOrSymbol key(scope); + for (uint i = 0; i < len; ++i) { + key = trapResult->get(i); + if (scope.engine->hasException) + return nullptr; + if (!key) { + scope.engine->throwTypeError(); + return nullptr; + } + Value keyAsValue = Primitive::fromReturnedValue(key->toPropertyKey().id()); + trapKeys->push_back(keyAsValue); + } + + ScopedArrayObject targetConfigurableKeys(scope, scope.engine->newArrayObject()); + ScopedArrayObject targetNonConfigurableKeys(scope, scope.engine->newArrayObject()); + ObjectIterator it(scope, target, ObjectIterator::EnumerableOnly); + ScopedPropertyKey k(scope); + while (1) { + PropertyAttributes attrs; + k = it.next(nullptr, &attrs); + if (!k->isValid()) + break; + Value keyAsValue = Primitive::fromReturnedValue(k->id()); + if (attrs.isConfigurable()) + targetConfigurableKeys->push_back(keyAsValue); + else + targetNonConfigurableKeys->push_back(keyAsValue); + } + if (target->isExtensible() && targetNonConfigurableKeys->getLength() == 0) + return new ProxyObjectOwnPropertyKeyIterator(trapKeys); + + ScopedArrayObject uncheckedResultKeys(scope, scope.engine->newArrayObject()); + uncheckedResultKeys->copyArrayData(trapKeys); + + len = targetNonConfigurableKeys->getLength(); + for (uint i = 0; i < len; ++i) { + k = PropertyKey::fromId(targetNonConfigurableKeys->get(i)); + if (!removeAllOccurrences(uncheckedResultKeys, k->id())) { + scope.engine->throwTypeError(); + return nullptr; + } + } + + if (target->isExtensible()) + return new ProxyObjectOwnPropertyKeyIterator(trapKeys); + + len = targetConfigurableKeys->getLength(); + for (uint i = 0; i < len; ++i) { + k = PropertyKey::fromId(targetConfigurableKeys->get(i)); + if (!removeAllOccurrences(uncheckedResultKeys, k->id())) { + scope.engine->throwTypeError(); + return nullptr; + } + } + + len = uncheckedResultKeys->getLength(); + for (uint i = 0; i < len; ++i) { + if (targetConfigurableKeys->get(i) != Encode::undefined()) { + scope.engine->throwTypeError(); + return nullptr; + } + } + + return new ProxyObjectOwnPropertyKeyIterator(trapKeys); +} + + //ReturnedValue ProxyObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) //{ diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h index 4d631e882c..bad4d040c7 100644 --- a/src/qml/jsruntime/qv4proxy_p.h +++ b/src/qml/jsruntime/qv4proxy_p.h @@ -95,6 +95,7 @@ struct ProxyObject: Object { static bool virtualPreventExtensions(Managed *); static Heap::Object *virtualGetPrototypeOf(const Managed *); static bool virtualSetPrototypeOf(Managed *, const Object *); + static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m); // those might require a second proxy object that derives from FunctionObject... // static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index b495be9f26..014de6ca4a 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -459,7 +459,6 @@ built-ins/Number/prototype/toPrecision/return-values.js fails built-ins/Number/string-binary-literal.js fails built-ins/Number/string-hex-literal-invalid.js fails built-ins/Number/string-octal-literal.js fails -built-ins/Object/assign/source-own-prop-desc-missing.js fails built-ins/Object/create/15.2.3.5-4-14.js strictFails built-ins/Object/create/15.2.3.5-4-37.js strictFails built-ins/Object/entries/getter-making-future-key-nonenumerable.js fails @@ -469,7 +468,6 @@ built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-212.js fails built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-213.js fails built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-214.js fails built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-215.js fails -built-ins/Object/getOwnPropertyDescriptors/observable-operations.js fails built-ins/Object/getOwnPropertyDescriptors/proxy-undefined-descriptor.js fails built-ins/Object/getOwnPropertyDescriptors/symbols-included.js fails built-ins/Object/keys/proxy-keys.js fails @@ -677,28 +675,12 @@ built-ins/Proxy/getPrototypeOf/trap-is-not-callable-realm.js fails built-ins/Proxy/has/call-object-create.js fails built-ins/Proxy/has/trap-is-not-callable-realm.js fails built-ins/Proxy/isExtensible/trap-is-not-callable-realm.js fails -built-ins/Proxy/ownKeys/call-parameters-object-getownpropertynames.js fails built-ins/Proxy/ownKeys/call-parameters-object-getownpropertysymbols.js fails -built-ins/Proxy/ownKeys/call-parameters-object-keys.js fails -built-ins/Proxy/ownKeys/extensible-return-trap-result-absent-not-configurable-keys.js fails -built-ins/Proxy/ownKeys/extensible-return-trap-result.js fails -built-ins/Proxy/ownKeys/not-extensible-missing-keys-throws.js fails -built-ins/Proxy/ownKeys/not-extensible-new-keys-throws.js fails built-ins/Proxy/ownKeys/not-extensible-return-keys.js fails -built-ins/Proxy/ownKeys/null-handler.js fails -built-ins/Proxy/ownKeys/return-all-non-configurable-keys.js fails built-ins/Proxy/ownKeys/return-duplicate-entries-throws.js fails built-ins/Proxy/ownKeys/return-duplicate-symbol-entries-throws.js fails built-ins/Proxy/ownKeys/return-not-list-object-throws-realm.js fails -built-ins/Proxy/ownKeys/return-not-list-object-throws.js fails -built-ins/Proxy/ownKeys/return-type-throws-array.js fails -built-ins/Proxy/ownKeys/return-type-throws-boolean.js fails -built-ins/Proxy/ownKeys/return-type-throws-null.js fails -built-ins/Proxy/ownKeys/return-type-throws-number.js fails -built-ins/Proxy/ownKeys/return-type-throws-object.js fails -built-ins/Proxy/ownKeys/return-type-throws-undefined.js fails built-ins/Proxy/ownKeys/trap-is-not-callable-realm.js fails -built-ins/Proxy/ownKeys/trap-is-not-callable.js fails built-ins/Proxy/ownKeys/trap-is-undefined.js fails built-ins/Proxy/preventExtensions/trap-is-not-callable-realm.js fails built-ins/Proxy/proxy-no-prototype.js fails |