aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4proxy.cpp
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 /src/qml/jsruntime/qv4proxy.cpp
parent18376523d495597bbe009cf20d783816c9f3a44a (diff)
Implement support for ownKeys in Proxy objects
Change-Id: I7edee88e4252e6ed0d2666c3f633bb595cb0e831 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4proxy.cpp')
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp154
1 files changed, 154 insertions, 0 deletions
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 *)
//{