From 895a3f08b4feade6b377c1818a7fff9b0b1052c6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 3 Aug 2018 10:06:52 +0200 Subject: Implement support for ownKeys in Proxy objects Change-Id: I7edee88e4252e6ed0d2666c3f633bb595cb0e831 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4objectiterator.cpp | 6 +- src/qml/jsruntime/qv4proxy.cpp | 154 ++++++++++++++++++++++++++++++++ src/qml/jsruntime/qv4proxy_p.h | 1 + 3 files changed, 160 insertions(+), 1 deletion(-) (limited to 'src/qml/jsruntime') 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(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(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(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 *); -- cgit v1.2.3