1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "builtin/MapObject.h"
8 
9 #include "jscntxt.h"
10 #include "jsiter.h"
11 #include "jsobj.h"
12 
13 #include "ds/OrderedHashTable.h"
14 #include "gc/Marking.h"
15 #include "js/Utility.h"
16 #include "vm/GlobalObject.h"
17 #include "vm/Interpreter.h"
18 #include "vm/SelfHosting.h"
19 #include "vm/Symbol.h"
20 
21 #include "jsobjinlines.h"
22 
23 #include "vm/Interpreter-inl.h"
24 #include "vm/NativeObject-inl.h"
25 
26 using namespace js;
27 
28 using mozilla::ArrayLength;
29 using mozilla::IsNaN;
30 using mozilla::NumberEqualsInt32;
31 
32 using JS::DoubleNaNValue;
33 using JS::ForOfIterator;
34 
35 
36 /*** HashableValue *******************************************************************************/
37 
38 bool
setValue(JSContext * cx,HandleValue v)39 HashableValue::setValue(JSContext* cx, HandleValue v)
40 {
41     if (v.isString()) {
42         // Atomize so that hash() and operator==() are fast and infallible.
43         JSString* str = AtomizeString(cx, v.toString(), DoNotPinAtom);
44         if (!str)
45             return false;
46         value = StringValue(str);
47     } else if (v.isDouble()) {
48         double d = v.toDouble();
49         int32_t i;
50         if (NumberEqualsInt32(d, &i)) {
51             // Normalize int32_t-valued doubles to int32_t for faster hashing and testing.
52             value = Int32Value(i);
53         } else if (IsNaN(d)) {
54             // NaNs with different bits must hash and test identically.
55             value = DoubleNaNValue();
56         } else {
57             value = v;
58         }
59     } else {
60         value = v;
61     }
62 
63     MOZ_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() || value.isNumber() ||
64                value.isString() || value.isSymbol() || value.isObject());
65     return true;
66 }
67 
68 static HashNumber
HashValue(const Value & v,const mozilla::HashCodeScrambler & hcs)69 HashValue(const Value& v, const mozilla::HashCodeScrambler& hcs)
70 {
71     // HashableValue::setValue normalizes values so that the SameValue relation
72     // on HashableValues is the same as the == relationship on
73     // value.asRawBits(). So why not just return that? Security.
74     //
75     // To avoid revealing GC of atoms, string-based hash codes are computed
76     // from the string contents rather than any pointer; to avoid revealing
77     // addresses, pointer-based hash codes are computed using the
78     // HashCodeScrambler.
79 
80     if (v.isString())
81         return v.toString()->asAtom().hash();
82     if (v.isSymbol())
83         return v.toSymbol()->hash();
84     if (v.isObject())
85         return hcs.scramble(v.asRawBits());
86 
87     MOZ_ASSERT(v.isNull() || !v.isGCThing(), "do not reveal pointers via hash codes");
88     return v.asRawBits();
89 }
90 
91 HashNumber
hash(const mozilla::HashCodeScrambler & hcs) const92 HashableValue::hash(const mozilla::HashCodeScrambler& hcs) const
93 {
94     return HashValue(value, hcs);
95 }
96 
97 bool
operator ==(const HashableValue & other) const98 HashableValue::operator==(const HashableValue& other) const
99 {
100     // Two HashableValues are equal if they have equal bits.
101     bool b = (value.asRawBits() == other.value.asRawBits());
102 
103 #ifdef DEBUG
104     bool same;
105     JS::RootingContext* rcx = GetJSContextFromMainThread();
106     RootedValue valueRoot(rcx, value);
107     RootedValue otherRoot(rcx, other.value);
108     MOZ_ASSERT(SameValue(nullptr, valueRoot, otherRoot, &same));
109     MOZ_ASSERT(same == b);
110 #endif
111     return b;
112 }
113 
114 HashableValue
mark(JSTracer * trc) const115 HashableValue::mark(JSTracer* trc) const
116 {
117     HashableValue hv(*this);
118     TraceEdge(trc, &hv.value, "key");
119     return hv;
120 }
121 
122 
123 /*** MapIterator *********************************************************************************/
124 
125 namespace {
126 
127 } /* anonymous namespace */
128 
129 static const ClassOps MapIteratorObjectClassOps = {
130     nullptr, /* addProperty */
131     nullptr, /* delProperty */
132     nullptr, /* getProperty */
133     nullptr, /* setProperty */
134     nullptr, /* enumerate */
135     nullptr, /* resolve */
136     nullptr, /* mayResolve */
137     MapIteratorObject::finalize
138 };
139 
140 const Class MapIteratorObject::class_ = {
141     "Map Iterator",
142     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount) |
143     JSCLASS_FOREGROUND_FINALIZE,
144     &MapIteratorObjectClassOps
145 };
146 
147 const JSFunctionSpec MapIteratorObject::methods[] = {
148     JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0),
149     JS_FS_END
150 };
151 
152 static inline ValueMap::Range*
MapIteratorObjectRange(NativeObject * obj)153 MapIteratorObjectRange(NativeObject* obj)
154 {
155     MOZ_ASSERT(obj->is<MapIteratorObject>());
156     return static_cast<ValueMap::Range*>(obj->getSlot(MapIteratorObject::RangeSlot).toPrivate());
157 }
158 
159 inline MapObject::IteratorKind
kind() const160 MapIteratorObject::kind() const
161 {
162     int32_t i = getSlot(KindSlot).toInt32();
163     MOZ_ASSERT(i == MapObject::Keys || i == MapObject::Values || i == MapObject::Entries);
164     return MapObject::IteratorKind(i);
165 }
166 
167 bool
initMapIteratorProto(JSContext * cx,Handle<GlobalObject * > global)168 GlobalObject::initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
169 {
170     Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
171     if (!base)
172         return false;
173     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, base));
174     if (!proto)
175         return false;
176     if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods) ||
177         !DefineToStringTag(cx, proto, cx->names().MapIterator))
178     {
179         return false;
180     }
181     global->setReservedSlot(MAP_ITERATOR_PROTO, ObjectValue(*proto));
182     return true;
183 }
184 
185 MapIteratorObject*
create(JSContext * cx,HandleObject mapobj,ValueMap * data,MapObject::IteratorKind kind)186 MapIteratorObject::create(JSContext* cx, HandleObject mapobj, ValueMap* data,
187                           MapObject::IteratorKind kind)
188 {
189     Rooted<GlobalObject*> global(cx, &mapobj->global());
190     Rooted<JSObject*> proto(cx, GlobalObject::getOrCreateMapIteratorPrototype(cx, global));
191     if (!proto)
192         return nullptr;
193 
194     ValueMap::Range* range = cx->new_<ValueMap::Range>(data->all());
195     if (!range)
196         return nullptr;
197 
198     MapIteratorObject* iterobj = NewObjectWithGivenProto<MapIteratorObject>(cx, proto);
199     if (!iterobj) {
200         js_delete(range);
201         return nullptr;
202     }
203     iterobj->setSlot(TargetSlot, ObjectValue(*mapobj));
204     iterobj->setSlot(RangeSlot, PrivateValue(range));
205     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
206     return iterobj;
207 }
208 
209 void
finalize(FreeOp * fop,JSObject * obj)210 MapIteratorObject::finalize(FreeOp* fop, JSObject* obj)
211 {
212     MOZ_ASSERT(fop->onMainThread());
213     fop->delete_(MapIteratorObjectRange(static_cast<NativeObject*>(obj)));
214 }
215 
216 bool
next(Handle<MapIteratorObject * > mapIterator,HandleArrayObject resultPairObj,JSContext * cx)217 MapIteratorObject::next(Handle<MapIteratorObject*> mapIterator, HandleArrayObject resultPairObj,
218                         JSContext* cx)
219 {
220     // Check invariants for inlined _GetNextMapEntryForIterator.
221 
222     // The array should be tenured, so that post-barrier can be done simply.
223     MOZ_ASSERT(resultPairObj->isTenured());
224 
225     // The array elements should be fixed.
226     MOZ_ASSERT(resultPairObj->hasFixedElements());
227     MOZ_ASSERT(resultPairObj->getDenseInitializedLength() == 2);
228     MOZ_ASSERT(resultPairObj->getDenseCapacity() >= 2);
229 
230     ValueMap::Range* range = MapIteratorObjectRange(mapIterator);
231     if (!range || range->empty()) {
232         js_delete(range);
233         mapIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr));
234         return true;
235     }
236     switch (mapIterator->kind()) {
237       case MapObject::Keys:
238         resultPairObj->setDenseElementWithType(cx, 0, range->front().key.get());
239         break;
240 
241       case MapObject::Values:
242         resultPairObj->setDenseElementWithType(cx, 1, range->front().value);
243         break;
244 
245       case MapObject::Entries: {
246         resultPairObj->setDenseElementWithType(cx, 0, range->front().key.get());
247         resultPairObj->setDenseElementWithType(cx, 1, range->front().value);
248         break;
249       }
250     }
251     range->popFront();
252     return false;
253 }
254 
255 /* static */ JSObject*
createResultPair(JSContext * cx)256 MapIteratorObject::createResultPair(JSContext* cx)
257 {
258     RootedArrayObject resultPairObj(cx, NewDenseFullyAllocatedArray(cx, 2, nullptr, TenuredObject));
259     if (!resultPairObj)
260         return nullptr;
261 
262     Rooted<TaggedProto> proto(cx, resultPairObj->taggedProto());
263     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto);
264     if (!group)
265         return nullptr;
266     resultPairObj->setGroup(group);
267 
268     resultPairObj->setDenseInitializedLength(2);
269     resultPairObj->initDenseElement(0, NullValue());
270     resultPairObj->initDenseElement(1, NullValue());
271 
272     // See comments in MapIteratorObject::next.
273     AddTypePropertyId(cx, resultPairObj, JSID_VOID, TypeSet::UnknownType());
274 
275     return resultPairObj;
276 }
277 
278 
279 /*** Map *****************************************************************************************/
280 
281 const ClassOps MapObject::classOps_ = {
282     nullptr, // addProperty
283     nullptr, // delProperty
284     nullptr, // getProperty
285     nullptr, // setProperty
286     nullptr, // enumerate
287     nullptr, // resolve
288     nullptr, // mayResolve
289     finalize,
290     nullptr, // call
291     nullptr, // hasInstance
292     nullptr, // construct
293     mark
294 };
295 
296 const Class MapObject::class_ = {
297     "Map",
298     JSCLASS_HAS_PRIVATE |
299     JSCLASS_HAS_RESERVED_SLOTS(MapObject::SlotCount) |
300     JSCLASS_HAS_CACHED_PROTO(JSProto_Map) |
301     JSCLASS_FOREGROUND_FINALIZE,
302     &MapObject::classOps_
303 };
304 
305 const JSPropertySpec MapObject::properties[] = {
306     JS_PSG("size", size, 0),
307     JS_PS_END
308 };
309 
310 const JSFunctionSpec MapObject::methods[] = {
311     JS_FN("get", get, 1, 0),
312     JS_FN("has", has, 1, 0),
313     JS_FN("set", set, 2, 0),
314     JS_FN("delete", delete_, 1, 0),
315     JS_FN("keys", keys, 0, 0),
316     JS_FN("values", values, 0, 0),
317     JS_FN("clear", clear, 0, 0),
318     JS_SELF_HOSTED_FN("forEach", "MapForEach", 2, 0),
319     JS_FS_END
320 };
321 
322 const JSPropertySpec MapObject::staticProperties[] = {
323     JS_SELF_HOSTED_SYM_GET(species, "MapSpecies", 0),
324     JS_PS_END
325 };
326 
327 static JSObject*
InitClass(JSContext * cx,Handle<GlobalObject * > global,const Class * clasp,JSProtoKey key,Native construct,const JSPropertySpec * properties,const JSFunctionSpec * methods,const JSPropertySpec * staticProperties)328 InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSProtoKey key, Native construct,
329           const JSPropertySpec* properties, const JSFunctionSpec* methods,
330           const JSPropertySpec* staticProperties)
331 {
332     RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
333     if (!proto)
334         return nullptr;
335 
336     Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(key, cx), 0));
337     if (!ctor ||
338         !JS_DefineProperties(cx, ctor, staticProperties) ||
339         !LinkConstructorAndPrototype(cx, ctor, proto) ||
340         !DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
341         !GlobalObject::initBuiltinConstructor(cx, global, key, ctor, proto))
342     {
343         return nullptr;
344     }
345     return proto;
346 }
347 
348 JSObject*
initClass(JSContext * cx,JSObject * obj)349 MapObject::initClass(JSContext* cx, JSObject* obj)
350 {
351     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
352     RootedObject proto(cx,
353         InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods,
354                   staticProperties));
355     if (proto) {
356         // Define the "entries" method.
357         JSFunction* fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0);
358         if (!fun)
359             return nullptr;
360 
361         // Define its alias.
362         RootedValue funval(cx, ObjectValue(*fun));
363         RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
364         if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
365             return nullptr;
366 
367         // Define Map.prototype[@@toStringTag].
368         if (!DefineToStringTag(cx, proto, cx->names().Map))
369             return nullptr;
370     }
371     return proto;
372 }
373 
374 template <class Range>
375 static void
MarkKey(Range & r,const HashableValue & key,JSTracer * trc)376 MarkKey(Range& r, const HashableValue& key, JSTracer* trc)
377 {
378     HashableValue newKey = key.mark(trc);
379 
380     if (newKey.get() != key.get()) {
381         // The hash function only uses the bits of the Value, so it is safe to
382         // rekey even when the object or string has been modified by the GC.
383         r.rekeyFront(newKey);
384     }
385 }
386 
387 void
mark(JSTracer * trc,JSObject * obj)388 MapObject::mark(JSTracer* trc, JSObject* obj)
389 {
390     if (ValueMap* map = obj->as<MapObject>().getData()) {
391         for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
392             MarkKey(r, r.front().key, trc);
393             TraceEdge(trc, &r.front().value, "value");
394         }
395     }
396 }
397 
398 struct js::UnbarrieredHashPolicy {
399     typedef Value Lookup;
hashjs::UnbarrieredHashPolicy400     static HashNumber hash(const Lookup& v, const mozilla::HashCodeScrambler& hcs) {
401         return HashValue(v, hcs);
402     }
matchjs::UnbarrieredHashPolicy403     static bool match(const Value& k, const Lookup& l) { return k == l; }
isEmptyjs::UnbarrieredHashPolicy404     static bool isEmpty(const Value& v) { return v.isMagic(JS_HASH_KEY_EMPTY); }
makeEmptyjs::UnbarrieredHashPolicy405     static void makeEmpty(Value* vp) { vp->setMagic(JS_HASH_KEY_EMPTY); }
406 };
407 
408 using NurseryKeysVector = Vector<JSObject*, 0, SystemAllocPolicy>;
409 
410 template <typename TableObject>
411 static NurseryKeysVector*
GetNurseryKeys(TableObject * t)412 GetNurseryKeys(TableObject* t)
413 {
414     Value value = t->getReservedSlot(TableObject::NurseryKeysSlot);
415     return reinterpret_cast<NurseryKeysVector*>(value.toPrivate());
416 }
417 
418 template <typename TableObject>
419 static NurseryKeysVector*
AllocNurseryKeys(TableObject * t)420 AllocNurseryKeys(TableObject* t)
421 {
422     MOZ_ASSERT(!GetNurseryKeys(t));
423     auto keys = js_new<NurseryKeysVector>();
424     if (!keys)
425         return nullptr;
426 
427     t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(keys));
428     return keys;
429 }
430 
431 template <typename TableObject>
432 static void
DeleteNurseryKeys(TableObject * t)433 DeleteNurseryKeys(TableObject* t)
434 {
435     auto keys = GetNurseryKeys(t);
436     MOZ_ASSERT(keys);
437     js_delete(keys);
438     t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(nullptr));
439 }
440 
441 // A generic store buffer entry that traces all nursery keys for an ordered hash
442 // map or set.
443 template <typename ObjectT>
444 class js::OrderedHashTableRef : public gc::BufferableRef
445 {
446     ObjectT* object;
447 
448   public:
OrderedHashTableRef(ObjectT * obj)449     explicit OrderedHashTableRef(ObjectT* obj) : object(obj) {}
450 
trace(JSTracer * trc)451     void trace(JSTracer* trc) override {
452         auto realTable = object->getData();
453         auto unbarrieredTable = reinterpret_cast<typename ObjectT::UnbarrieredTable*>(realTable);
454         NurseryKeysVector* keys = GetNurseryKeys(object);
455         MOZ_ASSERT(keys);
456         for (JSObject* obj : *keys) {
457             MOZ_ASSERT(obj);
458             Value key = ObjectValue(*obj);
459             Value prior = key;
460             MOZ_ASSERT(unbarrieredTable->hash(key) ==
461                        realTable->hash(*reinterpret_cast<HashableValue*>(&key)));
462             TraceManuallyBarrieredEdge(trc, &key, "ordered hash table key");
463             unbarrieredTable->rekeyOneEntry(prior, key);
464         }
465         DeleteNurseryKeys(object);
466     }
467 };
468 
469 template <typename ObjectT>
470 inline static MOZ_MUST_USE bool
WriteBarrierPostImpl(JSRuntime * rt,ObjectT * obj,const Value & keyValue)471 WriteBarrierPostImpl(JSRuntime* rt, ObjectT* obj, const Value& keyValue)
472 {
473     if (MOZ_LIKELY(!keyValue.isObject()))
474         return true;
475 
476     JSObject* key = &keyValue.toObject();
477     if (!IsInsideNursery(key))
478         return true;
479 
480     NurseryKeysVector* keys = GetNurseryKeys(obj);
481     if (!keys) {
482         keys = AllocNurseryKeys(obj);
483         if (!keys)
484             return false;
485 
486         rt->gc.storeBuffer.putGeneric(OrderedHashTableRef<ObjectT>(obj));
487     }
488 
489     if (!keys->append(key))
490         return false;
491 
492     return true;
493 }
494 
495 inline static MOZ_MUST_USE bool
WriteBarrierPost(JSRuntime * rt,MapObject * map,const Value & key)496 WriteBarrierPost(JSRuntime* rt, MapObject* map, const Value& key)
497 {
498     return WriteBarrierPostImpl(rt, map, key);
499 }
500 
501 inline static MOZ_MUST_USE bool
WriteBarrierPost(JSRuntime * rt,SetObject * set,const Value & key)502 WriteBarrierPost(JSRuntime* rt, SetObject* set, const Value& key)
503 {
504     return WriteBarrierPostImpl(rt, set, key);
505 }
506 
507 bool
getKeysAndValuesInterleaved(JSContext * cx,HandleObject obj,JS::MutableHandle<GCVector<JS::Value>> entries)508 MapObject::getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
509                                        JS::MutableHandle<GCVector<JS::Value>> entries)
510 {
511     ValueMap* map = obj->as<MapObject>().getData();
512     if (!map)
513         return false;
514 
515     for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
516         if (!entries.append(r.front().key.get()) ||
517             !entries.append(r.front().value))
518         {
519             return false;
520         }
521     }
522 
523     return true;
524 }
525 
526 bool
set(JSContext * cx,HandleObject obj,HandleValue k,HandleValue v)527 MapObject::set(JSContext* cx, HandleObject obj, HandleValue k, HandleValue v)
528 {
529     ValueMap* map = obj->as<MapObject>().getData();
530     if (!map)
531         return false;
532 
533     Rooted<HashableValue> key(cx);
534     if (!key.setValue(cx, k))
535         return false;
536 
537     HeapPtr<Value> rval(v);
538     if (!WriteBarrierPost(cx->runtime(), &obj->as<MapObject>(), key.value()) ||
539         !map->put(key, rval))
540     {
541         ReportOutOfMemory(cx);
542         return false;
543     }
544 
545     return true;
546 }
547 
548 MapObject*
create(JSContext * cx,HandleObject proto)549 MapObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
550 {
551     auto map = cx->make_unique<ValueMap>(cx->runtime(),
552                                          cx->compartment()->randomHashCodeScrambler());
553     if (!map || !map->init()) {
554         ReportOutOfMemory(cx);
555         return nullptr;
556     }
557 
558     MapObject* mapObj = NewObjectWithClassProto<MapObject>(cx,  proto);
559     if (!mapObj)
560         return nullptr;
561 
562     mapObj->setPrivate(map.release());
563     mapObj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
564     return mapObj;
565 }
566 
567 void
finalize(FreeOp * fop,JSObject * obj)568 MapObject::finalize(FreeOp* fop, JSObject* obj)
569 {
570     MOZ_ASSERT(fop->onMainThread());
571     if (ValueMap* map = obj->as<MapObject>().getData())
572         fop->delete_(map);
573 }
574 
575 bool
construct(JSContext * cx,unsigned argc,Value * vp)576 MapObject::construct(JSContext* cx, unsigned argc, Value* vp)
577 {
578     CallArgs args = CallArgsFromVp(argc, vp);
579 
580     if (!ThrowIfNotConstructing(cx, args, "Map"))
581         return false;
582 
583     RootedObject proto(cx);
584     RootedObject newTarget(cx, &args.newTarget().toObject());
585     if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
586         return false;
587 
588     Rooted<MapObject*> obj(cx, MapObject::create(cx, proto));
589     if (!obj)
590         return false;
591 
592     if (!args.get(0).isNullOrUndefined()) {
593         FixedInvokeArgs<1> args2(cx);
594         args2[0].set(args[0]);
595 
596         RootedValue thisv(cx, ObjectValue(*obj));
597         if (!CallSelfHostedFunction(cx, cx->names().MapConstructorInit, thisv, args2, args2.rval()))
598             return false;
599     }
600 
601     args.rval().setObject(*obj);
602     return true;
603 }
604 
605 bool
is(HandleValue v)606 MapObject::is(HandleValue v)
607 {
608     return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().as<MapObject>().getPrivate();
609 }
610 
611 bool
is(HandleObject o)612 MapObject::is(HandleObject o)
613 {
614     return o->hasClass(&class_) && o->as<MapObject>().getPrivate();
615 }
616 
617 #define ARG0_KEY(cx, args, key)                                               \
618     Rooted<HashableValue> key(cx);                                            \
619     if (args.length() > 0 && !key.setValue(cx, args[0]))                      \
620         return false
621 
622 ValueMap&
extract(HandleObject o)623 MapObject::extract(HandleObject o)
624 {
625     MOZ_ASSERT(o->hasClass(&MapObject::class_));
626     return *o->as<MapObject>().getData();
627 }
628 
629 ValueMap&
extract(const CallArgs & args)630 MapObject::extract(const CallArgs& args)
631 {
632     MOZ_ASSERT(args.thisv().isObject());
633     MOZ_ASSERT(args.thisv().toObject().hasClass(&MapObject::class_));
634     return *args.thisv().toObject().as<MapObject>().getData();
635 }
636 
637 uint32_t
size(JSContext * cx,HandleObject obj)638 MapObject::size(JSContext* cx, HandleObject obj)
639 {
640     ValueMap& map = extract(obj);
641     static_assert(sizeof(map.count()) <= sizeof(uint32_t),
642                   "map count must be precisely representable as a JS number");
643     return map.count();
644 }
645 
646 bool
size_impl(JSContext * cx,const CallArgs & args)647 MapObject::size_impl(JSContext* cx, const CallArgs& args)
648 {
649     RootedObject obj(cx, &args.thisv().toObject());
650     args.rval().setNumber(size(cx, obj));
651     return true;
652 }
653 
654 bool
size(JSContext * cx,unsigned argc,Value * vp)655 MapObject::size(JSContext* cx, unsigned argc, Value* vp)
656 {
657     CallArgs args = CallArgsFromVp(argc, vp);
658     return CallNonGenericMethod<MapObject::is, MapObject::size_impl>(cx, args);
659 }
660 
661 bool
get(JSContext * cx,HandleObject obj,HandleValue key,MutableHandleValue rval)662 MapObject::get(JSContext* cx, HandleObject obj,
663                HandleValue key, MutableHandleValue rval)
664 {
665     ValueMap& map = extract(obj);
666     Rooted<HashableValue> k(cx);
667 
668     if (!k.setValue(cx, key))
669         return false;
670 
671     if (ValueMap::Entry* p = map.get(k))
672         rval.set(p->value);
673     else
674         rval.setUndefined();
675 
676     return true;
677 }
678 
679 bool
get_impl(JSContext * cx,const CallArgs & args)680 MapObject::get_impl(JSContext* cx, const CallArgs& args)
681 {
682     RootedObject obj(cx, &args.thisv().toObject());
683     return get(cx, obj, args.get(0), args.rval());
684 }
685 
686 bool
get(JSContext * cx,unsigned argc,Value * vp)687 MapObject::get(JSContext* cx, unsigned argc, Value* vp)
688 {
689     CallArgs args = CallArgsFromVp(argc, vp);
690     return CallNonGenericMethod<MapObject::is, MapObject::get_impl>(cx, args);
691 }
692 
693 bool
has(JSContext * cx,HandleObject obj,HandleValue key,bool * rval)694 MapObject::has(JSContext* cx, HandleObject obj, HandleValue key, bool* rval)
695 {
696     ValueMap& map = extract(obj);
697     Rooted<HashableValue> k(cx);
698 
699     if (!k.setValue(cx, key))
700         return false;
701 
702     *rval = map.has(k);
703     return true;
704 }
705 
706 bool
has_impl(JSContext * cx,const CallArgs & args)707 MapObject::has_impl(JSContext* cx, const CallArgs& args)
708 {
709     bool found;
710     RootedObject obj(cx, &args.thisv().toObject());
711     if (has(cx, obj, args.get(0), &found)) {
712         args.rval().setBoolean(found);
713         return true;
714     }
715     return false;
716 }
717 
718 bool
has(JSContext * cx,unsigned argc,Value * vp)719 MapObject::has(JSContext* cx, unsigned argc, Value* vp)
720 {
721     CallArgs args = CallArgsFromVp(argc, vp);
722     return CallNonGenericMethod<MapObject::is, MapObject::has_impl>(cx, args);
723 }
724 
725 bool
set_impl(JSContext * cx,const CallArgs & args)726 MapObject::set_impl(JSContext* cx, const CallArgs& args)
727 {
728     MOZ_ASSERT(MapObject::is(args.thisv()));
729 
730     ValueMap& map = extract(args);
731     ARG0_KEY(cx, args, key);
732     HeapPtr<Value> rval(args.get(1));
733     if (!WriteBarrierPost(cx->runtime(), &args.thisv().toObject().as<MapObject>(), key.value()) ||
734         !map.put(key, rval))
735     {
736         ReportOutOfMemory(cx);
737         return false;
738     }
739 
740     args.rval().set(args.thisv());
741     return true;
742 }
743 
744 bool
set(JSContext * cx,unsigned argc,Value * vp)745 MapObject::set(JSContext* cx, unsigned argc, Value* vp)
746 {
747     CallArgs args = CallArgsFromVp(argc, vp);
748     return CallNonGenericMethod<MapObject::is, MapObject::set_impl>(cx, args);
749 }
750 
751 bool
delete_(JSContext * cx,HandleObject obj,HandleValue key,bool * rval)752 MapObject::delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
753 {
754     ValueMap &map = extract(obj);
755     Rooted<HashableValue> k(cx);
756 
757     if (!k.setValue(cx, key))
758         return false;
759 
760     if (!map.remove(k, rval)) {
761         ReportOutOfMemory(cx);
762         return false;
763     }
764     return true;
765 }
766 
767 bool
delete_impl(JSContext * cx,const CallArgs & args)768 MapObject::delete_impl(JSContext *cx, const CallArgs& args)
769 {
770     // MapObject::mark does not mark deleted entries. Incremental GC therefore
771     // requires that no HeapPtr<Value> objects pointing to heap values be left
772     // alive in the ValueMap.
773     //
774     // OrderedHashMap::remove() doesn't destroy the removed entry. It merely
775     // calls OrderedHashMap::MapOps::makeEmpty. But that is sufficient, because
776     // makeEmpty clears the value by doing e->value = Value(), and in the case
777     // of a ValueMap, Value() means HeapPtr<Value>(), which is the same as
778     // HeapPtr<Value>(UndefinedValue()).
779     MOZ_ASSERT(MapObject::is(args.thisv()));
780 
781     ValueMap& map = extract(args);
782     ARG0_KEY(cx, args, key);
783     bool found;
784     if (!map.remove(key, &found)) {
785         ReportOutOfMemory(cx);
786         return false;
787     }
788     args.rval().setBoolean(found);
789     return true;
790 }
791 
792 bool
delete_(JSContext * cx,unsigned argc,Value * vp)793 MapObject::delete_(JSContext* cx, unsigned argc, Value* vp)
794 {
795     CallArgs args = CallArgsFromVp(argc, vp);
796     return CallNonGenericMethod<MapObject::is, MapObject::delete_impl>(cx, args);
797 }
798 
799 bool
iterator(JSContext * cx,IteratorKind kind,HandleObject obj,MutableHandleValue iter)800 MapObject::iterator(JSContext* cx, IteratorKind kind,
801                     HandleObject obj, MutableHandleValue iter)
802 {
803     ValueMap& map = extract(obj);
804     Rooted<JSObject*> iterobj(cx, MapIteratorObject::create(cx, obj, &map, kind));
805     return iterobj && (iter.setObject(*iterobj), true);
806 }
807 
808 bool
iterator_impl(JSContext * cx,const CallArgs & args,IteratorKind kind)809 MapObject::iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind)
810 {
811     RootedObject obj(cx, &args.thisv().toObject());
812     return iterator(cx, kind, obj, args.rval());
813 }
814 
815 bool
keys_impl(JSContext * cx,const CallArgs & args)816 MapObject::keys_impl(JSContext* cx, const CallArgs& args)
817 {
818     return iterator_impl(cx, args, Keys);
819 }
820 
821 bool
keys(JSContext * cx,unsigned argc,Value * vp)822 MapObject::keys(JSContext* cx, unsigned argc, Value* vp)
823 {
824     CallArgs args = CallArgsFromVp(argc, vp);
825     return CallNonGenericMethod(cx, is, keys_impl, args);
826 }
827 
828 bool
values_impl(JSContext * cx,const CallArgs & args)829 MapObject::values_impl(JSContext* cx, const CallArgs& args)
830 {
831     return iterator_impl(cx, args, Values);
832 }
833 
834 bool
values(JSContext * cx,unsigned argc,Value * vp)835 MapObject::values(JSContext* cx, unsigned argc, Value* vp)
836 {
837     CallArgs args = CallArgsFromVp(argc, vp);
838     return CallNonGenericMethod(cx, is, values_impl, args);
839 }
840 
841 bool
entries_impl(JSContext * cx,const CallArgs & args)842 MapObject::entries_impl(JSContext* cx, const CallArgs& args)
843 {
844     return iterator_impl(cx, args, Entries);
845 }
846 
847 bool
entries(JSContext * cx,unsigned argc,Value * vp)848 MapObject::entries(JSContext* cx, unsigned argc, Value* vp)
849 {
850     CallArgs args = CallArgsFromVp(argc, vp);
851     return CallNonGenericMethod(cx, is, entries_impl, args);
852 }
853 
854 bool
clear_impl(JSContext * cx,const CallArgs & args)855 MapObject::clear_impl(JSContext* cx, const CallArgs& args)
856 {
857     RootedObject obj(cx, &args.thisv().toObject());
858     args.rval().setUndefined();
859     return clear(cx, obj);
860 }
861 
862 bool
clear(JSContext * cx,unsigned argc,Value * vp)863 MapObject::clear(JSContext* cx, unsigned argc, Value* vp)
864 {
865     CallArgs args = CallArgsFromVp(argc, vp);
866     return CallNonGenericMethod(cx, is, clear_impl, args);
867 }
868 
869 bool
clear(JSContext * cx,HandleObject obj)870 MapObject::clear(JSContext* cx, HandleObject obj)
871 {
872     ValueMap& map = extract(obj);
873     if (!map.clear()) {
874         ReportOutOfMemory(cx);
875         return false;
876     }
877     return true;
878 }
879 
880 JSObject*
InitMapClass(JSContext * cx,HandleObject obj)881 js::InitMapClass(JSContext* cx, HandleObject obj)
882 {
883     return MapObject::initClass(cx, obj);
884 }
885 
886 
887 /*** SetIterator *********************************************************************************/
888 
889 static const ClassOps SetIteratorObjectClassOps = {
890     nullptr, /* addProperty */
891     nullptr, /* delProperty */
892     nullptr, /* getProperty */
893     nullptr, /* setProperty */
894     nullptr, /* enumerate */
895     nullptr, /* resolve */
896     nullptr, /* mayResolve */
897     SetIteratorObject::finalize
898 };
899 
900 const Class SetIteratorObject::class_ = {
901     "Set Iterator",
902     JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount) |
903     JSCLASS_FOREGROUND_FINALIZE,
904     &SetIteratorObjectClassOps
905 };
906 
907 const JSFunctionSpec SetIteratorObject::methods[] = {
908     JS_SELF_HOSTED_FN("next", "SetIteratorNext", 0, 0),
909     JS_FS_END
910 };
911 
912 static inline ValueSet::Range*
SetIteratorObjectRange(NativeObject * obj)913 SetIteratorObjectRange(NativeObject* obj)
914 {
915     MOZ_ASSERT(obj->is<SetIteratorObject>());
916     return static_cast<ValueSet::Range*>(obj->getSlot(SetIteratorObject::RangeSlot).toPrivate());
917 }
918 
919 inline SetObject::IteratorKind
kind() const920 SetIteratorObject::kind() const
921 {
922     int32_t i = getSlot(KindSlot).toInt32();
923     MOZ_ASSERT(i == SetObject::Values || i == SetObject::Entries);
924     return SetObject::IteratorKind(i);
925 }
926 
927 bool
initSetIteratorProto(JSContext * cx,Handle<GlobalObject * > global)928 GlobalObject::initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
929 {
930     Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
931     if (!base)
932         return false;
933     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, base));
934     if (!proto)
935         return false;
936     if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods) ||
937         !DefineToStringTag(cx, proto, cx->names().SetIterator))
938     {
939         return false;
940     }
941     global->setReservedSlot(SET_ITERATOR_PROTO, ObjectValue(*proto));
942     return true;
943 }
944 
945 SetIteratorObject*
create(JSContext * cx,HandleObject setobj,ValueSet * data,SetObject::IteratorKind kind)946 SetIteratorObject::create(JSContext* cx, HandleObject setobj, ValueSet* data,
947                           SetObject::IteratorKind kind)
948 {
949     MOZ_ASSERT(kind != SetObject::Keys);
950 
951     Rooted<GlobalObject*> global(cx, &setobj->global());
952     Rooted<JSObject*> proto(cx, GlobalObject::getOrCreateSetIteratorPrototype(cx, global));
953     if (!proto)
954         return nullptr;
955 
956     ValueSet::Range* range = cx->new_<ValueSet::Range>(data->all());
957     if (!range)
958         return nullptr;
959 
960     SetIteratorObject* iterobj = NewObjectWithGivenProto<SetIteratorObject>(cx, proto);
961     if (!iterobj) {
962         js_delete(range);
963         return nullptr;
964     }
965     iterobj->setSlot(TargetSlot, ObjectValue(*setobj));
966     iterobj->setSlot(RangeSlot, PrivateValue(range));
967     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
968     return iterobj;
969 }
970 
971 void
finalize(FreeOp * fop,JSObject * obj)972 SetIteratorObject::finalize(FreeOp* fop, JSObject* obj)
973 {
974     MOZ_ASSERT(fop->onMainThread());
975     fop->delete_(SetIteratorObjectRange(static_cast<NativeObject*>(obj)));
976 }
977 
978 bool
next(Handle<SetIteratorObject * > setIterator,HandleArrayObject resultObj,JSContext * cx)979 SetIteratorObject::next(Handle<SetIteratorObject*> setIterator, HandleArrayObject resultObj,
980                         JSContext* cx)
981 {
982     // Check invariants for inlined _GetNextSetEntryForIterator.
983 
984     // The array should be tenured, so that post-barrier can be done simply.
985     MOZ_ASSERT(resultObj->isTenured());
986 
987     // The array elements should be fixed.
988     MOZ_ASSERT(resultObj->hasFixedElements());
989     MOZ_ASSERT(resultObj->getDenseInitializedLength() == 1);
990     MOZ_ASSERT(resultObj->getDenseCapacity() >= 1);
991 
992     ValueSet::Range* range = SetIteratorObjectRange(setIterator);
993     if (!range || range->empty()) {
994         js_delete(range);
995         setIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr));
996         return true;
997     }
998     resultObj->setDenseElementWithType(cx, 0, range->front().get());
999     range->popFront();
1000     return false;
1001 }
1002 
1003 /* static */ JSObject*
createResult(JSContext * cx)1004 SetIteratorObject::createResult(JSContext* cx)
1005 {
1006     RootedArrayObject resultObj(cx, NewDenseFullyAllocatedArray(cx, 1, nullptr, TenuredObject));
1007     if (!resultObj)
1008         return nullptr;
1009 
1010     Rooted<TaggedProto> proto(cx, resultObj->taggedProto());
1011     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultObj->getClass(), proto);
1012     if (!group)
1013         return nullptr;
1014     resultObj->setGroup(group);
1015 
1016     resultObj->setDenseInitializedLength(1);
1017     resultObj->initDenseElement(0, NullValue());
1018 
1019     // See comments in SetIteratorObject::next.
1020     AddTypePropertyId(cx, resultObj, JSID_VOID, TypeSet::UnknownType());
1021 
1022     return resultObj;
1023 }
1024 
1025 
1026 /*** Set *****************************************************************************************/
1027 
1028 const ClassOps SetObject::classOps_ = {
1029     nullptr, // addProperty
1030     nullptr, // delProperty
1031     nullptr, // getProperty
1032     nullptr, // setProperty
1033     nullptr, // enumerate
1034     nullptr, // resolve
1035     nullptr, // mayResolve
1036     finalize,
1037     nullptr, // call
1038     nullptr, // hasInstance
1039     nullptr, // construct
1040     mark
1041 };
1042 
1043 const Class SetObject::class_ = {
1044     "Set",
1045     JSCLASS_HAS_PRIVATE |
1046     JSCLASS_HAS_RESERVED_SLOTS(SetObject::SlotCount) |
1047     JSCLASS_HAS_CACHED_PROTO(JSProto_Set) |
1048     JSCLASS_FOREGROUND_FINALIZE,
1049     &SetObject::classOps_
1050 };
1051 
1052 const JSPropertySpec SetObject::properties[] = {
1053     JS_PSG("size", size, 0),
1054     JS_PS_END
1055 };
1056 
1057 const JSFunctionSpec SetObject::methods[] = {
1058     JS_FN("has", has, 1, 0),
1059     JS_FN("add", add, 1, 0),
1060     JS_FN("delete", delete_, 1, 0),
1061     JS_FN("entries", entries, 0, 0),
1062     JS_FN("clear", clear, 0, 0),
1063     JS_SELF_HOSTED_FN("forEach", "SetForEach", 2, 0),
1064     JS_FS_END
1065 };
1066 
1067 const JSPropertySpec SetObject::staticProperties[] = {
1068     JS_SELF_HOSTED_SYM_GET(species, "SetSpecies", 0),
1069     JS_PS_END
1070 };
1071 
1072 JSObject*
initClass(JSContext * cx,JSObject * obj)1073 SetObject::initClass(JSContext* cx, JSObject* obj)
1074 {
1075     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
1076     RootedObject proto(cx,
1077         InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods,
1078                   staticProperties));
1079     if (proto) {
1080         // Define the "values" method.
1081         JSFunction* fun = JS_DefineFunction(cx, proto, "values", values, 0, 0);
1082         if (!fun)
1083             return nullptr;
1084 
1085         // Define its aliases.
1086         RootedValue funval(cx, ObjectValue(*fun));
1087         if (!JS_DefineProperty(cx, proto, "keys", funval, 0))
1088             return nullptr;
1089 
1090         RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
1091         if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0))
1092             return nullptr;
1093 
1094         // Define Set.prototype[@@toStringTag].
1095         if (!DefineToStringTag(cx, proto, cx->names().Set))
1096             return nullptr;
1097     }
1098     return proto;
1099 }
1100 
1101 
1102 bool
keys(JSContext * cx,HandleObject obj,JS::MutableHandle<GCVector<JS::Value>> keys)1103 SetObject::keys(JSContext* cx, HandleObject obj, JS::MutableHandle<GCVector<JS::Value>> keys)
1104 {
1105     ValueSet* set = obj->as<SetObject>().getData();
1106     if (!set)
1107         return false;
1108 
1109     for (ValueSet::Range r = set->all(); !r.empty(); r.popFront()) {
1110         if (!keys.append(r.front().get()))
1111             return false;
1112     }
1113 
1114     return true;
1115 }
1116 
1117 bool
add(JSContext * cx,HandleObject obj,HandleValue k)1118 SetObject::add(JSContext* cx, HandleObject obj, HandleValue k)
1119 {
1120     ValueSet* set = obj->as<SetObject>().getData();
1121     if (!set)
1122         return false;
1123 
1124     Rooted<HashableValue> key(cx);
1125     if (!key.setValue(cx, k))
1126         return false;
1127 
1128     if (!WriteBarrierPost(cx->runtime(), &obj->as<SetObject>(), key.value()) ||
1129         !set->put(key))
1130     {
1131         ReportOutOfMemory(cx);
1132         return false;
1133     }
1134     return true;
1135 }
1136 
1137 SetObject*
create(JSContext * cx,HandleObject proto)1138 SetObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
1139 {
1140     auto set = cx->make_unique<ValueSet>(cx->runtime(),
1141                                          cx->compartment()->randomHashCodeScrambler());
1142     if (!set || !set->init()) {
1143         ReportOutOfMemory(cx);
1144         return nullptr;
1145     }
1146 
1147     SetObject* obj = NewObjectWithClassProto<SetObject>(cx, proto);
1148     if (!obj)
1149         return nullptr;
1150 
1151     obj->setPrivate(set.release());
1152     obj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
1153     return obj;
1154 }
1155 
1156 void
mark(JSTracer * trc,JSObject * obj)1157 SetObject::mark(JSTracer* trc, JSObject* obj)
1158 {
1159     SetObject* setobj = static_cast<SetObject*>(obj);
1160     if (ValueSet* set = setobj->getData()) {
1161         for (ValueSet::Range r = set->all(); !r.empty(); r.popFront())
1162             MarkKey(r, r.front(), trc);
1163     }
1164 }
1165 
1166 void
finalize(FreeOp * fop,JSObject * obj)1167 SetObject::finalize(FreeOp* fop, JSObject* obj)
1168 {
1169     MOZ_ASSERT(fop->onMainThread());
1170     SetObject* setobj = static_cast<SetObject*>(obj);
1171     if (ValueSet* set = setobj->getData())
1172         fop->delete_(set);
1173 }
1174 
1175 bool
isBuiltinAdd(HandleValue add,JSContext * cx)1176 SetObject::isBuiltinAdd(HandleValue add, JSContext* cx)
1177 {
1178     return IsNativeFunction(add, SetObject::add);
1179 }
1180 
1181 bool
construct(JSContext * cx,unsigned argc,Value * vp)1182 SetObject::construct(JSContext* cx, unsigned argc, Value* vp)
1183 {
1184     CallArgs args = CallArgsFromVp(argc, vp);
1185 
1186     if (!ThrowIfNotConstructing(cx, args, "Set"))
1187         return false;
1188 
1189     RootedObject proto(cx);
1190     RootedObject newTarget(cx, &args.newTarget().toObject());
1191     if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
1192         return false;
1193 
1194     Rooted<SetObject*> obj(cx, SetObject::create(cx, proto));
1195     if (!obj)
1196         return false;
1197 
1198     if (!args.get(0).isNullOrUndefined()) {
1199         RootedValue iterable(cx, args[0]);
1200         bool optimized = false;
1201         if (!IsOptimizableInitForSet<GlobalObject::getOrCreateSetPrototype, isBuiltinAdd>(cx, obj, iterable, &optimized))
1202             return false;
1203 
1204         if (optimized) {
1205             RootedValue keyVal(cx);
1206             Rooted<HashableValue> key(cx);
1207             ValueSet* set = obj->getData();
1208             ArrayObject* array = &iterable.toObject().as<ArrayObject>();
1209             for (uint32_t index = 0; index < array->getDenseInitializedLength(); ++index) {
1210                 keyVal.set(array->getDenseElement(index));
1211                 MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
1212 
1213                 if (!key.setValue(cx, keyVal))
1214                     return false;
1215                 if (!WriteBarrierPost(cx->runtime(), obj, keyVal) ||
1216                     !set->put(key))
1217                 {
1218                     ReportOutOfMemory(cx);
1219                     return false;
1220                 }
1221             }
1222         } else {
1223             FixedInvokeArgs<1> args2(cx);
1224             args2[0].set(args[0]);
1225 
1226             RootedValue thisv(cx, ObjectValue(*obj));
1227             if (!CallSelfHostedFunction(cx, cx->names().SetConstructorInit, thisv, args2, args2.rval()))
1228                 return false;
1229         }
1230     }
1231 
1232     args.rval().setObject(*obj);
1233     return true;
1234 }
1235 
1236 bool
is(HandleValue v)1237 SetObject::is(HandleValue v)
1238 {
1239     return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().as<SetObject>().getPrivate();
1240 }
1241 
1242 bool
is(HandleObject o)1243 SetObject::is(HandleObject o)
1244 {
1245     return o->hasClass(&class_) && o->as<SetObject>().getPrivate();
1246 }
1247 
1248 ValueSet &
extract(HandleObject o)1249 SetObject::extract(HandleObject o)
1250 {
1251     MOZ_ASSERT(o->hasClass(&SetObject::class_));
1252     return *o->as<SetObject>().getData();
1253 }
1254 
1255 ValueSet &
extract(const CallArgs & args)1256 SetObject::extract(const CallArgs& args)
1257 {
1258     MOZ_ASSERT(args.thisv().isObject());
1259     MOZ_ASSERT(args.thisv().toObject().hasClass(&SetObject::class_));
1260     return *static_cast<SetObject&>(args.thisv().toObject()).getData();
1261 }
1262 
1263 uint32_t
size(JSContext * cx,HandleObject obj)1264 SetObject::size(JSContext *cx, HandleObject obj)
1265 {
1266     MOZ_ASSERT(SetObject::is(obj));
1267     ValueSet &set = extract(obj);
1268     static_assert(sizeof(set.count()) <= sizeof(uint32_t),
1269                   "set count must be precisely representable as a JS number");
1270     return set.count();
1271 }
1272 
1273 bool
size_impl(JSContext * cx,const CallArgs & args)1274 SetObject::size_impl(JSContext* cx, const CallArgs& args)
1275 {
1276     MOZ_ASSERT(is(args.thisv()));
1277 
1278     ValueSet& set = extract(args);
1279     static_assert(sizeof(set.count()) <= sizeof(uint32_t),
1280                   "set count must be precisely representable as a JS number");
1281     args.rval().setNumber(set.count());
1282     return true;
1283 }
1284 
1285 bool
size(JSContext * cx,unsigned argc,Value * vp)1286 SetObject::size(JSContext* cx, unsigned argc, Value* vp)
1287 {
1288     CallArgs args = CallArgsFromVp(argc, vp);
1289     return CallNonGenericMethod<SetObject::is, SetObject::size_impl>(cx, args);
1290 }
1291 
1292 bool
has_impl(JSContext * cx,const CallArgs & args)1293 SetObject::has_impl(JSContext* cx, const CallArgs& args)
1294 {
1295     MOZ_ASSERT(is(args.thisv()));
1296 
1297     ValueSet& set = extract(args);
1298     ARG0_KEY(cx, args, key);
1299     args.rval().setBoolean(set.has(key));
1300     return true;
1301 }
1302 
1303 bool
has(JSContext * cx,HandleObject obj,HandleValue key,bool * rval)1304 SetObject::has(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
1305 {
1306     MOZ_ASSERT(SetObject::is(obj));
1307 
1308     ValueSet &set = extract(obj);
1309     Rooted<HashableValue> k(cx);
1310 
1311     if (!k.setValue(cx, key))
1312         return false;
1313 
1314     *rval = set.has(k);
1315     return true;
1316 }
1317 
1318 bool
has(JSContext * cx,unsigned argc,Value * vp)1319 SetObject::has(JSContext *cx, unsigned argc, Value *vp)
1320 {
1321     CallArgs args = CallArgsFromVp(argc, vp);
1322     return CallNonGenericMethod<SetObject::is, SetObject::has_impl>(cx, args);
1323 }
1324 
1325 bool
add_impl(JSContext * cx,const CallArgs & args)1326 SetObject::add_impl(JSContext* cx, const CallArgs& args)
1327 {
1328     MOZ_ASSERT(is(args.thisv()));
1329 
1330     ValueSet& set = extract(args);
1331     ARG0_KEY(cx, args, key);
1332     if (!WriteBarrierPost(cx->runtime(), &args.thisv().toObject().as<SetObject>(), key.value()) ||
1333         !set.put(key))
1334     {
1335         ReportOutOfMemory(cx);
1336         return false;
1337     }
1338     args.rval().set(args.thisv());
1339     return true;
1340 }
1341 
1342 bool
add(JSContext * cx,unsigned argc,Value * vp)1343 SetObject::add(JSContext* cx, unsigned argc, Value* vp)
1344 {
1345     CallArgs args = CallArgsFromVp(argc, vp);
1346     return CallNonGenericMethod<SetObject::is, SetObject::add_impl>(cx, args);
1347 }
1348 
1349 bool
delete_(JSContext * cx,HandleObject obj,HandleValue key,bool * rval)1350 SetObject::delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
1351 {
1352     MOZ_ASSERT(SetObject::is(obj));
1353 
1354     ValueSet &set = extract(obj);
1355     Rooted<HashableValue> k(cx);
1356 
1357     if (!k.setValue(cx, key))
1358         return false;
1359 
1360     if (!set.remove(k, rval)) {
1361         ReportOutOfMemory(cx);
1362         return false;
1363     }
1364     return true;
1365 }
1366 
1367 bool
delete_impl(JSContext * cx,const CallArgs & args)1368 SetObject::delete_impl(JSContext *cx, const CallArgs& args)
1369 {
1370     MOZ_ASSERT(is(args.thisv()));
1371 
1372     ValueSet& set = extract(args);
1373     ARG0_KEY(cx, args, key);
1374     bool found;
1375     if (!set.remove(key, &found)) {
1376         ReportOutOfMemory(cx);
1377         return false;
1378     }
1379     args.rval().setBoolean(found);
1380     return true;
1381 }
1382 
1383 bool
delete_(JSContext * cx,unsigned argc,Value * vp)1384 SetObject::delete_(JSContext* cx, unsigned argc, Value* vp)
1385 {
1386     CallArgs args = CallArgsFromVp(argc, vp);
1387     return CallNonGenericMethod<SetObject::is, SetObject::delete_impl>(cx, args);
1388 }
1389 
1390 bool
iterator(JSContext * cx,IteratorKind kind,HandleObject obj,MutableHandleValue iter)1391 SetObject::iterator(JSContext *cx, IteratorKind kind,
1392                     HandleObject obj, MutableHandleValue iter)
1393 {
1394     MOZ_ASSERT(SetObject::is(obj));
1395     ValueSet &set = extract(obj);
1396     Rooted<JSObject*> iterobj(cx, SetIteratorObject::create(cx, obj, &set, kind));
1397     return iterobj && (iter.setObject(*iterobj), true);
1398 }
1399 
1400 bool
iterator_impl(JSContext * cx,const CallArgs & args,IteratorKind kind)1401 SetObject::iterator_impl(JSContext *cx, const CallArgs& args, IteratorKind kind)
1402 {
1403     Rooted<SetObject*> setobj(cx, &args.thisv().toObject().as<SetObject>());
1404     ValueSet& set = *setobj->getData();
1405     Rooted<JSObject*> iterobj(cx, SetIteratorObject::create(cx, setobj, &set, kind));
1406     if (!iterobj)
1407         return false;
1408     args.rval().setObject(*iterobj);
1409     return true;
1410 }
1411 
1412 bool
values_impl(JSContext * cx,const CallArgs & args)1413 SetObject::values_impl(JSContext* cx, const CallArgs& args)
1414 {
1415     return iterator_impl(cx, args, Values);
1416 }
1417 
1418 bool
values(JSContext * cx,unsigned argc,Value * vp)1419 SetObject::values(JSContext* cx, unsigned argc, Value* vp)
1420 {
1421     CallArgs args = CallArgsFromVp(argc, vp);
1422     return CallNonGenericMethod(cx, is, values_impl, args);
1423 }
1424 
1425 bool
entries_impl(JSContext * cx,const CallArgs & args)1426 SetObject::entries_impl(JSContext* cx, const CallArgs& args)
1427 {
1428     return iterator_impl(cx, args, Entries);
1429 }
1430 
1431 bool
entries(JSContext * cx,unsigned argc,Value * vp)1432 SetObject::entries(JSContext* cx, unsigned argc, Value* vp)
1433 {
1434     CallArgs args = CallArgsFromVp(argc, vp);
1435     return CallNonGenericMethod(cx, is, entries_impl, args);
1436 }
1437 
1438 bool
clear(JSContext * cx,HandleObject obj)1439 SetObject::clear(JSContext *cx, HandleObject obj)
1440 {
1441     MOZ_ASSERT(SetObject::is(obj));
1442     ValueSet &set = extract(obj);
1443     if (!set.clear()) {
1444         ReportOutOfMemory(cx);
1445         return false;
1446     }
1447     return true;
1448 }
1449 
1450 bool
clear_impl(JSContext * cx,const CallArgs & args)1451 SetObject::clear_impl(JSContext *cx, const CallArgs& args)
1452 {
1453     Rooted<SetObject*> setobj(cx, &args.thisv().toObject().as<SetObject>());
1454     if (!setobj->getData()->clear()) {
1455         ReportOutOfMemory(cx);
1456         return false;
1457     }
1458     args.rval().setUndefined();
1459     return true;
1460 }
1461 
1462 bool
clear(JSContext * cx,unsigned argc,Value * vp)1463 SetObject::clear(JSContext* cx, unsigned argc, Value* vp)
1464 {
1465     CallArgs args = CallArgsFromVp(argc, vp);
1466     return CallNonGenericMethod(cx, is, clear_impl, args);
1467 }
1468 
1469 JSObject*
InitSetClass(JSContext * cx,HandleObject obj)1470 js::InitSetClass(JSContext* cx, HandleObject obj)
1471 {
1472     return SetObject::initClass(cx, obj);
1473 }
1474 
1475 /*** JS static utility functions *********************************************/
1476 
1477 static bool
forEach(const char * funcName,JSContext * cx,HandleObject obj,HandleValue callbackFn,HandleValue thisArg)1478 forEach(const char* funcName, JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue thisArg)
1479 {
1480     CHECK_REQUEST(cx);
1481 
1482     RootedId forEachId(cx, NameToId(cx->names().forEach));
1483     RootedFunction forEachFunc(cx, JS::GetSelfHostedFunction(cx, funcName, forEachId, 2));
1484     if (!forEachFunc)
1485         return false;
1486 
1487     RootedValue fval(cx, ObjectValue(*forEachFunc));
1488     return Call(cx, fval, obj, callbackFn, thisArg, &fval);
1489 }
1490 
1491 // Handles Clear/Size for public jsapi map/set access
1492 template<typename RetT>
1493 RetT
CallObjFunc(RetT (* ObjFunc)(JSContext *,HandleObject),JSContext * cx,HandleObject obj)1494 CallObjFunc(RetT(*ObjFunc)(JSContext*, HandleObject), JSContext* cx, HandleObject obj)
1495 {
1496     CHECK_REQUEST(cx);
1497     assertSameCompartment(cx, obj);
1498 
1499     // Always unwrap, in case this is an xray or cross-compartment wrapper.
1500     RootedObject unwrappedObj(cx);
1501     unwrappedObj = UncheckedUnwrap(obj);
1502 
1503     // Enter the compartment of the backing object before calling functions on
1504     // it.
1505     JSAutoCompartment ac(cx, unwrappedObj);
1506     return ObjFunc(cx, unwrappedObj);
1507 }
1508 
1509 // Handles Has/Delete for public jsapi map/set access
1510 bool
CallObjFunc(bool (* ObjFunc)(JSContext * cx,HandleObject obj,HandleValue key,bool * rval),JSContext * cx,HandleObject obj,HandleValue key,bool * rval)1511 CallObjFunc(bool(*ObjFunc)(JSContext *cx, HandleObject obj, HandleValue key, bool *rval),
1512             JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
1513 {
1514     CHECK_REQUEST(cx);
1515     assertSameCompartment(cx, obj, key);
1516 
1517     // Always unwrap, in case this is an xray or cross-compartment wrapper.
1518     RootedObject unwrappedObj(cx);
1519     unwrappedObj = UncheckedUnwrap(obj);
1520     JSAutoCompartment ac(cx, unwrappedObj);
1521 
1522     // If we're working with a wrapped map/set, rewrap the key into the
1523     // compartment of the unwrapped map/set.
1524     RootedValue wrappedKey(cx, key);
1525     if (obj != unwrappedObj) {
1526         if (!JS_WrapValue(cx, &wrappedKey))
1527             return false;
1528     }
1529     return ObjFunc(cx, unwrappedObj, wrappedKey, rval);
1530 }
1531 
1532 // Handles iterator generation for public jsapi map/set access
1533 template<typename Iter>
1534 bool
CallObjFunc(bool (* ObjFunc)(JSContext * cx,Iter kind,HandleObject obj,MutableHandleValue iter),JSContext * cx,Iter iterType,HandleObject obj,MutableHandleValue rval)1535 CallObjFunc(bool(*ObjFunc)(JSContext* cx, Iter kind,
1536                            HandleObject obj, MutableHandleValue iter),
1537             JSContext *cx, Iter iterType, HandleObject obj, MutableHandleValue rval)
1538 {
1539     CHECK_REQUEST(cx);
1540     assertSameCompartment(cx, obj);
1541 
1542     // Always unwrap, in case this is an xray or cross-compartment wrapper.
1543     RootedObject unwrappedObj(cx);
1544     unwrappedObj = UncheckedUnwrap(obj);
1545     {
1546         // Retrieve the iterator while in the unwrapped map/set's compartment,
1547         // otherwise we'll crash on a compartment assert.
1548         JSAutoCompartment ac(cx, unwrappedObj);
1549         if (!ObjFunc(cx, iterType, unwrappedObj, rval))
1550             return false;
1551     }
1552 
1553     // If the caller is in a different compartment than the map/set, rewrap the
1554     // iterator object into the caller's compartment.
1555     if (obj != unwrappedObj) {
1556         if (!JS_WrapValue(cx, rval))
1557             return false;
1558     }
1559     return true;
1560 }
1561 
1562 /*** JS public APIs **********************************************************/
1563 
JS_PUBLIC_API(JSObject *)1564 JS_PUBLIC_API(JSObject*)
1565 JS::NewMapObject(JSContext* cx)
1566 {
1567     return MapObject::create(cx);
1568 }
1569 
JS_PUBLIC_API(uint32_t)1570 JS_PUBLIC_API(uint32_t)
1571 JS::MapSize(JSContext* cx, HandleObject obj)
1572 {
1573     return CallObjFunc<uint32_t>(&MapObject::size, cx, obj);
1574 }
1575 
JS_PUBLIC_API(bool)1576 JS_PUBLIC_API(bool)
1577 JS::MapGet(JSContext* cx, HandleObject obj, HandleValue key, MutableHandleValue rval)
1578 {
1579     CHECK_REQUEST(cx);
1580     assertSameCompartment(cx, obj, key, rval);
1581 
1582     // Unwrap the object, and enter its compartment. If object isn't wrapped,
1583     // this is essentially a noop.
1584     RootedObject unwrappedObj(cx);
1585     unwrappedObj = UncheckedUnwrap(obj);
1586     {
1587         JSAutoCompartment ac(cx, unwrappedObj);
1588         RootedValue wrappedKey(cx, key);
1589 
1590         // If we passed in a wrapper, wrap our key into its compartment now.
1591         if (obj != unwrappedObj) {
1592             if (!JS_WrapValue(cx, &wrappedKey))
1593                 return false;
1594         }
1595         if (!MapObject::get(cx, unwrappedObj, wrappedKey, rval))
1596             return false;
1597     }
1598 
1599     // If we passed in a wrapper, wrap our return value on the way out.
1600     if (obj != unwrappedObj) {
1601         if (!JS_WrapValue(cx, rval))
1602             return false;
1603     }
1604     return true;
1605 }
1606 
JS_PUBLIC_API(bool)1607 JS_PUBLIC_API(bool)
1608 JS::MapSet(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val)
1609 {
1610     CHECK_REQUEST(cx);
1611     assertSameCompartment(cx, obj, key, val);
1612 
1613     // Unwrap the object, and enter its compartment. If object isn't wrapped,
1614     // this is essentially a noop.
1615     RootedObject unwrappedObj(cx);
1616     unwrappedObj = UncheckedUnwrap(obj);
1617     {
1618         JSAutoCompartment ac(cx, unwrappedObj);
1619 
1620         // If we passed in a wrapper, wrap both key and value before adding to
1621         // the map
1622         RootedValue wrappedKey(cx, key);
1623         RootedValue wrappedValue(cx, val);
1624         if (obj != unwrappedObj) {
1625             if (!JS_WrapValue(cx, &wrappedKey) ||
1626                 !JS_WrapValue(cx, &wrappedValue)) {
1627                 return false;
1628             }
1629         }
1630         return MapObject::set(cx, unwrappedObj, wrappedKey, wrappedValue);
1631     }
1632 }
1633 
JS_PUBLIC_API(bool)1634 JS_PUBLIC_API(bool)
1635 JS::MapHas(JSContext* cx, HandleObject obj, HandleValue key, bool* rval)
1636 {
1637     return CallObjFunc(MapObject::has, cx, obj, key, rval);
1638 }
1639 
JS_PUBLIC_API(bool)1640 JS_PUBLIC_API(bool)
1641 JS::MapDelete(JSContext *cx, HandleObject obj, HandleValue key, bool* rval)
1642 {
1643     return CallObjFunc(MapObject::delete_, cx, obj, key, rval);
1644 }
1645 
JS_PUBLIC_API(bool)1646 JS_PUBLIC_API(bool)
1647 JS::MapClear(JSContext* cx, HandleObject obj)
1648 {
1649     return CallObjFunc(&MapObject::clear, cx, obj);
1650 }
1651 
JS_PUBLIC_API(bool)1652 JS_PUBLIC_API(bool)
1653 JS::MapKeys(JSContext* cx, HandleObject obj, MutableHandleValue rval)
1654 {
1655     return CallObjFunc(&MapObject::iterator, cx, MapObject::Keys, obj, rval);
1656 }
1657 
JS_PUBLIC_API(bool)1658 JS_PUBLIC_API(bool)
1659 JS::MapValues(JSContext* cx, HandleObject obj, MutableHandleValue rval)
1660 {
1661     return CallObjFunc(&MapObject::iterator, cx, MapObject::Values, obj, rval);
1662 }
1663 
JS_PUBLIC_API(bool)1664 JS_PUBLIC_API(bool)
1665 JS::MapEntries(JSContext* cx, HandleObject obj, MutableHandleValue rval)
1666 {
1667     return CallObjFunc(&MapObject::iterator, cx, MapObject::Entries, obj, rval);
1668 }
1669 
JS_PUBLIC_API(bool)1670 JS_PUBLIC_API(bool)
1671 JS::MapForEach(JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue thisVal)
1672 {
1673     return forEach("MapForEach", cx, obj, callbackFn, thisVal);
1674 }
1675 
JS_PUBLIC_API(JSObject *)1676 JS_PUBLIC_API(JSObject *)
1677 JS::NewSetObject(JSContext *cx)
1678 {
1679     return SetObject::create(cx);
1680 }
1681 
JS_PUBLIC_API(uint32_t)1682 JS_PUBLIC_API(uint32_t)
1683 JS::SetSize(JSContext *cx, HandleObject obj)
1684 {
1685     return CallObjFunc<uint32_t>(&SetObject::size, cx, obj);
1686 }
1687 
JS_PUBLIC_API(bool)1688 JS_PUBLIC_API(bool)
1689 JS::SetAdd(JSContext *cx, HandleObject obj, HandleValue key)
1690 {
1691     CHECK_REQUEST(cx);
1692     assertSameCompartment(cx, obj, key);
1693 
1694     // Unwrap the object, and enter its compartment. If object isn't wrapped,
1695     // this is essentially a noop.
1696     RootedObject unwrappedObj(cx);
1697     unwrappedObj = UncheckedUnwrap(obj);
1698     {
1699         JSAutoCompartment ac(cx, unwrappedObj);
1700 
1701         // If we passed in a wrapper, wrap key before adding to the set
1702         RootedValue wrappedKey(cx, key);
1703         if (obj != unwrappedObj) {
1704             if (!JS_WrapValue(cx, &wrappedKey))
1705                 return false;
1706         }
1707         return SetObject::add(cx, unwrappedObj, wrappedKey);
1708     }
1709 }
1710 
JS_PUBLIC_API(bool)1711 JS_PUBLIC_API(bool)
1712 JS::SetHas(JSContext* cx, HandleObject obj, HandleValue key, bool* rval)
1713 {
1714     return CallObjFunc(SetObject::has, cx, obj, key, rval);
1715 }
1716 
JS_PUBLIC_API(bool)1717 JS_PUBLIC_API(bool)
1718 JS::SetDelete(JSContext *cx, HandleObject obj, HandleValue key, bool *rval)
1719 {
1720     return CallObjFunc(SetObject::delete_, cx, obj, key, rval);
1721 }
1722 
JS_PUBLIC_API(bool)1723 JS_PUBLIC_API(bool)
1724 JS::SetClear(JSContext* cx, HandleObject obj)
1725 {
1726     return CallObjFunc(&SetObject::clear, cx, obj);
1727 }
1728 
JS_PUBLIC_API(bool)1729 JS_PUBLIC_API(bool)
1730 JS::SetKeys(JSContext* cx, HandleObject obj, MutableHandleValue rval)
1731 {
1732     return SetValues(cx, obj, rval);
1733 }
1734 
JS_PUBLIC_API(bool)1735 JS_PUBLIC_API(bool)
1736 JS::SetValues(JSContext* cx, HandleObject obj, MutableHandleValue rval)
1737 {
1738     return CallObjFunc(&SetObject::iterator, cx, SetObject::Values, obj, rval);
1739 }
1740 
JS_PUBLIC_API(bool)1741 JS_PUBLIC_API(bool)
1742 JS::SetEntries(JSContext* cx, HandleObject obj, MutableHandleValue rval)
1743 {
1744     return CallObjFunc(&SetObject::iterator, cx, SetObject::Entries, obj, rval);
1745 }
1746 
JS_PUBLIC_API(bool)1747 JS_PUBLIC_API(bool)
1748 JS::SetForEach(JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue thisVal)
1749 {
1750     return forEach("SetForEach", cx, obj, callbackFn, thisVal);
1751 }
1752