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:
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "gc/SweepingAPI.h"
11 #include "js/GCHashTable.h"
12 #include "js/RootingAPI.h"
14 #include "jsapi-tests/tests.h"
18 // Exercise WeakCache<GCHashSet>.
19 BEGIN_TEST(testWeakCacheSet
) {
20 // Create two objects tenured and two in the nursery. If zeal is on,
21 // this may fail and we'll get more tenured objects. That's fine:
22 // the test will continue to work, it will just not test as much.
23 JS::RootedObject
tenured1(cx
, JS_NewPlainObject(cx
));
24 JS::RootedObject
tenured2(cx
, JS_NewPlainObject(cx
));
26 JS::RootedObject
nursery1(cx
, JS_NewPlainObject(cx
));
27 JS::RootedObject
nursery2(cx
, JS_NewPlainObject(cx
));
30 GCHashSet
<HeapPtr
<JSObject
*>, StableCellHasher
<HeapPtr
<JSObject
*>>,
32 using Cache
= WeakCache
<ObjectSet
>;
33 Cache
cache(JS::GetObjectZone(tenured1
));
40 // Verify relocation and that we don't sweep too aggressively.
42 CHECK(cache
.has(tenured1
));
43 CHECK(cache
.has(tenured2
));
44 CHECK(cache
.has(nursery1
));
45 CHECK(cache
.has(nursery2
));
47 // Unroot two entries and verify that they get removed.
48 tenured2
= nursery2
= nullptr;
50 CHECK(cache
.has(tenured1
));
51 CHECK(cache
.has(nursery1
));
52 CHECK(cache
.count() == 2);
56 END_TEST(testWeakCacheSet
)
58 // Exercise WeakCache<GCHashMap>.
59 BEGIN_TEST(testWeakCacheMap
) {
60 // Create two objects tenured and two in the nursery. If zeal is on,
61 // this may fail and we'll get more tenured objects. That's fine:
62 // the test will continue to work, it will just not test as much.
63 JS::RootedObject
tenured1(cx
, JS_NewPlainObject(cx
));
64 JS::RootedObject
tenured2(cx
, JS_NewPlainObject(cx
));
66 JS::RootedObject
nursery1(cx
, JS_NewPlainObject(cx
));
67 JS::RootedObject
nursery2(cx
, JS_NewPlainObject(cx
));
69 using ObjectMap
= js::GCHashMap
<HeapPtr
<JSObject
*>, uint32_t,
70 js::StableCellHasher
<HeapPtr
<JSObject
*>>>;
71 using Cache
= WeakCache
<ObjectMap
>;
72 Cache
cache(JS::GetObjectZone(tenured1
), cx
);
74 cache
.put(tenured1
, 1);
75 cache
.put(tenured2
, 2);
76 cache
.put(nursery1
, 3);
77 cache
.put(nursery2
, 4);
80 CHECK(cache
.has(tenured1
));
81 CHECK(cache
.has(tenured2
));
82 CHECK(cache
.has(nursery1
));
83 CHECK(cache
.has(nursery2
));
85 tenured2
= nursery2
= nullptr;
87 CHECK(cache
.has(tenured1
));
88 CHECK(cache
.has(nursery1
));
89 CHECK(cache
.count() == 2);
93 END_TEST(testWeakCacheMap
)
95 BEGIN_TEST(testWeakCacheMapWithUniquePtr
) {
96 JS::RootedObject
tenured1(cx
, JS_NewPlainObject(cx
));
97 JS::RootedObject
tenured2(cx
, JS_NewPlainObject(cx
));
99 JS::RootedObject
nursery1(cx
, JS_NewPlainObject(cx
));
100 JS::RootedObject
nursery2(cx
, JS_NewPlainObject(cx
));
102 using ObjectMap
= js::GCHashMap
<HeapPtr
<JSObject
*>, UniquePtr
<uint32_t>,
103 js::StableCellHasher
<HeapPtr
<JSObject
*>>>;
104 using Cache
= WeakCache
<ObjectMap
>;
105 Cache
cache(JS::GetObjectZone(tenured1
), cx
);
107 cache
.put(tenured1
, MakeUnique
<uint32_t>(1));
108 cache
.put(tenured2
, MakeUnique
<uint32_t>(2));
109 cache
.put(nursery1
, MakeUnique
<uint32_t>(3));
110 cache
.put(nursery2
, MakeUnique
<uint32_t>(4));
113 CHECK(cache
.has(tenured1
));
114 CHECK(cache
.has(tenured2
));
115 CHECK(cache
.has(nursery1
));
116 CHECK(cache
.has(nursery2
));
118 tenured2
= nursery2
= nullptr;
120 CHECK(cache
.has(tenured1
));
121 CHECK(cache
.has(nursery1
));
122 CHECK(cache
.count() == 2);
126 END_TEST(testWeakCacheMapWithUniquePtr
)
128 // Exercise WeakCache<GCVector>.
129 BEGIN_TEST(testWeakCacheGCVector
) {
130 // Create two objects tenured and two in the nursery. If zeal is on,
131 // this may fail and we'll get more tenured objects. That's fine:
132 // the test will continue to work, it will just not test as much.
133 JS::RootedObject
tenured1(cx
, JS_NewPlainObject(cx
));
134 JS::RootedObject
tenured2(cx
, JS_NewPlainObject(cx
));
136 JS::RootedObject
nursery1(cx
, JS_NewPlainObject(cx
));
137 JS::RootedObject
nursery2(cx
, JS_NewPlainObject(cx
));
139 using ObjectVector
= WeakCache
<GCVector
<HeapPtr
<JSObject
*>>>;
140 ObjectVector
cache(JS::GetObjectZone(tenured1
), cx
);
142 CHECK(cache
.append(tenured1
));
143 CHECK(cache
.append(tenured2
));
144 CHECK(cache
.append(nursery1
));
145 CHECK(cache
.append(nursery2
));
148 CHECK(cache
.get().length() == 4);
149 CHECK(cache
.get()[0] == tenured1
);
150 CHECK(cache
.get()[1] == tenured2
);
151 CHECK(cache
.get()[2] == nursery1
);
152 CHECK(cache
.get()[3] == nursery2
);
154 tenured2
= nursery2
= nullptr;
156 CHECK(cache
.get().length() == 2);
157 CHECK(cache
.get()[0] == tenured1
);
158 CHECK(cache
.get()[1] == nursery1
);
162 END_TEST(testWeakCacheGCVector
)
166 // A simple structure that embeds an object pointer. We cripple the hash
167 // implementation so that we can test hash table collisions.
169 HeapPtr
<JSObject
*> obj
;
170 explicit ObjectEntry(JSObject
* o
) : obj(o
) {}
171 bool operator==(const ObjectEntry
& other
) const { return obj
== other
.obj
; }
172 bool traceWeak(JSTracer
* trc
) {
173 return TraceWeakEdge(trc
, &obj
, "ObjectEntry::obj");
179 struct StableCellHasher
<ObjectEntry
> {
180 using Key
= ObjectEntry
;
181 using Lookup
= JSObject
*;
183 static bool maybeGetHash(const Lookup
& l
, HashNumber
* hashOut
) {
184 if (!StableCellHasher
<JSObject
*>::maybeGetHash(l
, hashOut
)) {
187 // Reduce hash code to single bit to generate hash collisions.
191 static bool ensureHash(const Lookup
& l
, HashNumber
* hashOut
) {
192 if (!StableCellHasher
<JSObject
*>::ensureHash(l
, hashOut
)) {
195 // Reduce hash code to single bit to generate hash collisions.
199 static HashNumber
hash(const Lookup
& l
) {
200 // Reduce hash code to single bit to generate hash collisions.
201 return StableCellHasher
<HeapPtr
<JSObject
*>>::hash(l
) & 0x1;
203 static bool match(const Key
& k
, const Lookup
& l
) {
204 return StableCellHasher
<HeapPtr
<JSObject
*>>::match(k
.obj
, l
);
209 // A structure that contains a pointer to a JSObject but is keyed based on an
210 // integer. This lets us test replacing dying entries in a set.
211 struct NumberAndObjectEntry
{
213 HeapPtr
<JSObject
*> obj
;
215 NumberAndObjectEntry(uint32_t n
, JSObject
* o
) : number(n
), obj(o
) {}
216 bool operator==(const NumberAndObjectEntry
& other
) const {
217 return number
== other
.number
&& obj
== other
.obj
;
219 bool traceWeak(JSTracer
* trc
) {
220 return TraceWeakEdge(trc
, &obj
, "NumberAndObjectEntry::obj");
224 struct NumberAndObjectLookup
{
226 HeapPtr
<JSObject
*> obj
;
228 NumberAndObjectLookup(uint32_t n
, JSObject
* o
) : number(n
), obj(o
) {}
229 MOZ_IMPLICIT
NumberAndObjectLookup(const NumberAndObjectEntry
& entry
)
230 : number(entry
.number
), obj(entry
.obj
) {}
235 struct StableCellHasher
<NumberAndObjectEntry
> {
236 using Key
= NumberAndObjectEntry
;
237 using Lookup
= NumberAndObjectLookup
;
239 static bool maybeGetHash(const Lookup
& l
, HashNumber
* hashOut
) {
240 if (!StableCellHasher
<JSObject
*>::maybeGetHash(l
.obj
, hashOut
)) {
243 *hashOut
^= l
.number
;
246 static bool ensureHash(const Lookup
& l
, HashNumber
* hashOut
) {
247 if (!StableCellHasher
<JSObject
*>::ensureHash(l
.obj
, hashOut
)) {
250 *hashOut
^= l
.number
;
253 static HashNumber
hash(const Lookup
& l
) {
254 return StableCellHasher
<HeapPtr
<JSObject
*>>::hash(l
.obj
) ^ l
.number
;
256 static bool match(const Key
& k
, const Lookup
& l
) {
257 return k
.number
== l
.number
&&
258 StableCellHasher
<HeapPtr
<JSObject
*>>::match(k
.obj
, l
.obj
);
263 BEGIN_TEST(testIncrementalWeakCacheSweeping
) {
264 AutoLeaveZeal
nozeal(cx
);
266 JS_SetGCParameter(cx
, JSGC_INCREMENTAL_GC_ENABLED
, true);
267 JS::SetGCZeal(cx
, 17, 1000000);
271 CHECK(TestReplaceDyingInSet());
272 CHECK(TestReplaceDyingInMap());
273 CHECK(TestUniqueIDLookups());
275 JS::SetGCZeal(cx
, 0, 0);
276 JS_SetGCParameter(cx
, JSGC_INCREMENTAL_GC_ENABLED
, false);
281 template <typename Cache
>
282 bool GCUntilCacheSweep(JSContext
* cx
, const Cache
& cache
) {
283 CHECK(!IsIncrementalGCInProgress(cx
));
285 JS::Zone
* zone
= JS::GetObjectZone(global
);
286 JS::PrepareZoneForGC(cx
, zone
);
287 JS::SliceBudget
budget(JS::WorkBudget(1));
288 cx
->runtime()->gc
.startDebugGC(JS::GCOptions::Normal
, budget
);
290 CHECK(IsIncrementalGCInProgress(cx
));
291 CHECK(zone
->isGCSweeping());
292 CHECK(cache
.needsIncrementalBarrier());
297 template <typename Cache
>
298 bool SweepCacheAndFinishGC(JSContext
* cx
, const Cache
& cache
) {
299 CHECK(IsIncrementalGCInProgress(cx
));
301 PrepareForIncrementalGC(cx
);
302 IncrementalGCSlice(cx
, JS::GCReason::API
, JS::SliceBudget::unlimited());
304 JS::Zone
* zone
= JS::GetObjectZone(global
);
305 CHECK(!IsIncrementalGCInProgress(cx
));
306 CHECK(!zone
->isCollecting());
307 CHECK(!cache
.needsIncrementalBarrier());
314 GCHashSet
<HeapPtr
<JSObject
*>, StableCellHasher
<HeapPtr
<JSObject
*>>,
316 using Cache
= WeakCache
<ObjectSet
>;
317 Cache
cache(JS::GetObjectZone(global
), cx
);
319 // Sweep empty cache.
321 CHECK(cache
.empty());
323 CHECK(cache
.empty());
325 // Add an entry while sweeping.
327 JS::RootedObject
obj1(cx
, JS_NewPlainObject(cx
));
328 JS::RootedObject
obj2(cx
, JS_NewPlainObject(cx
));
329 JS::RootedObject
obj3(cx
, JS_NewPlainObject(cx
));
330 JS::RootedObject
obj4(cx
, JS_NewPlainObject(cx
));
336 CHECK(!cache
.has(obj1
));
337 CHECK(cache
.put(obj1
));
338 CHECK(cache
.count() == 1);
339 CHECK(cache
.has(obj1
));
340 CHECK(*cache
.lookup(obj1
) == obj1
);
342 CHECK(GCUntilCacheSweep(cx
, cache
));
344 CHECK(!cache
.has(obj2
));
345 CHECK(cache
.put(obj2
));
346 CHECK(cache
.has(obj2
));
347 CHECK(*cache
.lookup(obj2
) == obj2
);
349 CHECK(SweepCacheAndFinishGC(cx
, cache
));
351 CHECK(cache
.count() == 2);
352 CHECK(cache
.has(obj1
));
353 CHECK(cache
.has(obj2
));
355 // Test dying entries are not found while sweeping.
357 CHECK(cache
.put(obj3
));
358 CHECK(cache
.put(obj4
));
361 obj3
= obj4
= nullptr;
363 CHECK(GCUntilCacheSweep(cx
, cache
));
365 CHECK(cache
.has(obj1
));
366 CHECK(cache
.has(obj2
));
367 CHECK(!cache
.has(static_cast<JSObject
*>(old3
)));
368 CHECK(!cache
.has(static_cast<JSObject
*>(old4
)));
371 for (auto r
= cache
.all(); !r
.empty(); r
.popFront()) {
372 CHECK(r
.front() == obj1
|| r
.front() == obj2
);
377 CHECK(SweepCacheAndFinishGC(cx
, cache
));
379 CHECK(cache
.count() == 2);
381 // Test lookupForAdd while sweeping.
383 obj3
= JS_NewPlainObject(cx
);
384 obj4
= JS_NewPlainObject(cx
);
388 CHECK(cache
.lookupForAdd(obj1
));
389 CHECK(*cache
.lookupForAdd(obj1
) == obj1
);
391 auto addp
= cache
.lookupForAdd(obj3
);
393 CHECK(cache
.add(addp
, obj3
));
394 CHECK(cache
.has(obj3
));
396 CHECK(GCUntilCacheSweep(cx
, cache
));
398 addp
= cache
.lookupForAdd(obj4
);
400 CHECK(cache
.add(addp
, obj4
));
401 CHECK(cache
.has(obj4
));
403 CHECK(SweepCacheAndFinishGC(cx
, cache
));
405 CHECK(cache
.count() == 4);
406 CHECK(cache
.has(obj3
));
407 CHECK(cache
.has(obj4
));
409 // Test remove while sweeping.
413 CHECK(GCUntilCacheSweep(cx
, cache
));
417 CHECK(SweepCacheAndFinishGC(cx
, cache
));
419 CHECK(cache
.count() == 2);
420 CHECK(!cache
.has(obj3
));
421 CHECK(!cache
.has(obj4
));
423 // Test putNew while sweeping.
425 CHECK(GCUntilCacheSweep(cx
, cache
));
427 CHECK(cache
.putNew(obj3
));
428 CHECK(cache
.putNew(obj4
, obj4
));
430 CHECK(SweepCacheAndFinishGC(cx
, cache
));
432 CHECK(cache
.count() == 4);
433 CHECK(cache
.has(obj3
));
434 CHECK(cache
.has(obj4
));
443 GCHashMap
<HeapPtr
<JSObject
*>, uint32_t,
444 StableCellHasher
<HeapPtr
<JSObject
*>>, TempAllocPolicy
>;
445 using Cache
= WeakCache
<ObjectMap
>;
446 Cache
cache(JS::GetObjectZone(global
), cx
);
448 // Sweep empty cache.
450 CHECK(cache
.empty());
452 CHECK(cache
.empty());
454 // Add an entry while sweeping.
456 JS::RootedObject
obj1(cx
, JS_NewPlainObject(cx
));
457 JS::RootedObject
obj2(cx
, JS_NewPlainObject(cx
));
458 JS::RootedObject
obj3(cx
, JS_NewPlainObject(cx
));
459 JS::RootedObject
obj4(cx
, JS_NewPlainObject(cx
));
465 CHECK(!cache
.has(obj1
));
466 CHECK(cache
.put(obj1
, 1));
467 CHECK(cache
.count() == 1);
468 CHECK(cache
.has(obj1
));
469 CHECK(cache
.lookup(obj1
)->key() == obj1
);
471 CHECK(GCUntilCacheSweep(cx
, cache
));
472 CHECK(cache
.needsIncrementalBarrier());
474 CHECK(!cache
.has(obj2
));
475 CHECK(cache
.put(obj2
, 2));
476 CHECK(cache
.has(obj2
));
477 CHECK(cache
.lookup(obj2
)->key() == obj2
);
479 CHECK(SweepCacheAndFinishGC(cx
, cache
));
480 CHECK(!cache
.needsIncrementalBarrier());
482 CHECK(cache
.count() == 2);
483 CHECK(cache
.has(obj1
));
484 CHECK(cache
.has(obj2
));
488 CHECK(cache
.put(obj3
, 3));
489 CHECK(cache
.put(obj4
, 4));
492 obj3
= obj4
= nullptr;
494 CHECK(GCUntilCacheSweep(cx
, cache
));
496 CHECK(cache
.has(obj1
));
497 CHECK(cache
.has(obj2
));
498 CHECK(!cache
.has(static_cast<JSObject
*>(old3
)));
499 CHECK(!cache
.has(static_cast<JSObject
*>(old4
)));
502 for (auto r
= cache
.all(); !r
.empty(); r
.popFront()) {
503 CHECK(r
.front().key() == obj1
|| r
.front().key() == obj2
);
508 CHECK(SweepCacheAndFinishGC(cx
, cache
));
510 CHECK(cache
.count() == 2);
512 // Test lookupForAdd while sweeping.
514 obj3
= JS_NewPlainObject(cx
);
515 obj4
= JS_NewPlainObject(cx
);
519 CHECK(cache
.lookupForAdd(obj1
));
520 CHECK(cache
.lookupForAdd(obj1
)->key() == obj1
);
522 auto addp
= cache
.lookupForAdd(obj3
);
524 CHECK(cache
.add(addp
, obj3
, 3));
525 CHECK(cache
.has(obj3
));
527 CHECK(GCUntilCacheSweep(cx
, cache
));
529 addp
= cache
.lookupForAdd(obj4
);
531 CHECK(cache
.add(addp
, obj4
, 4));
532 CHECK(cache
.has(obj4
));
534 CHECK(SweepCacheAndFinishGC(cx
, cache
));
536 CHECK(cache
.count() == 4);
537 CHECK(cache
.has(obj3
));
538 CHECK(cache
.has(obj4
));
540 // Test remove while sweeping.
544 CHECK(GCUntilCacheSweep(cx
, cache
));
548 CHECK(SweepCacheAndFinishGC(cx
, cache
));
550 CHECK(cache
.count() == 2);
551 CHECK(!cache
.has(obj3
));
552 CHECK(!cache
.has(obj4
));
554 // Test putNew while sweeping.
556 CHECK(GCUntilCacheSweep(cx
, cache
));
558 CHECK(cache
.putNew(obj3
, 3));
559 CHECK(cache
.putNew(obj4
, 4));
561 CHECK(SweepCacheAndFinishGC(cx
, cache
));
563 CHECK(cache
.count() == 4);
564 CHECK(cache
.has(obj3
));
565 CHECK(cache
.has(obj4
));
572 bool TestReplaceDyingInSet() {
573 // Test replacing dying entries with ones that have the same key using the
576 using Cache
= WeakCache
<
577 GCHashSet
<NumberAndObjectEntry
, StableCellHasher
<NumberAndObjectEntry
>,
579 Cache
cache(JS::GetObjectZone(global
), cx
);
581 RootedObject
value1(cx
, JS_NewPlainObject(cx
));
582 RootedObject
value2(cx
, JS_NewPlainObject(cx
));
586 CHECK(cache
.put(NumberAndObjectEntry(1, value1
)));
587 CHECK(cache
.put(NumberAndObjectEntry(2, value2
)));
588 CHECK(cache
.put(NumberAndObjectEntry(3, value2
)));
589 CHECK(cache
.put(NumberAndObjectEntry(4, value2
)));
590 CHECK(cache
.put(NumberAndObjectEntry(5, value2
)));
591 CHECK(cache
.put(NumberAndObjectEntry(6, value2
)));
592 CHECK(cache
.put(NumberAndObjectEntry(7, value2
)));
595 CHECK(GCUntilCacheSweep(cx
, cache
));
597 CHECK(!cache
.has(NumberAndObjectLookup(2, value1
)));
598 CHECK(!cache
.has(NumberAndObjectLookup(3, value1
)));
599 CHECK(!cache
.has(NumberAndObjectLookup(4, value1
)));
600 CHECK(!cache
.has(NumberAndObjectLookup(5, value1
)));
601 CHECK(!cache
.has(NumberAndObjectLookup(6, value1
)));
603 auto ptr
= cache
.lookupForAdd(NumberAndObjectLookup(2, value1
));
605 CHECK(cache
.add(ptr
, NumberAndObjectEntry(2, value1
)));
607 auto ptr2
= cache
.lookupForAdd(NumberAndObjectLookup(3, value1
));
609 CHECK(cache
.relookupOrAdd(ptr2
, NumberAndObjectLookup(3, value1
),
610 NumberAndObjectEntry(3, value1
)));
612 CHECK(cache
.put(NumberAndObjectEntry(4, value1
)));
613 CHECK(cache
.putNew(NumberAndObjectEntry(5, value1
)));
615 CHECK(cache
.putNew(NumberAndObjectLookup(6, value1
),
616 NumberAndObjectEntry(6, value1
)));
618 CHECK(SweepCacheAndFinishGC(cx
, cache
));
620 CHECK(cache
.count() == 6);
621 CHECK(cache
.has(NumberAndObjectLookup(1, value1
)));
622 CHECK(cache
.has(NumberAndObjectLookup(2, value1
)));
623 CHECK(cache
.has(NumberAndObjectLookup(3, value1
)));
624 CHECK(cache
.has(NumberAndObjectLookup(4, value1
)));
625 CHECK(cache
.has(NumberAndObjectLookup(5, value1
)));
626 CHECK(cache
.has(NumberAndObjectLookup(6, value1
)));
631 bool TestReplaceDyingInMap() {
632 // Test replacing dying entries with ones that have the same key using the
635 using Cache
= WeakCache
<GCHashMap
<uint32_t, HeapPtr
<JSObject
*>,
636 DefaultHasher
<uint32_t>, TempAllocPolicy
>>;
637 Cache
cache(JS::GetObjectZone(global
), cx
);
639 RootedObject
value1(cx
, JS_NewPlainObject(cx
));
640 RootedObject
value2(cx
, JS_NewPlainObject(cx
));
644 CHECK(cache
.put(1, value1
));
645 CHECK(cache
.put(2, value2
));
646 CHECK(cache
.put(3, value2
));
647 CHECK(cache
.put(4, value2
));
648 CHECK(cache
.put(5, value2
));
649 CHECK(cache
.put(6, value2
));
652 CHECK(GCUntilCacheSweep(cx
, cache
));
654 CHECK(!cache
.has(2));
655 CHECK(!cache
.has(3));
656 CHECK(!cache
.has(4));
657 CHECK(!cache
.has(5));
658 CHECK(!cache
.has(6));
660 auto ptr
= cache
.lookupForAdd(2);
662 CHECK(cache
.add(ptr
, 2, value1
));
664 auto ptr2
= cache
.lookupForAdd(3);
666 CHECK(cache
.add(ptr2
, 3, HeapPtr
<JSObject
*>()));
668 auto ptr3
= cache
.lookupForAdd(4);
670 CHECK(cache
.relookupOrAdd(ptr3
, 4, value1
));
672 CHECK(cache
.put(5, value1
));
673 CHECK(cache
.putNew(6, value1
));
675 CHECK(SweepCacheAndFinishGC(cx
, cache
));
677 CHECK(cache
.count() == 6);
678 CHECK(cache
.lookup(1)->value() == value1
);
679 CHECK(cache
.lookup(2)->value() == value1
);
680 CHECK(cache
.lookup(3)->value() == nullptr);
681 CHECK(cache
.lookup(4)->value() == value1
);
682 CHECK(cache
.lookup(5)->value() == value1
);
683 CHECK(cache
.lookup(6)->value() == value1
);
688 bool TestUniqueIDLookups() {
689 // Test hash table lookups during incremental sweeping where the hash is
690 // generated based on a unique ID. The problem is that the unique ID table
691 // will have already been swept by this point so looking up a dead pointer
692 // in the table will fail. This lookup happens if we try to match a live key
693 // against a dead table entry with the same hash code.
695 const size_t DeadFactor
= 3;
696 const size_t ObjectCount
= 100;
698 using Cache
= WeakCache
<
699 GCHashSet
<ObjectEntry
, StableCellHasher
<ObjectEntry
>, TempAllocPolicy
>>;
700 Cache
cache(JS::GetObjectZone(global
), cx
);
702 Rooted
<GCVector
<JSObject
*, 0, SystemAllocPolicy
>> liveObjects(cx
);
704 for (size_t j
= 0; j
< ObjectCount
; j
++) {
705 JSObject
* obj
= JS_NewPlainObject(cx
);
707 CHECK(cache
.put(obj
));
708 if (j
% DeadFactor
== 0) {
709 CHECK(liveObjects
.get().append(obj
));
713 CHECK(cache
.count() == ObjectCount
);
715 CHECK(GCUntilCacheSweep(cx
, cache
));
717 for (size_t j
= 0; j
< liveObjects
.length(); j
++) {
718 CHECK(cache
.has(liveObjects
[j
]));
721 CHECK(SweepCacheAndFinishGC(cx
, cache
));
723 CHECK(cache
.count() == liveObjects
.length());
728 END_TEST(testIncrementalWeakCacheSweeping
)
730 #endif // defined JS_GC_ZEAL