summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/saved-stacks/1438121-generator.js
blob: 4e3a88122ccce7a15736441b8c0e3e8832c2b325 (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
123
124
125
126
127
128
129
130
131
const mainGlobal = this;
const debuggerGlobal = newGlobal({newCompartment: true});

function Memory({global}) {
  this.dbg = new (debuggerGlobal.Debugger);
  this.gDO = this.dbg.addDebuggee(global);
}

Memory.prototype = {
  constructor: Memory,
  attach() { return Promise.resolve('fake attach result'); },
  detach() { return Promise.resolve('fake detach result'); },
  startRecordingAllocations() {
    this.dbg.memory.trackingAllocationSites = true;
    return Promise.resolve('fake startRecordingAllocations result');
  },
  stopRecordingAllocations() {
    this.dbg.memory.trackingAllocationSites = false;
    return Promise.resolve('fake stopRecordingAllocations result');
  },
  getAllocations() {
    return Promise.resolve({ allocations: this.dbg.memory.drainAllocationsLog() });
  }
};

function ok(cond, msg) {
  assertEq(!!cond, true, `ok(${JSON.stringify(cond)}, ${JSON.stringify(msg)})`);
}

const is = assertEq;

function startServerAndGetSelectedTabMemory() {
  let memory = new Memory({ global: mainGlobal });
  return Promise.resolve({ memory, client: 'fake client' });
}

function destroyServerAndFinish() {
  return Promise.resolve('fake destroyServerAndFinish result');
}

function* body() {
  let { memory, client } = yield startServerAndGetSelectedTabMemory();
  yield memory.attach();

  yield memory.startRecordingAllocations();
  ok(true, "Can start recording allocations");

  // Allocate some objects.

  let alloc1, alloc2, alloc3;

  /* eslint-disable max-nested-callbacks */
  (function outer() {
    (function middle() {
      (function inner() {
        alloc1 = {}; alloc1.line = Error().lineNumber;
        alloc2 = []; alloc2.line = Error().lineNumber;
        // eslint-disable-next-line new-parens
        alloc3 = new function () {}; alloc3.line = Error().lineNumber;
      }());
    }());
  }());
  /* eslint-enable max-nested-callbacks */

  let response = yield memory.getAllocations();

  yield memory.stopRecordingAllocations();
  ok(true, "Can stop recording allocations");

  // Filter out allocations by library and test code, and get only the
  // allocations that occurred in our test case above.

  function isTestAllocation(alloc) {
    let frame = alloc.frame;
    return frame
      && frame.functionDisplayName === "inner"
      && (frame.line === alloc1.line
          || frame.line === alloc2.line
          || frame.line === alloc3.line);
  }

  let testAllocations = response.allocations.filter(isTestAllocation);
  ok(testAllocations.length >= 3,
     "Should find our 3 test allocations (plus some allocations for the error "
     + "objects used to get line numbers)");

  // For each of the test case's allocations, ensure that the parent frame
  // indices are correct. Also test that we did get an allocation at each
  // line we expected (rather than a bunch on the first line and none on the
  // others, etc).

  let expectedLines = new Set([alloc1.line, alloc2.line, alloc3.line]);

  for (let alloc of testAllocations) {
    let innerFrame = alloc.frame;
    ok(innerFrame, "Should get the inner frame");
    is(innerFrame.functionDisplayName, "inner");
    expectedLines.delete(innerFrame.line);

    let middleFrame = innerFrame.parent;
    ok(middleFrame, "Should get the middle frame");
    is(middleFrame.functionDisplayName, "middle");

    let outerFrame = middleFrame.parent;
    ok(outerFrame, "Should get the outer frame");
    is(outerFrame.functionDisplayName, "outer");

    // Not going to test the rest of the frames because they are Task.jsm
    // and promise frames and it gets gross. Plus, I wouldn't want this test
    // to start failing if they changed their implementations in a way that
    // added or removed stack frames here.
  }

  is(expectedLines.size, 0,
     "Should have found all the expected lines");

  yield memory.detach();
  destroyServerAndFinish(client);
}

const generator = body();
loop(generator.next());

function loop({ value: promise, done }) {
  if (done)
    return;
  promise
    .catch(e => loop(generator.throw(e)))
    .then(v => { loop(generator.next(v)); })
    .catch(e => { print(`Error: ${e}\nstack:\n${e.stack}`); });
}