weak-marking-02.js (10423B)
1 // |jit-test| allow-unhandlable-oom 2 3 // These tests will be using object literals as keys, and we want some of them 4 // to be dead after being inserted into a WeakMap. That means we must wrap 5 // everything in functions because it seems like the toplevel script hangs onto 6 // its object literals. 7 8 // Cross-compartment WeakMap keys work by storing a cross-compartment wrapper 9 // in the WeakMap, and the actual "delegate" object in the target compartment 10 // is the thing whose liveness is checked. 11 12 gczeal(0); 13 14 var g2 = newGlobal({newCompartment: true}); 15 g2.eval('function genObj(name) { return {"name": name} }'); 16 17 function basicSweeping() { 18 var wm1 = new WeakMap(); 19 wm1.set({'name': 'obj1'}, {'name': 'val1'}); 20 var hold = g2.genObj('obj2'); 21 wm1.set(hold, {'name': 'val2'}); 22 wm1.set({'name': 'obj3'}, {'name': 'val3'}); 23 var obj4 = g2.genObj('obj4'); 24 wm1.set(obj4, {'name': 'val3'}); 25 obj4 = undefined; 26 27 startgc(100000, 'shrinking'); 28 gcslice(); 29 assertEq(wm1.get(hold).name, 'val2'); 30 assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1); 31 } 32 33 basicSweeping(); 34 35 // Same, but behind an additional WM layer, to avoid ordering problems (not 36 // that I've checked that basicSweeping even has any problems.) 37 38 function basicSweeping2() { 39 var wm1 = new WeakMap(); 40 wm1.set({'name': 'obj1'}, {'name': 'val1'}); 41 var hold = g2.genObj('obj2'); 42 wm1.set(hold, {'name': 'val2'}); 43 wm1.set({'name': 'obj3'}, {'name': 'val3'}); 44 var obj4 = g2.genObj('obj4'); 45 wm1.set(obj4, {'name': 'val3'}); 46 obj4 = undefined; 47 48 var base1 = {'name': 'base1'}; 49 var base2 = {'name': 'base2'}; 50 var wm_base1 = new WeakMap(); 51 var wm_base2 = new WeakMap(); 52 wm_base1.set(base1, wm_base2); 53 wm_base2.set(base2, wm1); 54 wm1 = wm_base2 = undefined; 55 56 startgc(100000, 'shrinking'); 57 gcslice(); 58 59 assertEq(nondeterministicGetWeakMapKeys(wm_base1).length, 1); 60 wm_base2 = wm_base1.get(base1); 61 assertEq(nondeterministicGetWeakMapKeys(wm_base2).length, 1); 62 assertEq(nondeterministicGetWeakMapKeys(wm_base1)[0], base1); 63 assertEq(nondeterministicGetWeakMapKeys(wm_base2)[0], base2); 64 wm_base2 = wm_base1.get(base1); 65 wm1 = wm_base2.get(base2); 66 assertEq(wm1.get(hold).name, 'val2'); 67 assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1); 68 } 69 70 basicSweeping2(); 71 72 // Scatter the weakmap, the keys, and the values among different compartments. 73 74 function tripleZoneMarking() { 75 var g1 = newGlobal({newCompartment: true}); 76 var g2 = newGlobal({newCompartment: true}); 77 var g3 = newGlobal({newCompartment: true}); 78 79 var wm = g1.eval("new WeakMap()"); 80 var key = g2.eval("({'name': 'obj1'})"); 81 var value = g3.eval("({'name': 'val1'})"); 82 g1 = g2 = g3 = undefined; 83 wm.set(key, value); 84 85 // Make all of it only reachable via a weakmap in the main test compartment, 86 // so that all of this happens during weak marking mode. Use the weakmap as 87 // its own key, so we know that the weakmap will get traced before the key 88 // and therefore will populate the weakKeys table and all of that jazz. 89 var base_wm = new WeakMap(); 90 base_wm.set(base_wm, [ wm, key ]); 91 92 wm = key = value = undefined; 93 94 startgc(100000, 'shrinking'); 95 gcslice(); 96 97 var keys = nondeterministicGetWeakMapKeys(base_wm); 98 assertEq(keys.length, 1); 99 var [ wm, key ] = base_wm.get(keys[0]); 100 assertEq(key.name, "obj1"); 101 value = wm.get(key); 102 assertEq(value.name, "val1"); 103 } 104 105 tripleZoneMarking(); 106 107 // Same as above, but this time use enqueueMark to enforce ordering. 108 109 function tripleZoneMarking2() { 110 var g1 = newGlobal(); 111 var g2 = newGlobal(); 112 var g3 = newGlobal(); 113 114 var wm = g1.eval("wm = new WeakMap()"); 115 var key = g2.eval("key = ({'name': 'obj1'})"); 116 var value = g3.eval("({'name': 'val1'})"); 117 wm.set(key, value); 118 119 enqueueMark("enter-weak-marking-mode"); 120 g1.eval("enqueueMark(wm)"); // weakmap 121 g2.eval("enqueueMark(key)"); // delegate 122 g1.wm = g2.key = undefined; 123 g1 = g2 = g3 = undefined; 124 wm = key = value = undefined; 125 126 gc(); 127 128 var [ dummy, weakmap, keywrapper ] = getMarkQueue(); 129 assertEq(keywrapper.name, "obj1"); 130 value = weakmap.get(keywrapper); 131 assertEq(value.name, "val1"); 132 133 clearMarkQueue(); 134 } 135 136 if (this.enqueueMark) 137 tripleZoneMarking2(); 138 139 function enbugger() { 140 var g = newGlobal({newCompartment: true}); 141 var dbg = new Debugger; 142 g.eval("function debuggee_f() { return 1; }"); 143 g.eval("function debuggee_g() { return 1; }"); 144 dbg.addDebuggee(g); 145 var [ s ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_f"); 146 var [ s2 ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_g"); 147 g.eval("debuggee_f = null"); 148 gc(); 149 dbg.removeAllDebuggees(); 150 gc(); 151 assertEq(s.displayName, "debuggee_f"); 152 153 var wm = new WeakMap; 154 var obj = Object.create(null); 155 var obj2 = Object.create(null); 156 wm.set(obj, s); 157 wm.set(obj2, obj); 158 wm.set(s2, obj2); 159 s = s2 = obj = obj2 = null; 160 161 gc(); 162 } 163 164 enbugger(); 165 166 // Want to test: zone edges 167 // Make map with cross-zone delegate. Collect the zones one at a time. 168 function zone_edges() { 169 var g3 = newGlobal(); 170 g3.eval('function genObj(name) { return {"name": name} }'); 171 172 var wm1 = new WeakMap(); 173 var hold = g2.genObj('obj1'); 174 var others = [g2.genObj('key2'), g2.genObj('key3')]; 175 wm1.set(hold, {'name': g3.genObj('val1'), 'others': others}); 176 others = null; 177 178 var m = new Map; 179 m.set(m, hold); 180 hold = null; 181 182 const zones = [ this, g2, g3 ]; 183 for (let zonebits = 0; zonebits < 2 ** zones.length; zonebits++) { 184 for (let z in zones) { 185 if (zonebits & (1 << z)) 186 schedulezone(zones[z]); 187 } 188 startgc(1); 189 wm1.set(wm1.get(m.get(m)).others[0], g2.genObj('val2')); 190 gcslice(1000000); 191 wm1.set(wm1.get(m.get(m)).others[1], g2.genObj('val3')); 192 gc(); 193 assertEq(wm1.get(m.get(m)).name.name, 'val1'); 194 assertEq(wm1.get(m.get(m)).others[0].name, 'key2'); 195 assertEq(wm1.get(wm1.get(m.get(m)).others[0]).name, 'val2'); 196 assertEq(wm1.get(m.get(m)).others[1].name, 'key3'); 197 assertEq(wm1.get(wm1.get(m.get(m)).others[1]).name, 'val3'); 198 assertEq(nondeterministicGetWeakMapKeys(wm1).length, 3); 199 } 200 201 // Do it again, with nuking. 202 const wm2 = g2.eval("new WeakMap"); 203 wm2.set(wm1.get(m.get(m)).others[0], Object.create(null)); 204 for (let zonebits = 0; zonebits < 2 ** zones.length; zonebits++) { 205 for (let z in zones) { 206 if (zonebits & (1 << z)) 207 schedulezone(zones[z]); 208 } 209 startgc(1); 210 wm1.set(wm1.get(m.get(m)).others[0], g2.genObj('val2')); 211 gcslice(1000000); 212 wm1.set(wm1.get(m.get(m)).others[1], g2.genObj('val3')); 213 nukeCCW(wm1.get(wm1.get(m.get(m)).others[0])); 214 nukeCCW(wm1.get(wm1.get(m.get(m)).others[1])); 215 gc(); 216 assertEq(wm1.get(m.get(m)).name.name, 'val1'); 217 assertEq(wm1.get(m.get(m)).others[0].name, 'key2'); 218 assertEq(wm1.get(m.get(m)).others[1].name, 'key3'); 219 assertEq(nondeterministicGetWeakMapKeys(wm1).length, 3); 220 } 221 } 222 223 zone_edges(); 224 225 // Stress test: lots of cross-zone and same-zone cross-compartment edges, and 226 // exercise the barriers. 227 function stress(opt) { 228 printErr(JSON.stringify(opt)); 229 230 var g1 = this; 231 var g2 = newGlobal({sameZoneAs: g1}); 232 var g3 = newGlobal(); 233 234 var globals = g1.globals = g2.globals = g3.globals = [ g1, g2, g3 ]; 235 g1.name = 'main'; 236 g2.name = 'same-zone'; 237 g3.name = 'other-zone'; 238 g1.names = g2.names = g3.names = [ g1.name, g2.name, g3.name ]; 239 240 // Basic setup: 241 // 242 // Three different globals, each with a weakmap and an object. Each global's 243 // weakmap contains all 3 objects (1 from each global) as keys. Internally, 244 // that means that each weakmap will contain one local object and two 245 // cross-compartment wrappers. 246 // 247 // Now duplicate that 3 times. The first weakmap will be unmodified. The 248 // second weakmap will have its keys updated to different values. The third 249 // weakmap will have its keys deleted. 250 251 for (const i in globals) { 252 const g = globals[i]; 253 g.eval('function genObj(name) { return {"name": name} }'); 254 g.eval("weakmap0 = new WeakMap()"); 255 g.eval("weakmap1 = new WeakMap()"); 256 g.eval("weakmap2 = new WeakMap()"); 257 g.eval(`obj = genObj('global-${names[i]}-object')`); 258 for (const j in [0, 1, 2]) { 259 g.eval(`weakmap${j}.set(genObj('global-${names[i]}-key}'), genObj("value"))`); 260 } 261 } 262 263 for (const i in globals) { 264 const g = globals[i]; 265 for (const j in globals) { 266 for (const k in [0, 1, 2]) { 267 g.eval(`weakmap${k}.set(globals[${j}].obj, genObj('value-${i}-${j}'))`); 268 } 269 } 270 } 271 272 // Construct object keys to retrieve the weakmaps with. 273 for (const g of globals) { 274 g.eval(`plain = genObj("plain")`); 275 g.eval(`update = genObj("update")`); 276 g.eval(`remove = genObj("remove")`); 277 } 278 279 // Put the weakmaps in another WeakMap. 280 for (const g of globals) { 281 g.eval(`weakmaps = new WeakMap(); 282 weakmaps.set(plain, weakmap0); 283 weakmaps.set(update, weakmap1); 284 weakmaps.set(remove, weakmap2);`); 285 } 286 287 // Eliminate the edges from the global to the object being used as a key. But 288 // assuming we want the key to be live (nothing else points to it), hide it 289 // behind another weakmap layer. 290 for (const g of globals) { 291 if (opt.live) { 292 g.eval("keyholder = genObj('key-holder')"); 293 g.eval("weakmaps.set(keyholder, obj)"); 294 } 295 g.eval("obj = null"); 296 } 297 298 // If we want a layer of indirection, remove the edges from the globals to 299 // their weakmaps. But note that the original purpose of this test *wants* 300 // the weakmaps themselves to be visited early, so that gcWeakKeys will be 301 // populated with not-yet-marked keys and the barriers will need to update 302 // entries there. 303 if (opt.indirect) { 304 for (const g of globals) { 305 g.eval("weakmap0 = weakmap1 = weakmap2 = null"); 306 } 307 } 308 309 // Start an incremental GC. TODO: need a zeal mode to yield before entering 310 // weak marking mode. 311 startgc(1); 312 313 // Do the mutations. 314 if (opt.live) { 315 for (const g of globals) { 316 g.eval("weakmaps.get(update).set(weakmaps.get(keyholder), genObj('val'))"); 317 g.eval("weakmaps.get(remove).delete(weakmaps.get(keyholder))"); 318 } 319 } 320 321 if (opt.nuke) { 322 for (const g of globals) { 323 if (g.name != 'main') 324 g.eval("nukeAllCCWs()"); 325 } 326 } 327 328 // Finish the GC. 329 gc(); 330 } 331 332 for (const live of [true, false]) { 333 for (const indirect of [true, false]) { 334 for (const nuke of [true, false]) { 335 stress({live, indirect, nuke}); 336 } 337 } 338 }