summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/debug/job-queue-01.js
blob: 259d0c2bafd0943d17e2d628165a6457503458ad (plain)
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)`);