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