diff options
Diffstat (limited to 'js/src/jit-test/tests/gc/weak-marking-01.js')
-rw-r--r-- | js/src/jit-test/tests/gc/weak-marking-01.js | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/gc/weak-marking-01.js b/js/src/jit-test/tests/gc/weak-marking-01.js new file mode 100644 index 0000000000..a132398510 --- /dev/null +++ b/js/src/jit-test/tests/gc/weak-marking-01.js @@ -0,0 +1,220 @@ +// These tests will be using object literals as keys, and we want some of them +// to be dead after being inserted into a WeakMap. That means we must wrap +// everything in functions because it seems like the toplevel script hangs onto +// its object literals. + +gczeal(0); +gcparam('minNurseryBytes', 1024 * 1024); +gcparam('maxNurseryBytes', 1024 * 1024); + +function startIncrementalGC() { + startgc(1, 'shrinking'); + while (gcstate() === "Prepare") { + gcslice(1); + } +} + +function syncIncrementalGC() { + startIncrementalGC(); + finishgc(); +} + +// All reachable keys should be found, and the rest should be swept. +function basicSweeping() { + gc(); + + var wm1 = new WeakMap(); + wm1.set({'name': 'obj1'}, {'name': 'val1'}); + var hold = {'name': 'obj2'}; + wm1.set(hold, {'name': 'val2'}); + wm1.set({'name': 'obj3'}, {'name': 'val3'}); + + syncIncrementalGC(); + + assertEq(wm1.get(hold).name, 'val2'); + assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1); +} + +basicSweeping(); + +// Keep values alive even when they are only referenced by (live) WeakMap values. +function weakGraph() { + gc(); + + var wm1 = new WeakMap(); + var obj1 = {'name': 'obj1'}; + var obj2 = {'name': 'obj2'}; + var obj3 = {'name': 'obj3'}; + var obj4 = {'name': 'obj4'}; + var clear = {'name': ''}; // Make the interpreter forget about the last obj created + + wm1.set(obj2, obj3); + wm1.set(obj3, obj1); + wm1.set(obj4, obj1); // This edge will be cleared + obj1 = obj3 = obj4 = undefined; + + syncIncrementalGC(); + + assertEq(obj2.name, "obj2"); + assertEq(wm1.get(obj2).name, "obj3"); + assertEq(wm1.get(wm1.get(obj2)).name, "obj1"); + print(nondeterministicGetWeakMapKeys(wm1).map(o => o.name).join(",")); + assertEq(nondeterministicGetWeakMapKeys(wm1).length, 2); +} + +weakGraph(); + +// ...but the weakmap itself has to stay alive, too. +function deadWeakMap() { + gc(); + + var wm1 = new WeakMap(); + var obj1 = makeFinalizeObserver(); + var obj2 = {'name': 'obj2'}; + var obj3 = {'name': 'obj3'}; + var obj4 = {'name': 'obj4'}; + var clear = {'name': ''}; // Make the interpreter forget about the last obj created + + wm1.set(obj2, obj3); + wm1.set(obj3, obj1); + wm1.set(obj4, obj1); // This edge will be cleared + var initialCount = finalizeCount(); + obj1 = obj3 = obj4 = undefined; + wm1 = undefined; + + syncIncrementalGC(); + + assertEq(obj2.name, "obj2"); + assertEq(finalizeCount(), initialCount + 1); +} + +deadWeakMap(); + +// WeakMaps do not strongly reference their keys or values. (WeakMaps hold a +// collection of (strong) references to *edges* from keys to values. If the +// WeakMap is not live, then its edges are of course not live either. An edge +// holds neither its key nor its value live; it just holds a strong ref from +// the key to the value. So if the key is live, the value is live too, but the +// edge itself has no references to anything.) +function deadKeys() { + gc(); + + var wm1 = new WeakMap(); + var obj1 = makeFinalizeObserver(); + var obj2 = {'name': 'obj2'}; + var obj3 = makeFinalizeObserver(); + var clear = {}; // Make the interpreter forget about the last obj created + + wm1.set(obj1, obj1); + wm1.set(obj3, obj2); + obj1 = obj3 = undefined; + var initialCount = finalizeCount(); + + syncIncrementalGC(); + + assertEq(finalizeCount(), initialCount + 2); + assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0); +} + +deadKeys(); + +// The weakKeys table has to grow if it encounters enough new unmarked weakmap +// keys. Trigger this to happen during weakmap marking. +// +// There's some trickiness involved in getting it to test the right thing, +// because if a key is marked before the weakmap, then it won't get entered +// into the weakKeys table. This chains through multiple weakmap layers to +// ensure that the objects can't get marked before the weakmaps. +function weakKeysRealloc() { + gc(); + + var wm1 = new WeakMap; + var wm2 = new WeakMap; + var wm3 = new WeakMap; + var obj1 = {'name': 'obj1'}; + var obj2 = {'name': 'obj2'}; + wm1.set(obj1, wm2); + wm2.set(obj2, wm3); + for (var i = 0; i < 10000; i++) { + wm3.set(Object.create(null), wm2); + } + wm3.set(Object.create(null), makeFinalizeObserver()); + + wm2 = undefined; + wm3 = undefined; + obj2 = undefined; + + var initialCount = finalizeCount(); + syncIncrementalGC(); + assertEq(finalizeCount(), initialCount + 1); +} + +weakKeysRealloc(); + +// The weakKeys table is populated during regular marking. When a key is later +// deleted, both it and its delegate should be removed from weakKeys. +// Otherwise, it will hold its value live if it gets marked, and our table +// traversals will include non-keys, etc. +function deletedKeys() { + gc(); + + var wm = new WeakMap; + var g = newGlobal(); + + for (var i = 0; i < 1000; i++) + wm.set(g.Object.create(null), i); + + startIncrementalGC(); + + for (var key of nondeterministicGetWeakMapKeys(wm)) { + if (wm.get(key) % 2) + wm.delete(key); + } + + gc(); +} + +deletedKeys(); + +// Test adding keys during incremental GC. +function incrementalAdds() { + gc(); + + var initialCount = finalizeCount(); + + var wm1 = new WeakMap; + var wm2 = new WeakMap; + var wm3 = new WeakMap; + var obj1 = {'name': 'obj1'}; + var obj2 = {'name': 'obj2'}; + wm1.set(obj1, wm2); + wm2.set(obj2, wm3); + for (var i = 0; i < 10000; i++) { + wm3.set(Object.create(null), wm2); + } + wm3.set(Object.create(null), makeFinalizeObserver()); + obj2 = undefined; + + var obj3 = []; + startIncrementalGC(); + var M = 10; + var N = 800; + for (var j = 0; j < M; j++) { + for (var i = 0; i < N; i++) + wm3.set(Object.create(null), makeFinalizeObserver()); // Should be swept + for (var i = 0; i < N; i++) { + obj3.push({'name': 'obj3'}); + wm1.set(obj3[obj3.length - 1], makeFinalizeObserver()); // Should not be swept + } + gcslice(); + } + + wm2 = undefined; + wm3 = undefined; + + gc(); + print("initialCount = " + initialCount); + assertEq(finalizeCount(), initialCount + 1 + M * N); +} + +incrementalAdds(); |