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