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