aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-08-03 10:06:52 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-04 09:16:33 +0000
commit895a3f08b4feade6b377c1818a7fff9b0b1052c6 (patch)
tree9dfef5030e3e7720282791e4585c396db095be02
parent18376523d495597bbe009cf20d783816c9f3a44a (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.cpp4
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp6
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp154
-rw-r--r--src/qml/jsruntime/qv4proxy_p.h1
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations18
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