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