Source code

Revision control

Copy as Markdown

Other Tools

// Multiple debuggers get their job queues drained after each hook.
// This covers:
// - onDebuggerStatement
// - onStep
// - onEnterFrame
// - onPop
// - onExceptionUnwind
// - breakpoint handlers
// - uncaughtExceptionHook
const g = newGlobal({ newCompartment: true });
g.parent = this;
var log = '';
let expected_throws = 0;
function setup(global, label) {
const dbg = new Debugger;
dbg.gDO = dbg.addDebuggee(global);
dbg.log = '';
dbg.onDebuggerStatement = function (frame) {
// Exercise the promise machinery: resolve a promise and perform a microtask
// checkpoint. When called from a debugger hook, the debuggee's microtasks
// should not run.
function exercise(name) {
dbg.log += name + ',';
log += `${label}-${name}-handler\n`;
Promise.resolve(42).then(v => {
assertEq(v, 42);
log += `${label}-${name}-tail\n`;
});
}
exercise('debugger');
frame.onStep = function () {
this.onStep = undefined;
exercise('step');
};
dbg.onEnterFrame = function (frame) {
dbg.onEnterFrame = undefined;
frame.onPop = function(completion) {
assertEq(completion.return, 'escutcheon');
exercise('pop');
}
exercise('enter');
}
expected_throws++;
dbg.onExceptionUnwind = function(frame, value) {
dbg.onExceptionUnwind = undefined;
assertEq(value, 'myrmidon');
exercise('exception');
if (--expected_throws > 0) {
return undefined;
} else {
return { return: 'escutcheon' };
}
};
// Set a breakpoint on entry to g.breakpoint_here.
const script = dbg.gDO.getOwnPropertyDescriptor('breakpoint_here').value.script;
const handler = {
hit(frame) {
script.clearAllBreakpoints();
exercise('bp');
}
};
script.setBreakpoint(0, handler);
dbg.uncaughtExceptionHook = function (ex) {
assertEq(ex, 'turncoat');
exercise('uncaught');
};
// Throw an uncaught exception from the Debugger handler. This should reach
// uncaughtExceptionHook, but shouldn't affect the debuggee.
throw 'turncoat';
};
return dbg;
}
const dbg1 = setup(g, '1');
const dbg2 = setup(g, '2');
const dbg3 = setup(g, '3');
g.eval(`
function breakpoint_here() {
throw 'myrmidon';
}
parent.log += 'eval-start\\n';
// DebuggeeWouldRun detection may prevent this callback from running at all if
// bug 1145201 is present. SpiderMonkey will try to run the promise reaction
// job from the Debugger hook's microtask checkpoint, triggering
// DebuggeeWouldRun. This is a little difficult to observe, since the callback
// never even begins execution. But it should cause the 'then' promise to be
// rejected, which the shell will report (if the assertEq(log, ...) doesn't
// kill the test first).
Promise.resolve(84).then(function(v) {
assertEq(v, 84);
parent.log += 'eval-react';
});
debugger;
parent.log += 'stuff to step over\\n';
breakpoint_here();
parent.log += 'eval-end\\n';
`);
log += 'main-drain\n'
drainJobQueue();
log += 'main-drain-done\n';
const regex = new RegExp(`eval-start
.-debugger-handler
.-uncaught-handler
.-debugger-tail
.-uncaught-tail
.-debugger-handler
.-uncaught-handler
.-debugger-tail
.-uncaught-tail
.-debugger-handler
.-uncaught-handler
.-debugger-tail
.-uncaught-tail
.-step-handler
.-step-tail
.-step-handler
.-step-tail
.-step-handler
.-step-tail
stuff to step over
.-enter-handler
.-enter-tail
.-enter-handler
.-enter-tail
.-enter-handler
.-enter-tail
.-bp-handler
.-bp-tail
.-bp-handler
.-bp-tail
.-bp-handler
.-bp-tail
.-exception-handler
.-exception-tail
.-exception-handler
.-exception-tail
.-exception-handler
.-exception-tail
.-pop-handler
.-pop-tail
.-pop-handler
.-pop-tail
.-pop-handler
.-pop-tail
eval-end
main-drain
eval-reactmain-drain-done
`);
assertEq(!!log.match(regex), true)
assertEq(dbg1.log, 'debugger,uncaught,step,enter,bp,exception,pop,');
assertEq(dbg2.log, 'debugger,uncaught,step,enter,bp,exception,pop,');
assertEq(dbg3.log, 'debugger,uncaught,step,enter,bp,exception,pop,');