Source code

Revision control

Copy as Markdown

Other Tools

// 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();