From cb3aa2346e85542c358034c92dc88fb543f1a4e1 Mon Sep 17 00:00:00 2001 From: Sankha Narayan Guria Date: Wed, 5 Jun 2013 14:17:30 -0500 Subject: [PATCH] Bug 869996 - Implement Set.prototype.{keys, values, entries}. r=jorendorff. --HG-- extra : rebase_source : 6d978d49d2fb696162b1cef5b643a209a01a4711 --- js/src/builtin/MapObject.cpp | 99 ++++++++++++++++--- js/src/builtin/MapObject.h | 9 +- .../tests/collections/Map-surfaces-1.js | 6 ++ .../tests/collections/Map-surfaces-2.js | 3 + .../tests/collections/Set-surfaces-1.js | 6 ++ .../tests/collections/Set-surfaces-2.js | 3 + .../tests/collections/Set-values-1.js | 14 +++ .../tests/collections/Set-values-2.js | 18 ++++ 8 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 js/src/jit-test/tests/collections/Set-values-1.js create mode 100644 js/src/jit-test/tests/collections/Set-values-2.js diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 081409cb06cc..53026da9b203 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -1041,8 +1041,6 @@ const JSFunctionSpec MapObject::methods[] = { JS_FN("delete", delete_, 1, 0), JS_FN("keys", keys, 0, 0), JS_FN("values", values, 0, 0), - JS_FN("entries", entries, 0, 0), - JS_FN("iterator", entries, 0, 0), JS_FN("clear", clear, 0, 0), JS_FS_END }; @@ -1071,7 +1069,20 @@ JSObject * MapObject::initClass(JSContext *cx, JSObject *obj) { Rooted global(cx, &obj->asGlobal()); - return InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods); + RootedObject proto(cx, + InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods)); + if (proto) { + // Define the "entries" method. + JSFunction *fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0); + if (!fun) + return NULL; + + // Define its alias. + RootedValue funval(cx, ObjectValue(*fun)); + if (!JS_DefineProperty(cx, proto, "iterator", funval, NULL, NULL, 0)) + return NULL; + } + return proto; } template @@ -1422,14 +1433,16 @@ js_InitMapClass(JSContext *cx, HandleObject obj) class js::SetIteratorObject : public JSObject { public: - enum { TargetSlot, RangeSlot, SlotCount }; + enum { TargetSlot, KindSlot, RangeSlot, SlotCount }; static const JSFunctionSpec methods[]; - static SetIteratorObject *create(JSContext *cx, HandleObject setobj, ValueSet *data); + static SetIteratorObject *create(JSContext *cx, HandleObject setobj, ValueSet *data, + SetObject::IteratorKind kind); static void finalize(FreeOp *fop, JSObject *obj); private: static inline bool is(const Value &v); inline ValueSet::Range *range(); + inline SetObject::IteratorKind kind() const; static bool next_impl(JSContext *cx, CallArgs args); static JSBool next(JSContext *cx, unsigned argc, Value *vp); }; @@ -1466,6 +1479,14 @@ SetIteratorObject::range() return static_cast(getSlot(RangeSlot).toPrivate()); } +inline SetObject::IteratorKind +SetIteratorObject::kind() const +{ + int32_t i = getSlot(KindSlot).toInt32(); + JS_ASSERT(i == SetObject::Values || i == SetObject::Entries); + return SetObject::IteratorKind(i); +} + bool GlobalObject::initSetIteratorProto(JSContext *cx, Handle global) { @@ -1483,7 +1504,8 @@ GlobalObject::initSetIteratorProto(JSContext *cx, Handle global) } SetIteratorObject * -SetIteratorObject::create(JSContext *cx, HandleObject setobj, ValueSet *data) +SetIteratorObject::create(JSContext *cx, HandleObject setobj, ValueSet *data, + SetObject::IteratorKind kind) { Rooted global(cx, &setobj->global()); Rooted proto(cx, global->getOrCreateSetIteratorPrototype(cx)); @@ -1500,6 +1522,7 @@ SetIteratorObject::create(JSContext *cx, HandleObject setobj, ValueSet *data) return NULL; } iterobj->setSlot(TargetSlot, ObjectValue(*setobj)); + iterobj->setSlot(KindSlot, Int32Value(int32_t(kind))); iterobj->setSlot(RangeSlot, PrivateValue(range)); return static_cast(iterobj); } @@ -1529,7 +1552,23 @@ SetIteratorObject::next_impl(JSContext *cx, CallArgs args) return js_ThrowStopIteration(cx); } - args.rval().set(range->front().get()); + switch (thisobj.kind()) { + case SetObject::Values: + args.rval().set(range->front().get()); + break; + + case SetObject::Entries: { + Value pair[2] = { range->front().get(), range->front().get() }; + AutoValueArray root(cx, pair, 2); + + JSObject *pairobj = NewDenseCopiedArray(cx, 2, pair); + if (!pairobj) + return false; + args.rval().setObject(*pairobj); + break; + } + } + range->popFront(); return true; } @@ -1579,7 +1618,7 @@ const JSFunctionSpec SetObject::methods[] = { JS_FN("has", has, 1, 0), JS_FN("add", add, 1, 0), JS_FN("delete", delete_, 1, 0), - JS_FN("iterator", iterator, 0, 0), + JS_FN("entries", entries, 0, 0), JS_FN("clear", clear, 0, 0), JS_FS_END }; @@ -1588,7 +1627,22 @@ JSObject * SetObject::initClass(JSContext *cx, JSObject *obj) { Rooted global(cx, &obj->asGlobal()); - return InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods); + RootedObject proto(cx, + InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods)); + if (proto) { + // Define the "values" method. + JSFunction *fun = JS_DefineFunction(cx, proto, "values", values, 0, 0); + if (!fun) + return NULL; + + // Define its aliases. + RootedValue funval(cx, ObjectValue(*fun)); + if (!JS_DefineProperty(cx, proto, "keys", funval, NULL, NULL, 0)) + return NULL; + if (!JS_DefineProperty(cx, proto, "iterator", funval, NULL, NULL, 0)) + return NULL; + } + return proto; } void @@ -1743,22 +1797,41 @@ SetObject::delete_(JSContext *cx, unsigned argc, Value *vp) } bool -SetObject::iterator_impl(JSContext *cx, CallArgs args) +SetObject::iterator_impl(JSContext *cx, CallArgs args, IteratorKind kind) { Rooted setobj(cx, &args.thisv().toObject().asSet()); ValueSet &set = *setobj->getData(); - Rooted iterobj(cx, SetIteratorObject::create(cx, setobj, &set)); + Rooted iterobj(cx, SetIteratorObject::create(cx, setobj, &set, kind)); if (!iterobj) return false; args.rval().setObject(*iterobj); return true; } +bool +SetObject::values_impl(JSContext *cx, CallArgs args) +{ + return iterator_impl(cx, args, Values); +} + JSBool -SetObject::iterator(JSContext *cx, unsigned argc, Value *vp) +SetObject::values(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, is, iterator_impl, args); + return CallNonGenericMethod(cx, is, values_impl, args); +} + +bool +SetObject::entries_impl(JSContext *cx, CallArgs args) +{ + return iterator_impl(cx, args, Entries); +} + +JSBool +SetObject::entries(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, is, entries_impl, args); } bool diff --git a/js/src/builtin/MapObject.h b/js/src/builtin/MapObject.h index 07f19c66ca10..4e58975f61b5 100644 --- a/js/src/builtin/MapObject.h +++ b/js/src/builtin/MapObject.h @@ -127,6 +127,7 @@ class MapObject : public JSObject { class SetObject : public JSObject { public: + enum IteratorKind { Values, Entries }; static JSObject *initClass(JSContext *cx, JSObject *obj); static Class class_; private: @@ -140,6 +141,8 @@ class SetObject : public JSObject { static bool is(const Value &v); + static bool iterator_impl(JSContext *cx, CallArgs args, IteratorKind kind); + static bool size_impl(JSContext *cx, CallArgs args); static JSBool size(JSContext *cx, unsigned argc, Value *vp); static bool has_impl(JSContext *cx, CallArgs args); @@ -148,8 +151,10 @@ class SetObject : public JSObject { static JSBool add(JSContext *cx, unsigned argc, Value *vp); static bool delete_impl(JSContext *cx, CallArgs args); static JSBool delete_(JSContext *cx, unsigned argc, Value *vp); - static bool iterator_impl(JSContext *cx, CallArgs args); - static JSBool iterator(JSContext *cx, unsigned argc, Value *vp); + static bool values_impl(JSContext *cx, CallArgs args); + static JSBool values(JSContext *cx, unsigned argc, Value *vp); + static bool entries_impl(JSContext *cx, CallArgs args); + static JSBool entries(JSContext *cx, unsigned argc, Value *vp); static bool clear_impl(JSContext *cx, CallArgs args); static JSBool clear(JSContext *cx, unsigned argc, Value *vp); }; diff --git a/js/src/jit-test/tests/collections/Map-surfaces-1.js b/js/src/jit-test/tests/collections/Map-surfaces-1.js index 2b620aeeb33c..fda71bccbbba 100644 --- a/js/src/jit-test/tests/collections/Map-surfaces-1.js +++ b/js/src/jit-test/tests/collections/Map-surfaces-1.js @@ -31,6 +31,9 @@ checkMethod("get", 1); checkMethod("has", 1); checkMethod("set", 2); checkMethod("delete", 1); +checkMethod("keys", 0); +checkMethod("values", 0); +checkMethod("entries", 0); var desc = Object.getOwnPropertyDescriptor(Map.prototype, "size"); assertEq(desc.enumerable, false); @@ -39,3 +42,6 @@ assertEq(typeof desc.get, 'function'); assertEq(desc.get.length, 0); assertEq(desc.set, undefined); checkMethod("clear", 0); + +// Map.prototype.iterator and .entries are the same function object. +assertEq(Map.prototype.iterator, Map.prototype.entries); diff --git a/js/src/jit-test/tests/collections/Map-surfaces-2.js b/js/src/jit-test/tests/collections/Map-surfaces-2.js index 78d27ac761df..5333f719de38 100644 --- a/js/src/jit-test/tests/collections/Map-surfaces-2.js +++ b/js/src/jit-test/tests/collections/Map-surfaces-2.js @@ -16,6 +16,9 @@ function test(obj) { testcase(obj, Map.prototype.set, "x", 1); testcase(obj, Map.prototype.delete, "x"); testcase(obj, Map.prototype.clear); + testcase(obj, Map.prototype.keys); + testcase(obj, Map.prototype.values); + testcase(obj, Map.prototype.entries); testcase(obj, Map_size_getter); } diff --git a/js/src/jit-test/tests/collections/Set-surfaces-1.js b/js/src/jit-test/tests/collections/Set-surfaces-1.js index fe44883d026b..094846eebfdc 100644 --- a/js/src/jit-test/tests/collections/Set-surfaces-1.js +++ b/js/src/jit-test/tests/collections/Set-surfaces-1.js @@ -30,6 +30,8 @@ function checkMethod(name, arity) { checkMethod("has", 1); checkMethod("add", 1); checkMethod("delete", 1); +checkMethod("values", 0); +checkMethod("entries", 0); var desc = Object.getOwnPropertyDescriptor(Set.prototype, "size"); assertEq(desc.enumerable, false); @@ -38,3 +40,7 @@ assertEq(typeof desc.get, 'function'); assertEq(desc.get.length, 0); assertEq(desc.set, undefined); checkMethod("clear", 0); + +// Set.prototype.keys, .values, and .iterator are the same function object +assertEq(Set.prototype.keys, Set.prototype.values); +assertEq(Set.prototype.iterator, Set.prototype.values); diff --git a/js/src/jit-test/tests/collections/Set-surfaces-2.js b/js/src/jit-test/tests/collections/Set-surfaces-2.js index 20ab6a32e6fa..3fc422c155cc 100644 --- a/js/src/jit-test/tests/collections/Set-surfaces-2.js +++ b/js/src/jit-test/tests/collections/Set-surfaces-2.js @@ -15,6 +15,9 @@ function test(obj) { testcase(obj, Set.prototype.add, 12); testcase(obj, Set.prototype.delete, 12); testcase(obj, Set.prototype.clear); + testcase(obj, Set.prototype.keys); + testcase(obj, Set.prototype.values); + testcase(obj, Set.prototype.entries); testcase(obj, Set_size_getter); } diff --git a/js/src/jit-test/tests/collections/Set-values-1.js b/js/src/jit-test/tests/collections/Set-values-1.js new file mode 100644 index 000000000000..bf437c6631d1 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-values-1.js @@ -0,0 +1,14 @@ +// set.keys(), .values(), and .entries() on an empty set produce empty iterators + +var s = Set(); +var ki = s.keys(), vi = s.values(), ei = s.entries(); +var p = Object.getPrototypeOf(ki); +assertEq(Object.getPrototypeOf(vi), p); +assertEq(Object.getPrototypeOf(ei), p); + +for (let k of ki) + throw "FAIL"; +for (let v of vi) + throw "FAIL"; +for (let [k, v] of ei) + throw "FAIL"; diff --git a/js/src/jit-test/tests/collections/Set-values-2.js b/js/src/jit-test/tests/collections/Set-values-2.js new file mode 100644 index 000000000000..c9304ff8f352 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-values-2.js @@ -0,0 +1,18 @@ +// set.keys() and set.values() return iterators over the elements +// and set.entries() returns an iterator that yields pairs [e, e]. + +load(libdir + "asserts.js"); + +var data = [1, 2, 3, 4]; +var s = Set(data); + +var ki = s.keys(); +assertEq(ki.next(), 1); +assertEq(ki.next(), 2); +assertEq(ki.next(), 3); +assertEq(ki.next(), 4); +assertThrowsValue(function () { ki.next(); }, StopIteration); + +assertEq([...s.keys()].toSource(), data.toSource()); +assertEq([...s.values()].toSource(), data.toSource()); +assertEq([...s.entries()].toSource(), [[1, 1], [2, 2], [3, 3], [4, 4]].toSource());