# HG changeset patch # Parent c8e47b184aba8cf8665a7d53ca74889390cb1d0e Bug 869996 - Implements Set.prototype.{keys, values, entries}; r=bbenvie diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -1414,24 +1414,26 @@ js_InitMapClass(JSContext *cx, HandleObj } /*** SetIterator *********************************************************************************/ 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); }; inline js::SetIteratorObject & JSObject::asSetIterator() { JS_ASSERT(isSetIterator()); @@ -1458,16 +1460,24 @@ const JSFunctionSpec SetIteratorObject:: }; inline ValueSet::Range * SetIteratorObject::range() { return static_cast(getSlot(RangeSlot).toPrivate()); } +inline SetObject::IteratorKind +MapIteratorObject::kind() const +{ + int32_t i = getSlot(KindSlot).toInt32; + JS_ASSERT(i == SetObject::Keys || i == SetObject::Values || i == SetObject::Entries); + return SetObject::IteratorKind(i); +} + bool GlobalObject::initSetIteratorProto(JSContext *cx, Handle global) { JSObject *base = global->getOrCreateIteratorPrototype(cx); if (!base) return false; RootedObject proto(cx, NewObjectWithGivenProto(cx, &SetIteratorClass, base, global)); if (!proto) @@ -1475,33 +1485,35 @@ GlobalObject::initSetIteratorProto(JSCon proto->setSlot(SetIteratorObject::RangeSlot, PrivateValue(NULL)); if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods)) return false; global->setReservedSlot(SET_ITERATOR_PROTO, ObjectValue(*proto)); return true; } 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)); if (!proto) return NULL; ValueSet::Range *range = cx->new_(data->all()); if (!range) return NULL; JSObject *iterobj = NewObjectWithGivenProto(cx, &SetIteratorClass, proto, global); if (!iterobj) { js_delete(range); return NULL; } iterobj->setSlot(TargetSlot, ObjectValue(*setobj)); + iterobj->setSlot(KindSlot, Int32Value(int32_t(kind))); iterobj->setSlot(RangeSlot, PrivateValue(range)); return static_cast(iterobj); } void SetIteratorObject::finalize(FreeOp *fop, JSObject *obj) { fop->delete_(obj->asSetIterator().range()); @@ -1521,17 +1533,31 @@ SetIteratorObject::next_impl(JSContext * if (!range) return js_ThrowStopIteration(cx); if (range->empty()) { js_delete(range); thisobj.setReservedSlot(RangeSlot, PrivateValue(NULL)); return js_ThrowStopIteration(cx); } - args.rval().set(range->front().get()); + SetObject::IteratorKind kind = thisobj.kind(); + if (kind == SetObject::Keys) { + args.rval().set(range->front().get()); + } else if (kind == SetObject::Values) { + args.rval().set(range->front().get()); + } else { + 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); + } + range->popFront(); return true; } JSBool SetIteratorObject::next(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); @@ -1571,16 +1597,19 @@ const JSPropertySpec SetObject::properti JS_PSG("size", size, 0), JS_PS_END }; const JSFunctionSpec SetObject::methods[] = { JS_FN("has", has, 1, 0), JS_FN("add", add, 1, 0), 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", iterator, 0, 0), JS_FN("clear", clear, 0, 0), JS_FS_END }; JSObject * SetObject::initClass(JSContext *cx, JSObject *obj) { @@ -1735,32 +1764,64 @@ SetObject::delete_impl(JSContext *cx, Ca JSBool SetObject::delete_(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); return CallNonGenericMethod(cx, args); } 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::keys_impl(JSContext *cx, CallArgs args) +{ + return iterator_impl(cx, args, Keys); +} + JSBool -SetObject::iterator(JSContext *cx, unsigned argc, Value *vp) +SetObject::keys(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, is, iterator_impl, args); + return CallNonGenericMethod(cx, is, keys_impl, args); +} + +bool +SetObject::values_impl(JSContext *cx, CallArgs args) +{ + return iterator_impl(cx, args, Values); +} + +JSBool +SetObject::values(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + 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 SetObject::clear_impl(JSContext *cx, CallArgs args) { Rooted setobj(cx, &args.thisv().toObject().asSet()); if (!setobj->getData()->clear()) { js_ReportOutOfMemory(cx); diff --git a/js/src/builtin/MapObject.h b/js/src/builtin/MapObject.h --- a/js/src/builtin/MapObject.h +++ b/js/src/builtin/MapObject.h @@ -122,39 +122,46 @@ class MapObject : public JSObject { 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); }; class SetObject : public JSObject { public: + enum IteratorKind { Keys, Values, Entries }; static JSObject *initClass(JSContext *cx, JSObject *obj); static Class class_; private: static const JSPropertySpec properties[]; static const JSFunctionSpec methods[]; ValueSet *getData() { return static_cast(getPrivate()); } static ValueSet & extract(CallReceiver call); static void mark(JSTracer *trc, JSObject *obj); static void finalize(FreeOp *fop, JSObject *obj); static JSBool construct(JSContext *cx, unsigned argc, Value *vp); 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); static JSBool has(JSContext *cx, unsigned argc, Value *vp); static bool add_impl(JSContext *cx, CallArgs args); 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 keys_impl(JSContext *cx, CallArgs args); + static JSBool keys(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); }; } /* namespace js */ extern JSObject * js_InitMapClass(JSContext *cx, js::HandleObject obj); 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 --- /dev/null +++ b/js/src/jit-test/tests/collections/Set-values-1.js @@ -0,0 +1,14 @@ +// map.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 or ki) + throw "FAIL"; +for (let v of vi) + throw "FAIL"; +for (let [k, v] of ei) + throw "FAIL";