1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
// Debuggee promise reaction jobs should not run from debugger callbacks.
// This covers:
// - onDebuggerStatement
// - onStep
// - onEnterFrame
// - onPop
// - onExceptionUnwind
// - breakpoint handlers
// - uncaughtExceptionHook
var g = newGlobal({ newCompartment: true });
g.parent = this;
var dbg = new Debugger;
var gDO = dbg.addDebuggee(g);
var log = '';
// Exercise the promise machinery: resolve a promise and drain the job queue (or
// in HTML terms, perform a microtask checkpoint). When called from a debugger
// hook, the debuggee's microtasks should not run.
function exercise(name) {
log += `${name}-handler`;
Promise.resolve(42).then(v => {
assertEq(v, 42);
log += `${name}-react`;
});
log += `(`;
drainJobQueue();
log += `)`;
// This should be run by the implicit microtask checkpoint after each Debugger
// hook call.
Promise.resolve(42).then(v => {
assertEq(v, 42);
log += `(${name}-tail)`;
});
}
dbg.onDebuggerStatement = function (frame) {
exercise('debugger');
frame.onStep = function () {
this.onStep = undefined;
exercise('step');
};
dbg.onEnterFrame = function (frame) {
dbg.onEnterFrame = undefined;
frame.onPop = function(completion) {
assertEq(completion.return, 'recompense');
exercise('pop');
}
exercise('enter');
}
dbg.onExceptionUnwind = function(frame, value) {
dbg.onExceptionUnwind = undefined;
assertEq(value, 'recidivism');
exercise('exception');
return { return: 'recompense' };
};
// Set a breakpoint on entry to g.breakpoint_here.
const script = 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';
};
g.eval(`
function breakpoint_here() {
throw 'recidivism';
}
parent.log += 'eval(';
// 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 += '...';
breakpoint_here();
parent.log += ')';
`);
log += 'main-drain('
drainJobQueue();
log += ')';
assertEq(log, `eval(\
debugger-handler(debugger-react)\
uncaught-handler((debugger-tail)uncaught-react)(uncaught-tail)\
step-handler(step-react)(step-tail)\
...\
enter-handler(enter-react)(enter-tail)\
bp-handler(bp-react)(bp-tail)\
exception-handler(exception-react)(exception-tail)\
pop-handler(pop-react)(pop-tail)\
)\
main-drain(eval-react)`);
|