weak-marking-01.js (5903B)
1 // These tests will be using object literals as keys, and we want some of them 2 // to be dead after being inserted into a WeakMap. That means we must wrap 3 // everything in functions because it seems like the toplevel script hangs onto 4 // its object literals. 5 6 gczeal(0); 7 gcparam('minNurseryBytes', 1024 * 1024); 8 gcparam('maxNurseryBytes', 1024 * 1024); 9 10 function startIncrementalGC() { 11 startgc(1, 'shrinking'); 12 while (gcstate() === "Prepare") { 13 gcslice(1); 14 } 15 } 16 17 function syncIncrementalGC() { 18 startIncrementalGC(); 19 finishgc(); 20 } 21 22 // All reachable keys should be found, and the rest should be swept. 23 function basicSweeping() { 24 gc(); 25 26 var wm1 = new WeakMap(); 27 wm1.set({'name': 'obj1'}, {'name': 'val1'}); 28 var hold = {'name': 'obj2'}; 29 wm1.set(hold, {'name': 'val2'}); 30 wm1.set({'name': 'obj3'}, {'name': 'val3'}); 31 32 syncIncrementalGC(); 33 34 assertEq(wm1.get(hold).name, 'val2'); 35 assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1); 36 } 37 38 basicSweeping(); 39 40 // Keep values alive even when they are only referenced by (live) WeakMap values. 41 function weakGraph() { 42 gc(); 43 44 var wm1 = new WeakMap(); 45 var obj1 = {'name': 'obj1'}; 46 var obj2 = {'name': 'obj2'}; 47 var obj3 = {'name': 'obj3'}; 48 var obj4 = {'name': 'obj4'}; 49 var clear = {'name': ''}; // Make the interpreter forget about the last obj created 50 51 wm1.set(obj2, obj3); 52 wm1.set(obj3, obj1); 53 wm1.set(obj4, obj1); // This edge will be cleared 54 obj1 = obj3 = obj4 = undefined; 55 56 syncIncrementalGC(); 57 58 assertEq(obj2.name, "obj2"); 59 assertEq(wm1.get(obj2).name, "obj3"); 60 assertEq(wm1.get(wm1.get(obj2)).name, "obj1"); 61 print(nondeterministicGetWeakMapKeys(wm1).map(o => o.name).join(",")); 62 assertEq(nondeterministicGetWeakMapKeys(wm1).length, 2); 63 } 64 65 weakGraph(); 66 67 // ...but the weakmap itself has to stay alive, too. 68 function deadWeakMap() { 69 gc(); 70 71 var wm1 = new WeakMap(); 72 var obj1 = makeFinalizeObserver(); 73 var obj2 = {'name': 'obj2'}; 74 var obj3 = {'name': 'obj3'}; 75 var obj4 = {'name': 'obj4'}; 76 var clear = {'name': ''}; // Make the interpreter forget about the last obj created 77 78 wm1.set(obj2, obj3); 79 wm1.set(obj3, obj1); 80 wm1.set(obj4, obj1); // This edge will be cleared 81 var initialCount = finalizeCount(); 82 obj1 = obj3 = obj4 = undefined; 83 wm1 = undefined; 84 85 syncIncrementalGC(); 86 87 assertEq(obj2.name, "obj2"); 88 assertEq(finalizeCount(), initialCount + 1); 89 } 90 91 deadWeakMap(); 92 93 // WeakMaps do not strongly reference their keys or values. (WeakMaps hold a 94 // collection of (strong) references to *edges* from keys to values. If the 95 // WeakMap is not live, then its edges are of course not live either. An edge 96 // holds neither its key nor its value live; it just holds a strong ref from 97 // the key to the value. So if the key is live, the value is live too, but the 98 // edge itself has no references to anything.) 99 function deadKeys() { 100 gc(); 101 102 var wm1 = new WeakMap(); 103 var obj1 = makeFinalizeObserver(); 104 var obj2 = {'name': 'obj2'}; 105 var obj3 = makeFinalizeObserver(); 106 var clear = {}; // Make the interpreter forget about the last obj created 107 108 wm1.set(obj1, obj1); 109 wm1.set(obj3, obj2); 110 obj1 = obj3 = undefined; 111 var initialCount = finalizeCount(); 112 113 syncIncrementalGC(); 114 115 assertEq(finalizeCount(), initialCount + 2); 116 assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0); 117 } 118 119 deadKeys(); 120 121 // The weakKeys table has to grow if it encounters enough new unmarked weakmap 122 // keys. Trigger this to happen during weakmap marking. 123 // 124 // There's some trickiness involved in getting it to test the right thing, 125 // because if a key is marked before the weakmap, then it won't get entered 126 // into the weakKeys table. This chains through multiple weakmap layers to 127 // ensure that the objects can't get marked before the weakmaps. 128 function weakKeysRealloc() { 129 gc(); 130 131 var wm1 = new WeakMap; 132 var wm2 = new WeakMap; 133 var wm3 = new WeakMap; 134 var obj1 = {'name': 'obj1'}; 135 var obj2 = {'name': 'obj2'}; 136 wm1.set(obj1, wm2); 137 wm2.set(obj2, wm3); 138 for (var i = 0; i < 10000; i++) { 139 wm3.set(Object.create(null), wm2); 140 } 141 wm3.set(Object.create(null), makeFinalizeObserver()); 142 143 wm2 = undefined; 144 wm3 = undefined; 145 obj2 = undefined; 146 147 var initialCount = finalizeCount(); 148 syncIncrementalGC(); 149 assertEq(finalizeCount(), initialCount + 1); 150 } 151 152 weakKeysRealloc(); 153 154 // The weakKeys table is populated during regular marking. When a key is later 155 // deleted, both it and its delegate should be removed from weakKeys. 156 // Otherwise, it will hold its value live if it gets marked, and our table 157 // traversals will include non-keys, etc. 158 function deletedKeys() { 159 gc(); 160 161 var wm = new WeakMap; 162 var g = newGlobal(); 163 164 for (var i = 0; i < 1000; i++) 165 wm.set(g.Object.create(null), i); 166 167 startIncrementalGC(); 168 169 for (var key of nondeterministicGetWeakMapKeys(wm)) { 170 if (wm.get(key) % 2) 171 wm.delete(key); 172 } 173 174 gc(); 175 } 176 177 deletedKeys(); 178 179 // Test adding keys during incremental GC. 180 function incrementalAdds() { 181 gc(); 182 183 var initialCount = finalizeCount(); 184 185 var wm1 = new WeakMap; 186 var wm2 = new WeakMap; 187 var wm3 = new WeakMap; 188 var obj1 = {'name': 'obj1'}; 189 var obj2 = {'name': 'obj2'}; 190 wm1.set(obj1, wm2); 191 wm2.set(obj2, wm3); 192 for (var i = 0; i < 10000; i++) { 193 wm3.set(Object.create(null), wm2); 194 } 195 wm3.set(Object.create(null), makeFinalizeObserver()); 196 obj2 = undefined; 197 198 var obj3 = []; 199 startIncrementalGC(); 200 var M = 10; 201 var N = 800; 202 for (var j = 0; j < M; j++) { 203 for (var i = 0; i < N; i++) 204 wm3.set(Object.create(null), makeFinalizeObserver()); // Should be swept 205 for (var i = 0; i < N; i++) { 206 obj3.push({'name': 'obj3'}); 207 wm1.set(obj3[obj3.length - 1], makeFinalizeObserver()); // Should not be swept 208 } 209 gcslice(); 210 } 211 212 wm2 = undefined; 213 wm3 = undefined; 214 215 gc(); 216 print("initialCount = " + initialCount); 217 assertEq(finalizeCount(), initialCount + 1 + M * N); 218 } 219 220 incrementalAdds();