Frame-onStep-generators-gc-01.js (2610B)
1 // onStep hooks on suspended Frames can keep Debuggers alive, even chaining them. 2 3 // The path through the heap we're building and testing here is: 4 // gen0 (generator object) -> frame1 (suspended Frame with .onStep) -> dbg1 (Debugger object) 5 // -> gen1 -> frame2 -> dbg2 6 // where everything after `gen1` is otherwise unreachable, and the edges 7 // `frame1 -> dbg1` and `frames2 -> dbg2` are due to the .onStep handlers, not 8 // strong refrences. 9 // 10 // There is no easy way to thread an event through this whole path; when we 11 // call gen0.next(), it will fire frame1.onStep(), but from there, making sure 12 // gen1.next() is called requires some minor heroics (see the WeakMap below). 13 14 var gen0; 15 16 var hits2 = 0; 17 var resuming2 = false; 18 19 function onStep2() { 20 if (resuming2) { 21 hits2++; 22 resuming2 = false; 23 } 24 } 25 26 function setup() { 27 let g1 = newGlobal({newCompartment: true}); 28 g1.eval(` 29 function* gf1() { 30 debugger; 31 yield 1; 32 return 'done'; 33 } 34 `); 35 gen0 = g1.gf1(); 36 37 let g2 = newGlobal({newCompartment: true}); 38 g2.eval(` 39 function* gf2() { debugger; yield 1; return 'done'; } 40 41 var resuming1 = false; 42 43 function makeOnStepHook1(dbg1) { 44 // We use this WeakMap as a weak reference from frame1.onStep to dbg1. 45 var weak = new WeakMap(); 46 weak.set(dbg1, {}); 47 return () => { 48 if (resuming1) { 49 var dbg1Arr = nondeterministicGetWeakMapKeys(weak); 50 assertEq(dbg1Arr.length, 1); 51 dbg1Arr[0].gen1.next(); 52 resuming1 = false; 53 } 54 }; 55 } 56 57 function test(g1, gen0) { 58 let dbg1 = Debugger(g1); 59 dbg1.onDebuggerStatement = frame1 => { 60 frame1.onStep = makeOnStepHook1(dbg1); 61 dbg1.onDebuggerStatement = undefined; 62 }; 63 gen0.next(); // run to yield point, creating frame1 and setting its onStep hook 64 resuming1 = true; 65 dbg1.gen1 = gf2(); 66 return dbg1.gen1; 67 } 68 `); 69 70 let dbg2 = Debugger(g2); 71 dbg2.onDebuggerStatement = frame2 => { 72 frame2.onStep = onStep2; 73 dbg2.onDebuggerStatement = undefined; 74 }; 75 var gen1 = g2.test(g1, gen0); 76 gen1.next(); // run to yield point, creating frame2 and setting its onStep hook 77 resuming2 = true; 78 } 79 80 setup(); 81 gc(); 82 assertEq(hits2, 0); 83 gen0.next(); // fires frame1.onStep, which calls gen1.next(), which fires frame2.onStep 84 assertEq(hits2, 1);