No bug - tagging b4d3227540c9ebc43d64aac6168fdca7019c22d8 with FIREFOX_BETA_126_BASE...
[gecko.git] / js / src / builtin / MapObject.cpp
blobcd4ae7f46adaf1f19f99e9f8fbb718fc7e1ccf5b
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/. */
7 #include "builtin/MapObject.h"
9 #include "jsapi.h"
11 #include "ds/OrderedHashTable.h"
12 #include "gc/GCContext.h"
13 #include "jit/InlinableNatives.h"
14 #include "js/MapAndSet.h"
15 #include "js/PropertyAndElement.h" // JS_DefineFunctions
16 #include "js/PropertySpec.h"
17 #include "js/Utility.h"
18 #include "vm/BigIntType.h"
19 #include "vm/EqualityOperations.h" // js::SameValue
20 #include "vm/GlobalObject.h"
21 #include "vm/Interpreter.h"
22 #include "vm/JSContext.h"
23 #include "vm/JSObject.h"
24 #include "vm/SelfHosting.h"
25 #include "vm/SymbolType.h"
27 #ifdef ENABLE_RECORD_TUPLE
28 # include "vm/RecordType.h"
29 # include "vm/TupleType.h"
30 #endif
32 #include "gc/GCContext-inl.h"
33 #include "gc/Marking-inl.h"
34 #include "vm/GeckoProfiler-inl.h"
35 #include "vm/NativeObject-inl.h"
37 using namespace js;
39 using mozilla::NumberEqualsInt32;
41 /*** HashableValue **********************************************************/
43 static PreBarriered<Value> NormalizeDoubleValue(double d) {
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 return Int32Value(i);
52 // Normalize the sign bit of a NaN.
53 return JS::CanonicalizedDoubleValue(d);
56 bool HashableValue::setValue(JSContext* cx, HandleValue v) {
57 if (v.isString()) {
58 // Atomize so that hash() and operator==() are fast and infallible.
59 JSString* str = AtomizeString(cx, v.toString());
60 if (!str) {
61 return false;
63 value = StringValue(str);
64 } else if (v.isDouble()) {
65 value = NormalizeDoubleValue(v.toDouble());
66 #ifdef ENABLE_RECORD_TUPLE
67 } else if (v.isExtendedPrimitive()) {
68 JSObject& obj = v.toExtendedPrimitive();
69 if (obj.is<RecordType>()) {
70 if (!obj.as<RecordType>().ensureAtomized(cx)) {
71 return false;
73 } else {
74 MOZ_ASSERT(obj.is<TupleType>());
75 if (!obj.as<TupleType>().ensureAtomized(cx)) {
76 return false;
79 value = v;
80 #endif
81 } else {
82 value = v;
85 MOZ_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() ||
86 value.isNumber() || value.isString() || value.isSymbol() ||
87 value.isObject() || value.isBigInt() ||
88 IF_RECORD_TUPLE(value.isExtendedPrimitive(), false));
89 return true;
92 static HashNumber HashValue(const Value& v,
93 const mozilla::HashCodeScrambler& hcs) {
94 // HashableValue::setValue normalizes values so that the SameValue relation
95 // on HashableValues is the same as the == relationship on
96 // value.asRawBits(). So why not just return that? Security.
98 // To avoid revealing GC of atoms, string-based hash codes are computed
99 // from the string contents rather than any pointer; to avoid revealing
100 // addresses, pointer-based hash codes are computed using the
101 // HashCodeScrambler.
103 if (v.isString()) {
104 return v.toString()->asAtom().hash();
106 if (v.isSymbol()) {
107 return v.toSymbol()->hash();
109 if (v.isBigInt()) {
110 return MaybeForwarded(v.toBigInt())->hash();
112 #ifdef ENABLE_RECORD_TUPLE
113 if (v.isExtendedPrimitive()) {
114 JSObject* obj = MaybeForwarded(&v.toExtendedPrimitive());
115 auto hasher = [&hcs](const Value& v) {
116 return HashValue(
117 v.isDouble() ? NormalizeDoubleValue(v.toDouble()).get() : v, hcs);
120 if (obj->is<RecordType>()) {
121 return obj->as<RecordType>().hash(hasher);
123 MOZ_ASSERT(obj->is<TupleType>());
124 return obj->as<TupleType>().hash(hasher);
126 #endif
127 if (v.isObject()) {
128 return hcs.scramble(v.asRawBits());
131 MOZ_ASSERT(!v.isGCThing(), "do not reveal pointers via hash codes");
132 return mozilla::HashGeneric(v.asRawBits());
135 HashNumber HashableValue::hash(const mozilla::HashCodeScrambler& hcs) const {
136 return HashValue(value, hcs);
139 #ifdef ENABLE_RECORD_TUPLE
140 inline bool SameExtendedPrimitiveType(const PreBarriered<Value>& a,
141 const PreBarriered<Value>& b) {
142 return a.toExtendedPrimitive().getClass() ==
143 b.toExtendedPrimitive().getClass();
145 #endif
147 bool HashableValue::equals(const HashableValue& other) const {
148 // Two HashableValues are equal if they have equal bits.
149 bool b = (value.asRawBits() == other.value.asRawBits());
151 if (!b && (value.type() == other.value.type())) {
152 if (value.isBigInt()) {
153 // BigInt values are considered equal if they represent the same
154 // mathematical value.
155 b = BigInt::equal(value.toBigInt(), other.value.toBigInt());
157 #ifdef ENABLE_RECORD_TUPLE
158 else if (value.isExtendedPrimitive() &&
159 SameExtendedPrimitiveType(value, other.value)) {
160 b = js::SameValueZeroLinear(value, other.value);
162 #endif
165 #ifdef DEBUG
166 bool same;
167 JSContext* cx = TlsContext.get();
168 RootedValue valueRoot(cx, value);
169 RootedValue otherRoot(cx, other.value);
170 MOZ_ASSERT(SameValueZero(cx, valueRoot, otherRoot, &same));
171 MOZ_ASSERT(same == b);
172 #endif
173 return b;
176 /*** MapIterator ************************************************************/
178 namespace {} /* anonymous namespace */
180 static const JSClassOps MapIteratorObjectClassOps = {
181 nullptr, // addProperty
182 nullptr, // delProperty
183 nullptr, // enumerate
184 nullptr, // newEnumerate
185 nullptr, // resolve
186 nullptr, // mayResolve
187 MapIteratorObject::finalize, // finalize
188 nullptr, // call
189 nullptr, // construct
190 nullptr, // trace
193 static const ClassExtension MapIteratorObjectClassExtension = {
194 MapIteratorObject::objectMoved, // objectMovedOp
197 const JSClass MapIteratorObject::class_ = {
198 "Map Iterator",
199 JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount) |
200 JSCLASS_FOREGROUND_FINALIZE | JSCLASS_SKIP_NURSERY_FINALIZE,
201 &MapIteratorObjectClassOps, JS_NULL_CLASS_SPEC,
202 &MapIteratorObjectClassExtension};
204 const JSFunctionSpec MapIteratorObject::methods[] = {
205 JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0), JS_FS_END};
207 static inline ValueMap::Range* MapIteratorObjectRange(NativeObject* obj) {
208 MOZ_ASSERT(obj->is<MapIteratorObject>());
209 return obj->maybePtrFromReservedSlot<ValueMap::Range>(
210 MapIteratorObject::RangeSlot);
213 inline MapObject::IteratorKind MapIteratorObject::kind() const {
214 int32_t i = getReservedSlot(KindSlot).toInt32();
215 MOZ_ASSERT(i == MapObject::Keys || i == MapObject::Values ||
216 i == MapObject::Entries);
217 return MapObject::IteratorKind(i);
220 /* static */
221 bool GlobalObject::initMapIteratorProto(JSContext* cx,
222 Handle<GlobalObject*> global) {
223 Rooted<JSObject*> base(
224 cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
225 if (!base) {
226 return false;
228 Rooted<PlainObject*> proto(
229 cx, GlobalObject::createBlankPrototypeInheriting<PlainObject>(cx, base));
230 if (!proto) {
231 return false;
233 if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods) ||
234 !DefineToStringTag(cx, proto, cx->names().Map_Iterator_)) {
235 return false;
237 global->initBuiltinProto(ProtoKind::MapIteratorProto, proto);
238 return true;
241 template <typename TableObject>
242 static inline bool HasNurseryMemory(TableObject* t) {
243 return t->getReservedSlot(TableObject::HasNurseryMemorySlot).toBoolean();
246 template <typename TableObject>
247 static inline void SetHasNurseryMemory(TableObject* t, bool b) {
248 t->setReservedSlot(TableObject::HasNurseryMemorySlot, JS::BooleanValue(b));
251 MapIteratorObject* MapIteratorObject::create(JSContext* cx, HandleObject obj,
252 const ValueMap* data,
253 MapObject::IteratorKind kind) {
254 Handle<MapObject*> mapobj(obj.as<MapObject>());
255 Rooted<GlobalObject*> global(cx, &mapobj->global());
256 Rooted<JSObject*> proto(
257 cx, GlobalObject::getOrCreateMapIteratorPrototype(cx, global));
258 if (!proto) {
259 return nullptr;
262 MapIteratorObject* iterobj =
263 NewObjectWithGivenProto<MapIteratorObject>(cx, proto);
264 if (!iterobj) {
265 return nullptr;
268 iterobj->init(mapobj, kind);
270 constexpr size_t BufferSize =
271 RoundUp(sizeof(ValueMap::Range), gc::CellAlignBytes);
273 Nursery& nursery = cx->nursery();
274 void* buffer =
275 nursery.allocateBufferSameLocation(iterobj, BufferSize, js::MallocArena);
276 if (!buffer) {
277 // Retry with |iterobj| and |buffer| forcibly tenured.
278 iterobj = NewTenuredObjectWithGivenProto<MapIteratorObject>(cx, proto);
279 if (!iterobj) {
280 return nullptr;
283 iterobj->init(mapobj, kind);
285 buffer = nursery.allocateBufferSameLocation(iterobj, BufferSize,
286 js::MallocArena);
287 if (!buffer) {
288 ReportOutOfMemory(cx);
289 return nullptr;
293 bool insideNursery = IsInsideNursery(iterobj);
294 MOZ_ASSERT(insideNursery == nursery.isInside(buffer));
296 if (insideNursery && !HasNurseryMemory(mapobj.get())) {
297 if (!cx->nursery().addMapWithNurseryMemory(mapobj)) {
298 ReportOutOfMemory(cx);
299 return nullptr;
301 SetHasNurseryMemory(mapobj.get(), true);
304 auto range = data->createRange(buffer, insideNursery);
305 iterobj->setReservedSlot(RangeSlot, PrivateValue(range));
307 return iterobj;
310 void MapIteratorObject::finalize(JS::GCContext* gcx, JSObject* obj) {
311 MOZ_ASSERT(gcx->onMainThread());
312 MOZ_ASSERT(!IsInsideNursery(obj));
314 auto range = MapIteratorObjectRange(&obj->as<NativeObject>());
315 MOZ_ASSERT(!gcx->runtime()->gc.nursery().isInside(range));
317 // Bug 1560019: Malloc memory associated with MapIteratorObjects is not
318 // currently tracked.
319 gcx->deleteUntracked(range);
322 size_t MapIteratorObject::objectMoved(JSObject* obj, JSObject* old) {
323 if (!IsInsideNursery(old)) {
324 return 0;
327 MapIteratorObject* iter = &obj->as<MapIteratorObject>();
328 ValueMap::Range* range = MapIteratorObjectRange(iter);
329 if (!range) {
330 return 0;
333 Nursery& nursery = iter->runtimeFromMainThread()->gc.nursery();
334 if (!nursery.isInside(range)) {
335 nursery.removeMallocedBufferDuringMinorGC(range);
338 size_t size = RoundUp(sizeof(ValueMap::Range), gc::CellAlignBytes);
339 AutoEnterOOMUnsafeRegion oomUnsafe;
340 void* buffer = nursery.allocateBufferSameLocation(obj, size, js::MallocArena);
341 if (!buffer) {
342 oomUnsafe.crash("MapIteratorObject::objectMoved");
345 bool iteratorIsInNursery = IsInsideNursery(obj);
346 MOZ_ASSERT(iteratorIsInNursery == nursery.isInside(buffer));
347 auto* newRange = new (buffer) ValueMap::Range(*range, iteratorIsInNursery);
348 range->~Range();
349 iter->setReservedSlot(MapIteratorObject::RangeSlot, PrivateValue(newRange));
351 if (iteratorIsInNursery && iter->target()) {
352 SetHasNurseryMemory(iter->target(), true);
355 return size;
358 MapObject* MapIteratorObject::target() const {
359 Value value = getFixedSlot(TargetSlot);
360 if (value.isUndefined()) {
361 return nullptr;
364 return &MaybeForwarded(&value.toObject())->as<MapObject>();
367 template <typename Range>
368 static void DestroyRange(JSObject* iterator, Range* range) {
369 MOZ_ASSERT(IsInsideNursery(iterator) ==
370 iterator->runtimeFromMainThread()->gc.nursery().isInside(range));
372 range->~Range();
373 if (!IsInsideNursery(iterator)) {
374 js_free(range);
378 bool MapIteratorObject::next(MapIteratorObject* mapIterator,
379 ArrayObject* resultPairObj) {
380 // IC code calls this directly.
381 AutoUnsafeCallWithABI unsafe;
383 // Check invariants for inlined GetNextMapEntryForIterator.
385 // The array should be tenured, so that post-barrier can be done simply.
386 MOZ_ASSERT(resultPairObj->isTenured());
388 // The array elements should be fixed.
389 MOZ_ASSERT(resultPairObj->hasFixedElements());
390 MOZ_ASSERT(resultPairObj->getDenseInitializedLength() == 2);
391 MOZ_ASSERT(resultPairObj->getDenseCapacity() >= 2);
393 ValueMap::Range* range = MapIteratorObjectRange(mapIterator);
394 if (!range) {
395 return true;
398 if (range->empty()) {
399 DestroyRange<ValueMap::Range>(mapIterator, range);
400 mapIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr));
401 return true;
404 switch (mapIterator->kind()) {
405 case MapObject::Keys:
406 resultPairObj->setDenseElement(0, range->front().key.get());
407 break;
409 case MapObject::Values:
410 resultPairObj->setDenseElement(1, range->front().value);
411 break;
413 case MapObject::Entries: {
414 resultPairObj->setDenseElement(0, range->front().key.get());
415 resultPairObj->setDenseElement(1, range->front().value);
416 break;
419 range->popFront();
420 return false;
423 /* static */
424 JSObject* MapIteratorObject::createResultPair(JSContext* cx) {
425 Rooted<ArrayObject*> resultPairObj(
426 cx, NewDenseFullyAllocatedArray(cx, 2, TenuredObject));
427 if (!resultPairObj) {
428 return nullptr;
431 resultPairObj->setDenseInitializedLength(2);
432 resultPairObj->initDenseElement(0, NullValue());
433 resultPairObj->initDenseElement(1, NullValue());
435 return resultPairObj;
438 /*** Map ********************************************************************/
440 struct js::UnbarrieredHashPolicy {
441 using Lookup = Value;
442 static HashNumber hash(const Lookup& v,
443 const mozilla::HashCodeScrambler& hcs) {
444 return HashValue(v, hcs);
446 static bool match(const Value& k, const Lookup& l) { return k == l; }
447 static bool isEmpty(const Value& v) { return v.isMagic(JS_HASH_KEY_EMPTY); }
448 static void makeEmpty(Value* vp) { vp->setMagic(JS_HASH_KEY_EMPTY); }
451 // ValueMap, MapObject::UnbarrieredTable and MapObject::PreBarrieredTable must
452 // all have the same memory layout.
453 static_assert(sizeof(ValueMap) == sizeof(MapObject::UnbarrieredTable));
454 static_assert(sizeof(ValueMap::Entry) ==
455 sizeof(MapObject::UnbarrieredTable::Entry));
456 static_assert(sizeof(ValueMap) == sizeof(MapObject::PreBarrieredTable));
457 static_assert(sizeof(ValueMap::Entry) ==
458 sizeof(MapObject::PreBarrieredTable::Entry));
460 const JSClassOps MapObject::classOps_ = {
461 nullptr, // addProperty
462 nullptr, // delProperty
463 nullptr, // enumerate
464 nullptr, // newEnumerate
465 nullptr, // resolve
466 nullptr, // mayResolve
467 finalize, // finalize
468 nullptr, // call
469 nullptr, // construct
470 trace, // trace
473 const ClassSpec MapObject::classSpec_ = {
474 GenericCreateConstructor<MapObject::construct, 0, gc::AllocKind::FUNCTION>,
475 GenericCreatePrototype<MapObject>,
476 MapObject::staticMethods,
477 MapObject::staticProperties,
478 MapObject::methods,
479 MapObject::properties,
480 MapObject::finishInit,
483 const JSClass MapObject::class_ = {
484 "Map",
485 JSCLASS_DELAY_METADATA_BUILDER |
486 JSCLASS_HAS_RESERVED_SLOTS(MapObject::SlotCount) |
487 JSCLASS_HAS_CACHED_PROTO(JSProto_Map) | JSCLASS_FOREGROUND_FINALIZE |
488 JSCLASS_SKIP_NURSERY_FINALIZE,
489 &MapObject::classOps_,
490 &MapObject::classSpec_,
493 const JSClass MapObject::protoClass_ = {
494 "Map.prototype",
495 JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
496 JS_NULL_CLASS_OPS,
497 &MapObject::classSpec_,
500 const JSPropertySpec MapObject::properties[] = {
501 JS_PSG("size", size, 0),
502 JS_STRING_SYM_PS(toStringTag, "Map", JSPROP_READONLY),
503 JS_PS_END,
506 const JSFunctionSpec MapObject::methods[] = {
507 JS_INLINABLE_FN("get", get, 1, 0, MapGet),
508 JS_INLINABLE_FN("has", has, 1, 0, MapHas),
509 JS_FN("set", set, 2, 0),
510 JS_FN("delete", delete_, 1, 0),
511 JS_FN("keys", keys, 0, 0),
512 JS_FN("values", values, 0, 0),
513 JS_FN("clear", clear, 0, 0),
514 JS_SELF_HOSTED_FN("forEach", "MapForEach", 2, 0),
515 JS_FN("entries", entries, 0, 0),
516 // @@iterator is re-defined in finishInit so that it has the
517 // same identity as |entries|.
518 JS_SYM_FN(iterator, entries, 0, 0),
519 JS_FS_END,
522 const JSPropertySpec MapObject::staticProperties[] = {
523 JS_SELF_HOSTED_SYM_GET(species, "$MapSpecies", 0),
524 JS_PS_END,
527 const JSFunctionSpec MapObject::staticMethods[] = {
528 JS_SELF_HOSTED_FN("groupBy", "MapGroupBy", 2, 0),
529 JS_FS_END,
532 /* static */ bool MapObject::finishInit(JSContext* cx, HandleObject ctor,
533 HandleObject proto) {
534 Handle<NativeObject*> nativeProto = proto.as<NativeObject>();
536 RootedValue entriesFn(cx);
537 RootedId entriesId(cx, NameToId(cx->names().entries));
538 if (!NativeGetProperty(cx, nativeProto, entriesId, &entriesFn)) {
539 return false;
542 // 23.1.3.12 Map.prototype[@@iterator]()
543 // The initial value of the @@iterator property is the same function object
544 // as the initial value of the "entries" property.
545 RootedId iteratorId(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
546 return NativeDefineDataProperty(cx, nativeProto, iteratorId, entriesFn, 0);
549 void MapObject::trace(JSTracer* trc, JSObject* obj) {
550 if (ValueMap* map = obj->as<MapObject>().getTableUnchecked()) {
551 map->trace(trc);
555 using NurseryKeysVector = GCVector<Value, 0, SystemAllocPolicy>;
557 template <typename TableObject>
558 static NurseryKeysVector* GetNurseryKeys(TableObject* t) {
559 Value value = t->getReservedSlot(TableObject::NurseryKeysSlot);
560 return reinterpret_cast<NurseryKeysVector*>(value.toPrivate());
563 template <typename TableObject>
564 static NurseryKeysVector* AllocNurseryKeys(TableObject* t) {
565 MOZ_ASSERT(!GetNurseryKeys(t));
566 auto keys = js_new<NurseryKeysVector>();
567 if (!keys) {
568 return nullptr;
571 t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(keys));
572 return keys;
575 template <typename TableObject>
576 static void DeleteNurseryKeys(TableObject* t) {
577 auto keys = GetNurseryKeys(t);
578 MOZ_ASSERT(keys);
579 js_delete(keys);
580 t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(nullptr));
583 // A generic store buffer entry that traces all nursery keys for an ordered hash
584 // map or set.
585 template <typename ObjectT>
586 class js::OrderedHashTableRef : public gc::BufferableRef {
587 ObjectT* object;
589 public:
590 explicit OrderedHashTableRef(ObjectT* obj) : object(obj) {}
592 void trace(JSTracer* trc) override {
593 MOZ_ASSERT(!IsInsideNursery(object));
594 auto realTable = object->getTableUnchecked();
595 auto unbarrieredTable =
596 reinterpret_cast<typename ObjectT::UnbarrieredTable*>(realTable);
597 NurseryKeysVector* keys = GetNurseryKeys(object);
598 MOZ_ASSERT(keys);
600 keys->mutableEraseIf([&](Value& key) {
601 MOZ_ASSERT(
602 unbarrieredTable->hash(key) ==
603 realTable->hash(*reinterpret_cast<const HashableValue*>(&key)));
604 MOZ_ASSERT(IsInsideNursery(key.toGCThing()));
606 auto result =
607 unbarrieredTable->rekeyOneEntry(key, [trc](const Value& prior) {
608 Value key = prior;
609 TraceManuallyBarrieredEdge(trc, &key, "ordered hash table key");
610 return key;
613 if (result.isNothing()) {
614 return true; // Key removed.
617 key = result.value();
618 return !IsInsideNursery(key.toGCThing());
621 if (!keys->empty()) {
622 trc->runtime()->gc.storeBuffer().putGeneric(
623 OrderedHashTableRef<ObjectT>(object));
624 return;
627 DeleteNurseryKeys(object);
631 template <typename ObjectT>
632 [[nodiscard]] inline static bool PostWriteBarrierImpl(ObjectT* obj,
633 const Value& keyValue) {
634 if (MOZ_LIKELY(!keyValue.hasObjectPayload() && !keyValue.isBigInt())) {
635 MOZ_ASSERT_IF(keyValue.isGCThing(), !IsInsideNursery(keyValue.toGCThing()));
636 return true;
639 if (!IsInsideNursery(keyValue.toGCThing())) {
640 return true;
643 NurseryKeysVector* keys = GetNurseryKeys(obj);
644 if (!keys) {
645 keys = AllocNurseryKeys(obj);
646 if (!keys) {
647 return false;
650 keyValue.toGCThing()->storeBuffer()->putGeneric(
651 OrderedHashTableRef<ObjectT>(obj));
654 return keys->append(keyValue);
657 [[nodiscard]] inline static bool PostWriteBarrier(MapObject* map,
658 const Value& key) {
659 MOZ_ASSERT(!IsInsideNursery(map));
660 return PostWriteBarrierImpl(map, key);
663 [[nodiscard]] inline static bool PostWriteBarrier(SetObject* set,
664 const Value& key) {
665 if (IsInsideNursery(set)) {
666 return true;
669 return PostWriteBarrierImpl(set, key);
672 bool MapObject::getKeysAndValuesInterleaved(
673 HandleObject obj, JS::MutableHandle<GCVector<JS::Value>> entries) {
674 const ValueMap* map = obj->as<MapObject>().getData();
675 if (!map) {
676 return false;
679 for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
680 if (!entries.append(r.front().key.get()) ||
681 !entries.append(r.front().value)) {
682 return false;
686 return true;
689 bool MapObject::set(JSContext* cx, HandleObject obj, HandleValue k,
690 HandleValue v) {
691 MapObject* mapObject = &obj->as<MapObject>();
692 Rooted<HashableValue> key(cx);
693 if (!key.setValue(cx, k)) {
694 return false;
697 return setWithHashableKey(cx, mapObject, key, v);
700 /* static */
701 inline bool MapObject::setWithHashableKey(JSContext* cx, MapObject* obj,
702 Handle<HashableValue> key,
703 Handle<Value> value) {
704 ValueMap* table = obj->getTableUnchecked();
705 if (!table) {
706 return false;
709 bool needsPostBarriers = obj->isTenured();
710 if (needsPostBarriers) {
711 // Use the ValueMap representation which has post barriers.
712 if (!PostWriteBarrier(obj, key.get()) || !table->put(key.get(), value)) {
713 ReportOutOfMemory(cx);
714 return false;
716 } else {
717 // Use the PreBarrieredTable representation which does not.
718 auto* preBarriedTable = reinterpret_cast<PreBarrieredTable*>(table);
719 if (!preBarriedTable->put(key.get(), value.get())) {
720 ReportOutOfMemory(cx);
721 return false;
725 return true;
728 MapObject* MapObject::create(JSContext* cx,
729 HandleObject proto /* = nullptr */) {
730 auto map = cx->make_unique<ValueMap>(cx->zone(),
731 cx->realm()->randomHashCodeScrambler());
732 if (!map) {
733 return nullptr;
736 if (!map->init()) {
737 ReportOutOfMemory(cx);
738 return nullptr;
741 AutoSetNewObjectMetadata metadata(cx);
742 MapObject* mapObj = NewObjectWithClassProto<MapObject>(cx, proto);
743 if (!mapObj) {
744 return nullptr;
747 bool insideNursery = IsInsideNursery(mapObj);
748 if (insideNursery && !cx->nursery().addMapWithNurseryMemory(mapObj)) {
749 ReportOutOfMemory(cx);
750 return nullptr;
753 InitReservedSlot(mapObj, DataSlot, map.release(), MemoryUse::MapObjectTable);
754 mapObj->initReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
755 mapObj->initReservedSlot(HasNurseryMemorySlot,
756 JS::BooleanValue(insideNursery));
757 return mapObj;
760 size_t MapObject::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) {
761 size_t size = 0;
762 if (const ValueMap* map = getData()) {
763 size += map->sizeOfIncludingThis(mallocSizeOf);
765 if (NurseryKeysVector* nurseryKeys = GetNurseryKeys(this)) {
766 size += nurseryKeys->sizeOfIncludingThis(mallocSizeOf);
768 return size;
771 void MapObject::finalize(JS::GCContext* gcx, JSObject* obj) {
772 MOZ_ASSERT(gcx->onMainThread());
773 ValueMap* table = obj->as<MapObject>().getTableUnchecked();
774 if (!table) {
775 return;
778 MOZ_ASSERT_IF(obj->isTenured(), !table->hasNurseryRanges());
780 bool needsPostBarriers = obj->isTenured();
781 if (needsPostBarriers) {
782 // Use the ValueMap representation which has post barriers.
783 gcx->delete_(obj, table, MemoryUse::MapObjectTable);
784 } else {
785 // Use the PreBarrieredTable representation which does not.
786 auto* preBarriedTable = reinterpret_cast<PreBarrieredTable*>(table);
787 gcx->delete_(obj, preBarriedTable, MemoryUse::MapObjectTable);
791 void MapObject::clearNurseryRangesBeforeMinorGC() {
792 getTableUnchecked()->destroyNurseryRanges();
793 SetHasNurseryMemory(this, false);
796 /* static */
797 MapObject* MapObject::sweepAfterMinorGC(JS::GCContext* gcx, MapObject* mapobj) {
798 Nursery& nursery = gcx->runtime()->gc.nursery();
799 bool wasInCollectedRegion = nursery.inCollectedRegion(mapobj);
800 if (wasInCollectedRegion && !IsForwarded(mapobj)) {
801 finalize(gcx, mapobj);
802 return nullptr;
805 mapobj = MaybeForwarded(mapobj);
807 bool insideNursery = IsInsideNursery(mapobj);
808 if (insideNursery) {
809 SetHasNurseryMemory(mapobj, true);
812 if (wasInCollectedRegion && mapobj->isTenured()) {
813 AddCellMemory(mapobj, sizeof(ValueMap), MemoryUse::MapObjectTable);
816 if (!HasNurseryMemory(mapobj)) {
817 return nullptr;
820 return mapobj;
823 bool MapObject::construct(JSContext* cx, unsigned argc, Value* vp) {
824 AutoJSConstructorProfilerEntry pseudoFrame(cx, "Map");
825 CallArgs args = CallArgsFromVp(argc, vp);
827 if (!ThrowIfNotConstructing(cx, args, "Map")) {
828 return false;
831 RootedObject proto(cx);
832 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Map, &proto)) {
833 return false;
836 Rooted<MapObject*> obj(cx, MapObject::create(cx, proto));
837 if (!obj) {
838 return false;
841 if (!args.get(0).isNullOrUndefined()) {
842 FixedInvokeArgs<1> args2(cx);
843 args2[0].set(args[0]);
845 RootedValue thisv(cx, ObjectValue(*obj));
846 if (!CallSelfHostedFunction(cx, cx->names().MapConstructorInit, thisv,
847 args2, args2.rval())) {
848 return false;
852 args.rval().setObject(*obj);
853 return true;
856 bool MapObject::is(HandleValue v) {
857 return v.isObject() && v.toObject().hasClass(&class_) &&
858 !v.toObject().as<MapObject>().getReservedSlot(DataSlot).isUndefined();
861 bool MapObject::is(HandleObject o) {
862 return o->hasClass(&class_) &&
863 !o->as<MapObject>().getReservedSlot(DataSlot).isUndefined();
866 #define ARG0_KEY(cx, args, key) \
867 Rooted<HashableValue> key(cx); \
868 if (args.length() > 0 && !key.setValue(cx, args[0])) return false
870 const ValueMap& MapObject::extract(HandleObject o) {
871 MOZ_ASSERT(o->hasClass(&MapObject::class_));
872 return *o->as<MapObject>().getData();
875 const ValueMap& MapObject::extract(const CallArgs& args) {
876 MOZ_ASSERT(args.thisv().isObject());
877 MOZ_ASSERT(args.thisv().toObject().hasClass(&MapObject::class_));
878 return *args.thisv().toObject().as<MapObject>().getData();
881 uint32_t MapObject::size(JSContext* cx, HandleObject obj) {
882 const ValueMap& map = extract(obj);
883 static_assert(sizeof(map.count()) <= sizeof(uint32_t),
884 "map count must be precisely representable as a JS number");
885 return map.count();
888 bool MapObject::size_impl(JSContext* cx, const CallArgs& args) {
889 RootedObject obj(cx, &args.thisv().toObject());
890 args.rval().setNumber(size(cx, obj));
891 return true;
894 bool MapObject::size(JSContext* cx, unsigned argc, Value* vp) {
895 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "size");
896 CallArgs args = CallArgsFromVp(argc, vp);
897 return CallNonGenericMethod<MapObject::is, MapObject::size_impl>(cx, args);
900 bool MapObject::get(JSContext* cx, HandleObject obj, HandleValue key,
901 MutableHandleValue rval) {
902 const ValueMap& map = extract(obj);
903 Rooted<HashableValue> k(cx);
905 if (!k.setValue(cx, key)) {
906 return false;
909 if (const ValueMap::Entry* p = map.get(k)) {
910 rval.set(p->value);
911 } else {
912 rval.setUndefined();
915 return true;
918 bool MapObject::get_impl(JSContext* cx, const CallArgs& args) {
919 RootedObject obj(cx, &args.thisv().toObject());
920 return get(cx, obj, args.get(0), args.rval());
923 bool MapObject::get(JSContext* cx, unsigned argc, Value* vp) {
924 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "get");
925 CallArgs args = CallArgsFromVp(argc, vp);
926 return CallNonGenericMethod<MapObject::is, MapObject::get_impl>(cx, args);
929 bool MapObject::has(JSContext* cx, HandleObject obj, HandleValue key,
930 bool* rval) {
931 const ValueMap& map = extract(obj);
932 Rooted<HashableValue> k(cx);
934 if (!k.setValue(cx, key)) {
935 return false;
938 *rval = map.has(k);
939 return true;
942 bool MapObject::has_impl(JSContext* cx, const CallArgs& args) {
943 bool found;
944 RootedObject obj(cx, &args.thisv().toObject());
945 if (has(cx, obj, args.get(0), &found)) {
946 args.rval().setBoolean(found);
947 return true;
949 return false;
952 bool MapObject::has(JSContext* cx, unsigned argc, Value* vp) {
953 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "has");
954 CallArgs args = CallArgsFromVp(argc, vp);
955 return CallNonGenericMethod<MapObject::is, MapObject::has_impl>(cx, args);
958 bool MapObject::set_impl(JSContext* cx, const CallArgs& args) {
959 MOZ_ASSERT(MapObject::is(args.thisv()));
961 MapObject* obj = &args.thisv().toObject().as<MapObject>();
962 ARG0_KEY(cx, args, key);
963 if (!setWithHashableKey(cx, obj, key, args.get(1))) {
964 return false;
967 args.rval().set(args.thisv());
968 return true;
971 bool MapObject::set(JSContext* cx, unsigned argc, Value* vp) {
972 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "set");
973 CallArgs args = CallArgsFromVp(argc, vp);
974 return CallNonGenericMethod<MapObject::is, MapObject::set_impl>(cx, args);
977 bool MapObject::delete_(JSContext* cx, HandleObject obj, HandleValue key,
978 bool* rval) {
979 MapObject* mapObject = &obj->as<MapObject>();
980 Rooted<HashableValue> k(cx);
982 if (!k.setValue(cx, key)) {
983 return false;
986 bool ok;
987 if (mapObject->isTenured()) {
988 ok = mapObject->tenuredTable()->remove(k, rval);
989 } else {
990 ok = mapObject->nurseryTable()->remove(k, rval);
993 if (!ok) {
994 ReportOutOfMemory(cx);
995 return false;
998 return true;
1001 bool MapObject::delete_impl(JSContext* cx, const CallArgs& args) {
1002 // MapObject::trace does not trace deleted entries. Incremental GC therefore
1003 // requires that no HeapPtr<Value> objects pointing to heap values be left
1004 // alive in the ValueMap.
1006 // OrderedHashMap::remove() doesn't destroy the removed entry. It merely
1007 // calls OrderedHashMap::MapOps::makeEmpty. But that is sufficient, because
1008 // makeEmpty clears the value by doing e->value = Value(), and in the case
1009 // of a ValueMap, Value() means HeapPtr<Value>(), which is the same as
1010 // HeapPtr<Value>(UndefinedValue()).
1011 MOZ_ASSERT(MapObject::is(args.thisv()));
1012 RootedObject obj(cx, &args.thisv().toObject());
1014 bool found;
1015 if (!delete_(cx, obj, args.get(0), &found)) {
1016 return false;
1019 args.rval().setBoolean(found);
1020 return true;
1023 bool MapObject::delete_(JSContext* cx, unsigned argc, Value* vp) {
1024 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "delete");
1025 CallArgs args = CallArgsFromVp(argc, vp);
1026 return CallNonGenericMethod<MapObject::is, MapObject::delete_impl>(cx, args);
1029 bool MapObject::iterator(JSContext* cx, IteratorKind kind, HandleObject obj,
1030 MutableHandleValue iter) {
1031 const ValueMap& map = extract(obj);
1032 Rooted<JSObject*> iterobj(cx, MapIteratorObject::create(cx, obj, &map, kind));
1033 if (!iterobj) {
1034 return false;
1036 iter.setObject(*iterobj);
1037 return true;
1040 bool MapObject::iterator_impl(JSContext* cx, const CallArgs& args,
1041 IteratorKind kind) {
1042 RootedObject obj(cx, &args.thisv().toObject());
1043 return iterator(cx, kind, obj, args.rval());
1046 bool MapObject::keys_impl(JSContext* cx, const CallArgs& args) {
1047 return iterator_impl(cx, args, Keys);
1050 bool MapObject::keys(JSContext* cx, unsigned argc, Value* vp) {
1051 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "keys");
1052 CallArgs args = CallArgsFromVp(argc, vp);
1053 return CallNonGenericMethod(cx, is, keys_impl, args);
1056 bool MapObject::values_impl(JSContext* cx, const CallArgs& args) {
1057 return iterator_impl(cx, args, Values);
1060 bool MapObject::values(JSContext* cx, unsigned argc, Value* vp) {
1061 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "values");
1062 CallArgs args = CallArgsFromVp(argc, vp);
1063 return CallNonGenericMethod(cx, is, values_impl, args);
1066 bool MapObject::entries_impl(JSContext* cx, const CallArgs& args) {
1067 return iterator_impl(cx, args, Entries);
1070 bool MapObject::entries(JSContext* cx, unsigned argc, Value* vp) {
1071 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "entries");
1072 CallArgs args = CallArgsFromVp(argc, vp);
1073 return CallNonGenericMethod(cx, is, entries_impl, args);
1076 bool MapObject::clear_impl(JSContext* cx, const CallArgs& args) {
1077 RootedObject obj(cx, &args.thisv().toObject());
1078 args.rval().setUndefined();
1079 return clear(cx, obj);
1082 bool MapObject::clear(JSContext* cx, unsigned argc, Value* vp) {
1083 AutoJSMethodProfilerEntry pseudoFrame(cx, "Map.prototype", "clear");
1084 CallArgs args = CallArgsFromVp(argc, vp);
1085 return CallNonGenericMethod(cx, is, clear_impl, args);
1088 bool MapObject::clear(JSContext* cx, HandleObject obj) {
1089 MapObject* mapObject = &obj->as<MapObject>();
1091 bool ok;
1092 if (mapObject->isTenured()) {
1093 ok = mapObject->tenuredTable()->clear();
1094 } else {
1095 ok = mapObject->nurseryTable()->clear();
1098 if (!ok) {
1099 ReportOutOfMemory(cx);
1100 return false;
1103 return true;
1106 /*** SetIterator ************************************************************/
1108 static const JSClassOps SetIteratorObjectClassOps = {
1109 nullptr, // addProperty
1110 nullptr, // delProperty
1111 nullptr, // enumerate
1112 nullptr, // newEnumerate
1113 nullptr, // resolve
1114 nullptr, // mayResolve
1115 SetIteratorObject::finalize, // finalize
1116 nullptr, // call
1117 nullptr, // construct
1118 nullptr, // trace
1121 static const ClassExtension SetIteratorObjectClassExtension = {
1122 SetIteratorObject::objectMoved, // objectMovedOp
1125 const JSClass SetIteratorObject::class_ = {
1126 "Set Iterator",
1127 JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount) |
1128 JSCLASS_FOREGROUND_FINALIZE | JSCLASS_SKIP_NURSERY_FINALIZE,
1129 &SetIteratorObjectClassOps, JS_NULL_CLASS_SPEC,
1130 &SetIteratorObjectClassExtension};
1132 const JSFunctionSpec SetIteratorObject::methods[] = {
1133 JS_SELF_HOSTED_FN("next", "SetIteratorNext", 0, 0), JS_FS_END};
1135 static inline ValueSet::Range* SetIteratorObjectRange(NativeObject* obj) {
1136 MOZ_ASSERT(obj->is<SetIteratorObject>());
1137 return obj->maybePtrFromReservedSlot<ValueSet::Range>(
1138 SetIteratorObject::RangeSlot);
1141 inline SetObject::IteratorKind SetIteratorObject::kind() const {
1142 int32_t i = getReservedSlot(KindSlot).toInt32();
1143 MOZ_ASSERT(i == SetObject::Values || i == SetObject::Entries);
1144 return SetObject::IteratorKind(i);
1147 /* static */
1148 bool GlobalObject::initSetIteratorProto(JSContext* cx,
1149 Handle<GlobalObject*> global) {
1150 Rooted<JSObject*> base(
1151 cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
1152 if (!base) {
1153 return false;
1155 Rooted<PlainObject*> proto(
1156 cx, GlobalObject::createBlankPrototypeInheriting<PlainObject>(cx, base));
1157 if (!proto) {
1158 return false;
1160 if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods) ||
1161 !DefineToStringTag(cx, proto, cx->names().Set_Iterator_)) {
1162 return false;
1164 global->initBuiltinProto(ProtoKind::SetIteratorProto, proto);
1165 return true;
1168 SetIteratorObject* SetIteratorObject::create(JSContext* cx, HandleObject obj,
1169 ValueSet* data,
1170 SetObject::IteratorKind kind) {
1171 MOZ_ASSERT(kind != SetObject::Keys);
1173 Handle<SetObject*> setobj(obj.as<SetObject>());
1174 Rooted<GlobalObject*> global(cx, &setobj->global());
1175 Rooted<JSObject*> proto(
1176 cx, GlobalObject::getOrCreateSetIteratorPrototype(cx, global));
1177 if (!proto) {
1178 return nullptr;
1181 SetIteratorObject* iterobj =
1182 NewObjectWithGivenProto<SetIteratorObject>(cx, proto);
1183 if (!iterobj) {
1184 return nullptr;
1187 iterobj->init(setobj, kind);
1189 constexpr size_t BufferSize =
1190 RoundUp(sizeof(ValueSet::Range), gc::CellAlignBytes);
1192 Nursery& nursery = cx->nursery();
1193 void* buffer =
1194 nursery.allocateBufferSameLocation(iterobj, BufferSize, js::MallocArena);
1195 if (!buffer) {
1196 // Retry with |iterobj| and |buffer| forcibly tenured.
1197 iterobj = NewTenuredObjectWithGivenProto<SetIteratorObject>(cx, proto);
1198 if (!iterobj) {
1199 return nullptr;
1202 iterobj->init(setobj, kind);
1204 buffer = nursery.allocateBufferSameLocation(iterobj, BufferSize,
1205 js::MallocArena);
1206 if (!buffer) {
1207 ReportOutOfMemory(cx);
1208 return nullptr;
1212 bool insideNursery = IsInsideNursery(iterobj);
1213 MOZ_ASSERT(insideNursery == nursery.isInside(buffer));
1215 if (insideNursery && !HasNurseryMemory(setobj.get())) {
1216 if (!cx->nursery().addSetWithNurseryMemory(setobj)) {
1217 ReportOutOfMemory(cx);
1218 return nullptr;
1220 SetHasNurseryMemory(setobj.get(), true);
1223 auto range = data->createRange(buffer, insideNursery);
1224 iterobj->setReservedSlot(RangeSlot, PrivateValue(range));
1226 return iterobj;
1229 void SetIteratorObject::finalize(JS::GCContext* gcx, JSObject* obj) {
1230 MOZ_ASSERT(gcx->onMainThread());
1231 MOZ_ASSERT(!IsInsideNursery(obj));
1233 auto range = SetIteratorObjectRange(&obj->as<NativeObject>());
1234 MOZ_ASSERT(!gcx->runtime()->gc.nursery().isInside(range));
1236 // Bug 1560019: Malloc memory associated with SetIteratorObjects is not
1237 // currently tracked.
1238 gcx->deleteUntracked(range);
1241 size_t SetIteratorObject::objectMoved(JSObject* obj, JSObject* old) {
1242 if (!IsInsideNursery(old)) {
1243 return 0;
1246 SetIteratorObject* iter = &obj->as<SetIteratorObject>();
1247 ValueSet::Range* range = SetIteratorObjectRange(iter);
1248 if (!range) {
1249 return 0;
1252 Nursery& nursery = iter->runtimeFromMainThread()->gc.nursery();
1253 if (!nursery.isInside(range)) {
1254 nursery.removeMallocedBufferDuringMinorGC(range);
1257 size_t size = RoundUp(sizeof(ValueSet::Range), gc::CellAlignBytes);
1259 AutoEnterOOMUnsafeRegion oomUnsafe;
1260 void* buffer = nursery.allocateBufferSameLocation(obj, size, js::MallocArena);
1261 if (!buffer) {
1262 oomUnsafe.crash("SetIteratorObject::objectMoved");
1265 bool iteratorIsInNursery = IsInsideNursery(obj);
1266 MOZ_ASSERT(iteratorIsInNursery == nursery.isInside(buffer));
1267 auto* newRange = new (buffer) ValueSet::Range(*range, iteratorIsInNursery);
1268 range->~Range();
1269 iter->setReservedSlot(SetIteratorObject::RangeSlot, PrivateValue(newRange));
1271 if (iteratorIsInNursery && iter->target()) {
1272 SetHasNurseryMemory(iter->target(), true);
1275 return size;
1278 SetObject* SetIteratorObject::target() const {
1279 Value value = getFixedSlot(TargetSlot);
1280 if (value.isUndefined()) {
1281 return nullptr;
1284 return &MaybeForwarded(&value.toObject())->as<SetObject>();
1287 bool SetIteratorObject::next(SetIteratorObject* setIterator,
1288 ArrayObject* resultObj) {
1289 // IC code calls this directly.
1290 AutoUnsafeCallWithABI unsafe;
1292 // Check invariants for inlined _GetNextSetEntryForIterator.
1294 // The array should be tenured, so that post-barrier can be done simply.
1295 MOZ_ASSERT(resultObj->isTenured());
1297 // The array elements should be fixed.
1298 MOZ_ASSERT(resultObj->hasFixedElements());
1299 MOZ_ASSERT(resultObj->getDenseInitializedLength() == 1);
1300 MOZ_ASSERT(resultObj->getDenseCapacity() >= 1);
1302 ValueSet::Range* range = SetIteratorObjectRange(setIterator);
1303 if (!range) {
1304 return true;
1307 if (range->empty()) {
1308 DestroyRange<ValueSet::Range>(setIterator, range);
1309 setIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr));
1310 return true;
1313 resultObj->setDenseElement(0, range->front().get());
1314 range->popFront();
1315 return false;
1318 /* static */
1319 JSObject* SetIteratorObject::createResult(JSContext* cx) {
1320 Rooted<ArrayObject*> resultObj(
1321 cx, NewDenseFullyAllocatedArray(cx, 1, TenuredObject));
1322 if (!resultObj) {
1323 return nullptr;
1326 resultObj->setDenseInitializedLength(1);
1327 resultObj->initDenseElement(0, NullValue());
1329 return resultObj;
1332 /*** Set ********************************************************************/
1334 const JSClassOps SetObject::classOps_ = {
1335 nullptr, // addProperty
1336 nullptr, // delProperty
1337 nullptr, // enumerate
1338 nullptr, // newEnumerate
1339 nullptr, // resolve
1340 nullptr, // mayResolve
1341 finalize, // finalize
1342 nullptr, // call
1343 nullptr, // construct
1344 trace, // trace
1347 const ClassSpec SetObject::classSpec_ = {
1348 GenericCreateConstructor<SetObject::construct, 0, gc::AllocKind::FUNCTION>,
1349 GenericCreatePrototype<SetObject>,
1350 nullptr,
1351 SetObject::staticProperties,
1352 SetObject::methods,
1353 SetObject::properties,
1354 SetObject::finishInit,
1357 const JSClass SetObject::class_ = {
1358 "Set",
1359 JSCLASS_DELAY_METADATA_BUILDER |
1360 JSCLASS_HAS_RESERVED_SLOTS(SetObject::SlotCount) |
1361 JSCLASS_HAS_CACHED_PROTO(JSProto_Set) | JSCLASS_FOREGROUND_FINALIZE |
1362 JSCLASS_SKIP_NURSERY_FINALIZE,
1363 &SetObject::classOps_,
1364 &SetObject::classSpec_,
1367 const JSClass SetObject::protoClass_ = {
1368 "Set.prototype",
1369 JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
1370 JS_NULL_CLASS_OPS,
1371 &SetObject::classSpec_,
1374 const JSPropertySpec SetObject::properties[] = {
1375 JS_PSG("size", size, 0),
1376 JS_STRING_SYM_PS(toStringTag, "Set", JSPROP_READONLY),
1377 JS_PS_END,
1380 const JSFunctionSpec SetObject::methods[] = {
1381 JS_INLINABLE_FN("has", has, 1, 0, SetHas),
1382 JS_FN("add", add, 1, 0),
1383 JS_FN("delete", delete_, 1, 0),
1384 JS_FN("entries", entries, 0, 0),
1385 JS_FN("clear", clear, 0, 0),
1386 JS_SELF_HOSTED_FN("forEach", "SetForEach", 2, 0),
1387 #ifdef NIGHTLY_BUILD
1388 JS_SELF_HOSTED_FN("union", "SetUnion", 1, 0),
1389 JS_SELF_HOSTED_FN("difference", "SetDifference", 1, 0),
1390 JS_SELF_HOSTED_FN("intersection", "SetIntersection", 1, 0),
1391 JS_SELF_HOSTED_FN("symmetricDifference", "SetSymmetricDifference", 1, 0),
1392 JS_SELF_HOSTED_FN("isSubsetOf", "SetIsSubsetOf", 1, 0),
1393 JS_SELF_HOSTED_FN("isSupersetOf", "SetIsSupersetOf", 1, 0),
1394 JS_SELF_HOSTED_FN("isDisjointFrom", "SetIsDisjointFrom", 1, 0),
1395 #endif
1396 JS_FN("values", values, 0, 0),
1397 // @@iterator and |keys| re-defined in finishInit so that they have the
1398 // same identity as |values|.
1399 JS_FN("keys", values, 0, 0),
1400 JS_SYM_FN(iterator, values, 0, 0),
1401 JS_FS_END,
1403 // clang-format on
1405 const JSPropertySpec SetObject::staticProperties[] = {
1406 JS_SELF_HOSTED_SYM_GET(species, "$SetSpecies", 0),
1407 JS_PS_END,
1410 /* static */ bool SetObject::finishInit(JSContext* cx, HandleObject ctor,
1411 HandleObject proto) {
1412 Handle<NativeObject*> nativeProto = proto.as<NativeObject>();
1414 RootedValue valuesFn(cx);
1415 RootedId valuesId(cx, NameToId(cx->names().values));
1416 if (!NativeGetProperty(cx, nativeProto, valuesId, &valuesFn)) {
1417 return false;
1420 // 23.2.3.8 Set.prototype.keys()
1421 // The initial value of the "keys" property is the same function object
1422 // as the initial value of the "values" property.
1423 RootedId keysId(cx, NameToId(cx->names().keys));
1424 if (!NativeDefineDataProperty(cx, nativeProto, keysId, valuesFn, 0)) {
1425 return false;
1428 // 23.2.3.11 Set.prototype[@@iterator]()
1429 // See above.
1430 RootedId iteratorId(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
1431 return NativeDefineDataProperty(cx, nativeProto, iteratorId, valuesFn, 0);
1434 bool SetObject::keys(JSContext* cx, HandleObject obj,
1435 JS::MutableHandle<GCVector<JS::Value>> keys) {
1436 ValueSet* set = obj->as<SetObject>().getData();
1437 if (!set) {
1438 return false;
1441 for (ValueSet::Range r = set->all(); !r.empty(); r.popFront()) {
1442 if (!keys.append(r.front().get())) {
1443 return false;
1447 return true;
1450 bool SetObject::add(JSContext* cx, HandleObject obj, HandleValue k) {
1451 ValueSet* set = obj->as<SetObject>().getData();
1452 if (!set) {
1453 return false;
1456 Rooted<HashableValue> key(cx);
1457 if (!key.setValue(cx, k)) {
1458 return false;
1461 if (!PostWriteBarrier(&obj->as<SetObject>(), key.get()) ||
1462 !set->put(key.get())) {
1463 ReportOutOfMemory(cx);
1464 return false;
1466 return true;
1469 SetObject* SetObject::create(JSContext* cx,
1470 HandleObject proto /* = nullptr */) {
1471 auto set = cx->make_unique<ValueSet>(cx->zone(),
1472 cx->realm()->randomHashCodeScrambler());
1473 if (!set) {
1474 return nullptr;
1477 if (!set->init()) {
1478 ReportOutOfMemory(cx);
1479 return nullptr;
1482 AutoSetNewObjectMetadata metadata(cx);
1483 SetObject* obj = NewObjectWithClassProto<SetObject>(cx, proto);
1484 if (!obj) {
1485 return nullptr;
1488 bool insideNursery = IsInsideNursery(obj);
1489 if (insideNursery && !cx->nursery().addSetWithNurseryMemory(obj)) {
1490 ReportOutOfMemory(cx);
1491 return nullptr;
1494 InitReservedSlot(obj, DataSlot, set.release(), MemoryUse::MapObjectTable);
1495 obj->initReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
1496 obj->initReservedSlot(HasNurseryMemorySlot, JS::BooleanValue(insideNursery));
1497 return obj;
1500 void SetObject::trace(JSTracer* trc, JSObject* obj) {
1501 SetObject* setobj = static_cast<SetObject*>(obj);
1502 if (ValueSet* set = setobj->getData()) {
1503 set->trace(trc);
1507 size_t SetObject::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) {
1508 size_t size = 0;
1509 if (ValueSet* set = getData()) {
1510 size += set->sizeOfIncludingThis(mallocSizeOf);
1512 if (NurseryKeysVector* nurseryKeys = GetNurseryKeys(this)) {
1513 size += nurseryKeys->sizeOfIncludingThis(mallocSizeOf);
1515 return size;
1518 void SetObject::finalize(JS::GCContext* gcx, JSObject* obj) {
1519 MOZ_ASSERT(gcx->onMainThread());
1520 SetObject* setobj = static_cast<SetObject*>(obj);
1521 if (ValueSet* set = setobj->getData()) {
1522 MOZ_ASSERT_IF(obj->isTenured(), !set->hasNurseryRanges());
1523 gcx->delete_(obj, set, MemoryUse::MapObjectTable);
1527 void SetObject::clearNurseryRangesBeforeMinorGC() {
1528 getTableUnchecked()->destroyNurseryRanges();
1529 SetHasNurseryMemory(this, false);
1532 /* static */
1533 SetObject* SetObject::sweepAfterMinorGC(JS::GCContext* gcx, SetObject* setobj) {
1534 Nursery& nursery = gcx->runtime()->gc.nursery();
1535 bool wasInCollectedRegion = nursery.inCollectedRegion(setobj);
1536 if (wasInCollectedRegion && !IsForwarded(setobj)) {
1537 finalize(gcx, setobj);
1538 return nullptr;
1541 setobj = MaybeForwarded(setobj);
1543 bool insideNursery = IsInsideNursery(setobj);
1544 if (insideNursery) {
1545 SetHasNurseryMemory(setobj, true);
1548 if (wasInCollectedRegion && setobj->isTenured()) {
1549 AddCellMemory(setobj, sizeof(ValueSet), MemoryUse::MapObjectTable);
1552 if (!HasNurseryMemory(setobj)) {
1553 return nullptr;
1556 return setobj;
1559 bool SetObject::isBuiltinAdd(HandleValue add) {
1560 return IsNativeFunction(add, SetObject::add);
1563 bool SetObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1564 AutoJSConstructorProfilerEntry pseudoFrame(cx, "Set");
1565 CallArgs args = CallArgsFromVp(argc, vp);
1567 if (!ThrowIfNotConstructing(cx, args, "Set")) {
1568 return false;
1571 RootedObject proto(cx);
1572 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Set, &proto)) {
1573 return false;
1576 Rooted<SetObject*> obj(cx, SetObject::create(cx, proto));
1577 if (!obj) {
1578 return false;
1581 if (!args.get(0).isNullOrUndefined()) {
1582 RootedValue iterable(cx, args[0]);
1583 bool optimized = false;
1584 if (!IsOptimizableInitForSet<GlobalObject::getOrCreateSetPrototype,
1585 isBuiltinAdd>(cx, obj, iterable, &optimized)) {
1586 return false;
1589 if (optimized) {
1590 RootedValue keyVal(cx);
1591 Rooted<HashableValue> key(cx);
1592 ValueSet* set = obj->getData();
1593 Rooted<ArrayObject*> array(cx, &iterable.toObject().as<ArrayObject>());
1594 for (uint32_t index = 0; index < array->getDenseInitializedLength();
1595 ++index) {
1596 keyVal.set(array->getDenseElement(index));
1597 MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
1599 if (!key.setValue(cx, keyVal)) {
1600 return false;
1602 if (!PostWriteBarrier(obj, key.get()) || !set->put(key.get())) {
1603 ReportOutOfMemory(cx);
1604 return false;
1607 } else {
1608 FixedInvokeArgs<1> args2(cx);
1609 args2[0].set(args[0]);
1611 RootedValue thisv(cx, ObjectValue(*obj));
1612 if (!CallSelfHostedFunction(cx, cx->names().SetConstructorInit, thisv,
1613 args2, args2.rval())) {
1614 return false;
1619 args.rval().setObject(*obj);
1620 return true;
1623 bool SetObject::is(HandleValue v) {
1624 return v.isObject() && v.toObject().hasClass(&class_) &&
1625 !v.toObject().as<SetObject>().getReservedSlot(DataSlot).isUndefined();
1628 bool SetObject::is(HandleObject o) {
1629 return o->hasClass(&class_) &&
1630 !o->as<SetObject>().getReservedSlot(DataSlot).isUndefined();
1633 ValueSet& SetObject::extract(HandleObject o) {
1634 MOZ_ASSERT(o->hasClass(&SetObject::class_));
1635 return *o->as<SetObject>().getData();
1638 ValueSet& SetObject::extract(const CallArgs& args) {
1639 MOZ_ASSERT(args.thisv().isObject());
1640 MOZ_ASSERT(args.thisv().toObject().hasClass(&SetObject::class_));
1641 return *static_cast<SetObject&>(args.thisv().toObject()).getData();
1644 uint32_t SetObject::size(JSContext* cx, HandleObject obj) {
1645 MOZ_ASSERT(SetObject::is(obj));
1646 ValueSet& set = extract(obj);
1647 static_assert(sizeof(set.count()) <= sizeof(uint32_t),
1648 "set count must be precisely representable as a JS number");
1649 return set.count();
1652 bool SetObject::size_impl(JSContext* cx, const CallArgs& args) {
1653 MOZ_ASSERT(is(args.thisv()));
1655 ValueSet& set = extract(args);
1656 static_assert(sizeof(set.count()) <= sizeof(uint32_t),
1657 "set count must be precisely representable as a JS number");
1658 args.rval().setNumber(set.count());
1659 return true;
1662 bool SetObject::size(JSContext* cx, unsigned argc, Value* vp) {
1663 AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "size");
1664 CallArgs args = CallArgsFromVp(argc, vp);
1665 return CallNonGenericMethod<SetObject::is, SetObject::size_impl>(cx, args);
1668 bool SetObject::has_impl(JSContext* cx, const CallArgs& args) {
1669 MOZ_ASSERT(is(args.thisv()));
1671 ValueSet& set = extract(args);
1672 ARG0_KEY(cx, args, key);
1673 args.rval().setBoolean(set.has(key));
1674 return true;
1677 bool SetObject::has(JSContext* cx, HandleObject obj, HandleValue key,
1678 bool* rval) {
1679 MOZ_ASSERT(SetObject::is(obj));
1681 ValueSet& set = extract(obj);
1682 Rooted<HashableValue> k(cx);
1684 if (!k.setValue(cx, key)) {
1685 return false;
1688 *rval = set.has(k);
1689 return true;
1692 bool SetObject::has(JSContext* cx, unsigned argc, Value* vp) {
1693 AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "has");
1694 CallArgs args = CallArgsFromVp(argc, vp);
1695 return CallNonGenericMethod<SetObject::is, SetObject::has_impl>(cx, args);
1698 bool SetObject::add_impl(JSContext* cx, const CallArgs& args) {
1699 MOZ_ASSERT(is(args.thisv()));
1701 ValueSet& set = extract(args);
1702 ARG0_KEY(cx, args, key);
1703 if (!PostWriteBarrier(&args.thisv().toObject().as<SetObject>(), key.get()) ||
1704 !set.put(key.get())) {
1705 ReportOutOfMemory(cx);
1706 return false;
1708 args.rval().set(args.thisv());
1709 return true;
1712 bool SetObject::add(JSContext* cx, unsigned argc, Value* vp) {
1713 AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "add");
1714 CallArgs args = CallArgsFromVp(argc, vp);
1715 return CallNonGenericMethod<SetObject::is, SetObject::add_impl>(cx, args);
1718 bool SetObject::delete_(JSContext* cx, HandleObject obj, HandleValue key,
1719 bool* rval) {
1720 MOZ_ASSERT(SetObject::is(obj));
1722 ValueSet& set = extract(obj);
1723 Rooted<HashableValue> k(cx);
1725 if (!k.setValue(cx, key)) {
1726 return false;
1729 if (!set.remove(k, rval)) {
1730 ReportOutOfMemory(cx);
1731 return false;
1733 return true;
1736 bool SetObject::delete_impl(JSContext* cx, const CallArgs& args) {
1737 MOZ_ASSERT(is(args.thisv()));
1739 ValueSet& set = extract(args);
1740 ARG0_KEY(cx, args, key);
1741 bool found;
1742 if (!set.remove(key, &found)) {
1743 ReportOutOfMemory(cx);
1744 return false;
1746 args.rval().setBoolean(found);
1747 return true;
1750 bool SetObject::delete_(JSContext* cx, unsigned argc, Value* vp) {
1751 AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "delete");
1752 CallArgs args = CallArgsFromVp(argc, vp);
1753 return CallNonGenericMethod<SetObject::is, SetObject::delete_impl>(cx, args);
1756 bool SetObject::iterator(JSContext* cx, IteratorKind kind, HandleObject obj,
1757 MutableHandleValue iter) {
1758 MOZ_ASSERT(SetObject::is(obj));
1759 ValueSet& set = extract(obj);
1760 Rooted<JSObject*> iterobj(cx, SetIteratorObject::create(cx, obj, &set, kind));
1761 if (!iterobj) {
1762 return false;
1764 iter.setObject(*iterobj);
1765 return true;
1768 bool SetObject::iterator_impl(JSContext* cx, const CallArgs& args,
1769 IteratorKind kind) {
1770 Rooted<SetObject*> setobj(cx, &args.thisv().toObject().as<SetObject>());
1771 ValueSet& set = *setobj->getData();
1772 Rooted<JSObject*> iterobj(cx,
1773 SetIteratorObject::create(cx, setobj, &set, kind));
1774 if (!iterobj) {
1775 return false;
1777 args.rval().setObject(*iterobj);
1778 return true;
1781 bool SetObject::values_impl(JSContext* cx, const CallArgs& args) {
1782 return iterator_impl(cx, args, Values);
1785 bool SetObject::values(JSContext* cx, unsigned argc, Value* vp) {
1786 AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "values");
1787 CallArgs args = CallArgsFromVp(argc, vp);
1788 return CallNonGenericMethod(cx, is, values_impl, args);
1791 bool SetObject::entries_impl(JSContext* cx, const CallArgs& args) {
1792 return iterator_impl(cx, args, Entries);
1795 bool SetObject::entries(JSContext* cx, unsigned argc, Value* vp) {
1796 AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "entries");
1797 CallArgs args = CallArgsFromVp(argc, vp);
1798 return CallNonGenericMethod(cx, is, entries_impl, args);
1801 bool SetObject::clear(JSContext* cx, HandleObject obj) {
1802 MOZ_ASSERT(SetObject::is(obj));
1803 ValueSet& set = extract(obj);
1804 if (!set.clear()) {
1805 ReportOutOfMemory(cx);
1806 return false;
1808 return true;
1811 bool SetObject::clear_impl(JSContext* cx, const CallArgs& args) {
1812 Rooted<SetObject*> setobj(cx, &args.thisv().toObject().as<SetObject>());
1813 if (!setobj->getData()->clear()) {
1814 ReportOutOfMemory(cx);
1815 return false;
1817 args.rval().setUndefined();
1818 return true;
1821 bool SetObject::clear(JSContext* cx, unsigned argc, Value* vp) {
1822 AutoJSMethodProfilerEntry pseudoFrame(cx, "Set.prototype", "clear");
1823 CallArgs args = CallArgsFromVp(argc, vp);
1824 return CallNonGenericMethod(cx, is, clear_impl, args);
1827 bool SetObject::copy(JSContext* cx, unsigned argc, Value* vp) {
1828 CallArgs args = CallArgsFromVp(argc, vp);
1829 MOZ_ASSERT(args.length() == 1);
1830 MOZ_ASSERT(SetObject::is(args[0]));
1832 auto* result = SetObject::create(cx);
1833 if (!result) {
1834 return false;
1837 ValueSet* set = result->getData();
1838 MOZ_ASSERT(set);
1840 auto* from = &args[0].toObject().as<SetObject>();
1841 for (auto range = from->getData()->all(); !range.empty(); range.popFront()) {
1842 HashableValue value = range.front().get();
1844 if (!PostWriteBarrier(result, value) || !set->put(value)) {
1845 ReportOutOfMemory(cx);
1846 return false;
1850 args.rval().setObject(*result);
1851 return true;
1854 /*** JS static utility functions ********************************************/
1856 static bool forEach(const char* funcName, JSContext* cx, HandleObject obj,
1857 HandleValue callbackFn, HandleValue thisArg) {
1858 CHECK_THREAD(cx);
1860 RootedId forEachId(cx, NameToId(cx->names().forEach));
1861 RootedFunction forEachFunc(
1862 cx, JS::GetSelfHostedFunction(cx, funcName, forEachId, 2));
1863 if (!forEachFunc) {
1864 return false;
1867 RootedValue fval(cx, ObjectValue(*forEachFunc));
1868 return Call(cx, fval, obj, callbackFn, thisArg, &fval);
1871 // Handles Clear/Size for public jsapi map/set access
1872 template <typename RetT>
1873 RetT CallObjFunc(RetT (*ObjFunc)(JSContext*, HandleObject), JSContext* cx,
1874 HandleObject obj) {
1875 CHECK_THREAD(cx);
1876 cx->check(obj);
1878 // Always unwrap, in case this is an xray or cross-compartment wrapper.
1879 RootedObject unwrappedObj(cx);
1880 unwrappedObj = UncheckedUnwrap(obj);
1882 // Enter the realm of the backing object before calling functions on
1883 // it.
1884 JSAutoRealm ar(cx, unwrappedObj);
1885 return ObjFunc(cx, unwrappedObj);
1888 // Handles Has/Delete for public jsapi map/set access
1889 bool CallObjFunc(bool (*ObjFunc)(JSContext* cx, HandleObject obj,
1890 HandleValue key, bool* rval),
1891 JSContext* cx, HandleObject obj, HandleValue key, bool* rval) {
1892 CHECK_THREAD(cx);
1893 cx->check(obj, key);
1895 // Always unwrap, in case this is an xray or cross-compartment wrapper.
1896 RootedObject unwrappedObj(cx);
1897 unwrappedObj = UncheckedUnwrap(obj);
1898 JSAutoRealm ar(cx, unwrappedObj);
1900 // If we're working with a wrapped map/set, rewrap the key into the
1901 // compartment of the unwrapped map/set.
1902 RootedValue wrappedKey(cx, key);
1903 if (obj != unwrappedObj) {
1904 if (!JS_WrapValue(cx, &wrappedKey)) {
1905 return false;
1908 return ObjFunc(cx, unwrappedObj, wrappedKey, rval);
1911 // Handles iterator generation for public jsapi map/set access
1912 template <typename Iter>
1913 bool CallObjFunc(bool (*ObjFunc)(JSContext* cx, Iter kind, HandleObject obj,
1914 MutableHandleValue iter),
1915 JSContext* cx, Iter iterType, HandleObject obj,
1916 MutableHandleValue rval) {
1917 CHECK_THREAD(cx);
1918 cx->check(obj);
1920 // Always unwrap, in case this is an xray or cross-compartment wrapper.
1921 RootedObject unwrappedObj(cx);
1922 unwrappedObj = UncheckedUnwrap(obj);
1924 // Retrieve the iterator while in the unwrapped map/set's compartment,
1925 // otherwise we'll crash on a compartment assert.
1926 JSAutoRealm ar(cx, unwrappedObj);
1927 if (!ObjFunc(cx, iterType, unwrappedObj, rval)) {
1928 return false;
1932 // If the caller is in a different compartment than the map/set, rewrap the
1933 // iterator object into the caller's compartment.
1934 if (obj != unwrappedObj) {
1935 if (!JS_WrapValue(cx, rval)) {
1936 return false;
1939 return true;
1942 /*** JS public APIs *********************************************************/
1944 JS_PUBLIC_API JSObject* JS::NewMapObject(JSContext* cx) {
1945 return MapObject::create(cx);
1948 JS_PUBLIC_API uint32_t JS::MapSize(JSContext* cx, HandleObject obj) {
1949 return CallObjFunc<uint32_t>(&MapObject::size, cx, obj);
1952 JS_PUBLIC_API bool JS::MapGet(JSContext* cx, HandleObject obj, HandleValue key,
1953 MutableHandleValue rval) {
1954 CHECK_THREAD(cx);
1955 cx->check(obj, key, rval);
1957 // Unwrap the object, and enter its realm. If object isn't wrapped,
1958 // this is essentially a noop.
1959 RootedObject unwrappedObj(cx);
1960 unwrappedObj = UncheckedUnwrap(obj);
1962 JSAutoRealm ar(cx, unwrappedObj);
1963 RootedValue wrappedKey(cx, key);
1965 // If we passed in a wrapper, wrap our key into its compartment now.
1966 if (obj != unwrappedObj) {
1967 if (!JS_WrapValue(cx, &wrappedKey)) {
1968 return false;
1971 if (!MapObject::get(cx, unwrappedObj, wrappedKey, rval)) {
1972 return false;
1976 // If we passed in a wrapper, wrap our return value on the way out.
1977 if (obj != unwrappedObj) {
1978 if (!JS_WrapValue(cx, rval)) {
1979 return false;
1982 return true;
1985 JS_PUBLIC_API bool JS::MapSet(JSContext* cx, HandleObject obj, HandleValue key,
1986 HandleValue val) {
1987 CHECK_THREAD(cx);
1988 cx->check(obj, key, val);
1990 // Unwrap the object, and enter its compartment. If object isn't wrapped,
1991 // this is essentially a noop.
1992 RootedObject unwrappedObj(cx);
1993 unwrappedObj = UncheckedUnwrap(obj);
1995 JSAutoRealm ar(cx, unwrappedObj);
1997 // If we passed in a wrapper, wrap both key and value before adding to
1998 // the map
1999 RootedValue wrappedKey(cx, key);
2000 RootedValue wrappedValue(cx, val);
2001 if (obj != unwrappedObj) {
2002 if (!JS_WrapValue(cx, &wrappedKey) || !JS_WrapValue(cx, &wrappedValue)) {
2003 return false;
2006 return MapObject::set(cx, unwrappedObj, wrappedKey, wrappedValue);
2010 JS_PUBLIC_API bool JS::MapHas(JSContext* cx, HandleObject obj, HandleValue key,
2011 bool* rval) {
2012 return CallObjFunc(MapObject::has, cx, obj, key, rval);
2015 JS_PUBLIC_API bool JS::MapDelete(JSContext* cx, HandleObject obj,
2016 HandleValue key, bool* rval) {
2017 return CallObjFunc(MapObject::delete_, cx, obj, key, rval);
2020 JS_PUBLIC_API bool JS::MapClear(JSContext* cx, HandleObject obj) {
2021 return CallObjFunc(&MapObject::clear, cx, obj);
2024 JS_PUBLIC_API bool JS::MapKeys(JSContext* cx, HandleObject obj,
2025 MutableHandleValue rval) {
2026 return CallObjFunc(&MapObject::iterator, cx, MapObject::Keys, obj, rval);
2029 JS_PUBLIC_API bool JS::MapValues(JSContext* cx, HandleObject obj,
2030 MutableHandleValue rval) {
2031 return CallObjFunc(&MapObject::iterator, cx, MapObject::Values, obj, rval);
2034 JS_PUBLIC_API bool JS::MapEntries(JSContext* cx, HandleObject obj,
2035 MutableHandleValue rval) {
2036 return CallObjFunc(&MapObject::iterator, cx, MapObject::Entries, obj, rval);
2039 JS_PUBLIC_API bool JS::MapForEach(JSContext* cx, HandleObject obj,
2040 HandleValue callbackFn, HandleValue thisVal) {
2041 return forEach("MapForEach", cx, obj, callbackFn, thisVal);
2044 JS_PUBLIC_API JSObject* JS::NewSetObject(JSContext* cx) {
2045 return SetObject::create(cx);
2048 JS_PUBLIC_API uint32_t JS::SetSize(JSContext* cx, HandleObject obj) {
2049 return CallObjFunc<uint32_t>(&SetObject::size, cx, obj);
2052 JS_PUBLIC_API bool JS::SetAdd(JSContext* cx, HandleObject obj,
2053 HandleValue key) {
2054 CHECK_THREAD(cx);
2055 cx->check(obj, key);
2057 // Unwrap the object, and enter its compartment. If object isn't wrapped,
2058 // this is essentially a noop.
2059 RootedObject unwrappedObj(cx);
2060 unwrappedObj = UncheckedUnwrap(obj);
2062 JSAutoRealm ar(cx, unwrappedObj);
2064 // If we passed in a wrapper, wrap key before adding to the set
2065 RootedValue wrappedKey(cx, key);
2066 if (obj != unwrappedObj) {
2067 if (!JS_WrapValue(cx, &wrappedKey)) {
2068 return false;
2071 return SetObject::add(cx, unwrappedObj, wrappedKey);
2075 JS_PUBLIC_API bool JS::SetHas(JSContext* cx, HandleObject obj, HandleValue key,
2076 bool* rval) {
2077 return CallObjFunc(SetObject::has, cx, obj, key, rval);
2080 JS_PUBLIC_API bool JS::SetDelete(JSContext* cx, HandleObject obj,
2081 HandleValue key, bool* rval) {
2082 return CallObjFunc(SetObject::delete_, cx, obj, key, rval);
2085 JS_PUBLIC_API bool JS::SetClear(JSContext* cx, HandleObject obj) {
2086 return CallObjFunc(&SetObject::clear, cx, obj);
2089 JS_PUBLIC_API bool JS::SetKeys(JSContext* cx, HandleObject obj,
2090 MutableHandleValue rval) {
2091 return SetValues(cx, obj, rval);
2094 JS_PUBLIC_API bool JS::SetValues(JSContext* cx, HandleObject obj,
2095 MutableHandleValue rval) {
2096 return CallObjFunc(&SetObject::iterator, cx, SetObject::Values, obj, rval);
2099 JS_PUBLIC_API bool JS::SetEntries(JSContext* cx, HandleObject obj,
2100 MutableHandleValue rval) {
2101 return CallObjFunc(&SetObject::iterator, cx, SetObject::Entries, obj, rval);
2104 JS_PUBLIC_API bool JS::SetForEach(JSContext* cx, HandleObject obj,
2105 HandleValue callbackFn, HandleValue thisVal) {
2106 return forEach("SetForEach", cx, obj, callbackFn, thisVal);