job-queue-01.js (3247B)
1 // Debuggee promise reaction jobs should not run from debugger callbacks. 2 // This covers: 3 // - onDebuggerStatement 4 // - onStep 5 // - onEnterFrame 6 // - onPop 7 // - onExceptionUnwind 8 // - breakpoint handlers 9 // - uncaughtExceptionHook 10 11 var g = newGlobal({ newCompartment: true }); 12 g.parent = this; 13 var dbg = new Debugger; 14 var gDO = dbg.addDebuggee(g); 15 var log = ''; 16 17 // Exercise the promise machinery: resolve a promise and drain the job queue (or 18 // in HTML terms, perform a microtask checkpoint). When called from a debugger 19 // hook, the debuggee's microtasks should not run. 20 function exercise(name) { 21 log += `${name}-handler`; 22 Promise.resolve(42).then(v => { 23 assertEq(v, 42); 24 log += `${name}-react`; 25 }); 26 log += `(`; 27 drainJobQueue(); 28 log += `)`; 29 30 // This should be run by the implicit microtask checkpoint after each Debugger 31 // hook call. 32 Promise.resolve(42).then(v => { 33 assertEq(v, 42); 34 log += `(${name}-tail)`; 35 }); 36 } 37 38 dbg.onDebuggerStatement = function (frame) { 39 exercise('debugger'); 40 41 frame.onStep = function () { 42 this.onStep = undefined; 43 exercise('step'); 44 }; 45 46 dbg.onEnterFrame = function (frame) { 47 dbg.onEnterFrame = undefined; 48 frame.onPop = function(completion) { 49 assertEq(completion.return, 'recompense'); 50 exercise('pop'); 51 } 52 53 exercise('enter'); 54 } 55 56 dbg.onExceptionUnwind = function(frame, value) { 57 dbg.onExceptionUnwind = undefined; 58 assertEq(value, 'recidivism'); 59 exercise('exception'); 60 return { return: 'recompense' }; 61 }; 62 63 // Set a breakpoint on entry to g.breakpoint_here. 64 const script = gDO.getOwnPropertyDescriptor('breakpoint_here').value.script; 65 const handler = { 66 hit(frame) { 67 script.clearAllBreakpoints(); 68 exercise('bp'); 69 } 70 }; 71 script.setBreakpoint(0, handler); 72 73 dbg.uncaughtExceptionHook = function (ex) { 74 assertEq(ex, 'turncoat'); 75 exercise('uncaught'); 76 }; 77 78 // Throw an uncaught exception from the Debugger handler. This should reach 79 // uncaughtExceptionHook, but shouldn't affect the debuggee. 80 throw 'turncoat'; 81 }; 82 83 g.eval(` 84 function breakpoint_here() { 85 throw 'recidivism'; 86 } 87 88 parent.log += 'eval('; 89 90 // DebuggeeWouldRun detection may prevent this callback from running at all if 91 // bug 1145201 is present. SpiderMonkey will try to run the promise reaction 92 // job from the Debugger hook's microtask checkpoint, triggering 93 // DebuggeeWouldRun. This is a little difficult to observe, since the callback 94 // never even begins execution. But it should cause the 'then' promise to be 95 // rejected, which the shell will report (if the assertEq(log, ...) doesn't 96 // kill the test first). 97 98 Promise.resolve(84).then(function(v) { 99 assertEq(v, 84); 100 parent.log += 'eval-react'; 101 }); 102 debugger; 103 parent.log += '...'; 104 breakpoint_here(); 105 parent.log += ')'; 106 `); 107 108 log += 'main-drain(' 109 drainJobQueue(); 110 log += ')'; 111 112 assertEq(log, `eval(\ 113 debugger-handler(debugger-react)\ 114 uncaught-handler((debugger-tail)uncaught-react)(uncaught-tail)\ 115 step-handler(step-react)(step-tail)\ 116 ...\ 117 enter-handler(enter-react)(enter-tail)\ 118 bp-handler(bp-react)(bp-tail)\ 119 exception-handler(exception-react)(exception-tail)\ 120 pop-handler(pop-react)(pop-tail)\ 121 )\ 122 main-drain(eval-react)`);