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