tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Script-getPossibleBreakpoints.js (8424B)


      1 // simple ExpressionStatement
      2 assertBreakpoints(`
      3  /*S*/a;
      4  /*S*/obj.prop;
      5 `);
      6 
      7 // ExpressionStatement with calls
      8 assertBreakpoints(`
      9  /*S*/a();
     10  /*S*/obj./*B*/prop();
     11 `);
     12 
     13 // calls with many args
     14 assertBreakpoints(`
     15  /*S*/a/*B*/(1);
     16  /*S*/a/*B*/(1,2);
     17  /*S*/a/*B*/(1,2,3);
     18 `);
     19 
     20 
     21 // ExpressionStatement with nested expression calls.
     22 assertBreakpoints(`
     23  "45";
     24  /*S*/"45" + /*B*/a();
     25  /*S*/b() + "45";
     26 
     27  /*S*/"45" + o./*B*/a();
     28  /*S*/o./*B*/b() + "45";
     29  /*S*/"45" + o./*B*/a() + o./*B*/b();
     30  /*S*/o./*B*/b() + "45" + o./*B*/a();
     31  /*S*/o./*B*/b() + o./*B*/a() + "45";
     32 `);
     33 
     34 // var VariableStatement initializers
     35 assertBreakpoints(`
     36  var foo1 = /*S*/"" + o.a + "" + /*B*/b(),
     37      foo2 = /*S*/"45",
     38      foo3 = /*S*/"45" + /*B*/a(),
     39      foo4 = /*S*/b() + "45",
     40      foo5 = /*S*/"45" + /*B*/a() + /*B*/b(),
     41      foo6 = /*S*/b() + "45" + /*B*/a(),
     42      foo7 = /*S*/b() + /*B*/a() + "45",
     43      foo8 = /*S*/"45" + o./*B*/a(),
     44      foo9 = /*S*/o./*B*/b() + "45",
     45      foo10 = /*S*/"45" + o./*B*/a() + o./*B*/b(),
     46      foo11 = /*S*/o./*B*/b() + "45" + o./*B*/a(),
     47      foo12 = /*S*/o./*B*/b() + o./*B*/a() + "45";
     48 `);
     49 
     50 // let VariableStatement initializers
     51 assertBreakpoints(`
     52  let foo1 = /*S*/"" + o.a + "" + /*B*/b(),
     53      foo2 = /*S*/"45",
     54      foo3 = /*S*/"45" + /*B*/a(),
     55      foo4 = /*S*/b() + "45",
     56      foo5 = /*S*/"45" + /*B*/a() + /*B*/b(),
     57      foo6 = /*S*/b() + "45" + /*B*/a(),
     58      foo7 = /*S*/b() + /*B*/a() + "45",
     59      foo8 = /*S*/"45" + o./*B*/a(),
     60      foo9 = /*S*/o./*B*/b() + "45",
     61      foo10 = /*S*/"45" + o./*B*/a() + o./*B*/b(),
     62      foo11 = /*S*/o./*B*/b() + "45" + o./*B*/a(),
     63      foo12 = /*S*/o./*B*/b() + o./*B*/a() + "45";
     64 `);
     65 
     66 // const VariableStatement initializers
     67 assertBreakpoints(`
     68  const foo1 = /*S*/"" + o.a + "" + /*B*/b(),
     69        foo2 = /*S*/"45",
     70        foo3 = /*S*/"45" + /*B*/a(),
     71        foo4 = /*S*/b() + "45",
     72        foo5 = /*S*/"45" + /*B*/a() + /*B*/b(),
     73        foo6 = /*S*/b() + "45" + /*B*/a(),
     74        foo7 = /*S*/b() + /*B*/a() + "45",
     75        foo8 = /*S*/"45" + o./*B*/a(),
     76        foo9 = /*S*/o./*B*/b() + "45",
     77        foo10 = /*S*/"45" + o./*B*/a() + o./*B*/b(),
     78        foo11 = /*S*/o./*B*/b() + "45" + o./*B*/a(),
     79        foo12 = /*S*/o./*B*/b() + o./*B*/a() + "45";
     80 `);
     81 
     82 // EmptyStatement
     83 assertBreakpoints(`
     84  ;
     85  ;
     86  ;
     87  /*S*/a();
     88 `);
     89 
     90 // IfStatement
     91 assertBreakpoints(`
     92  if (/*S*/a) {}
     93  if (/*S*/a()) {}
     94  if (/*S*/obj.prop) {}
     95  if (/*S*/obj./*B*/prop()) {}
     96  if (/*S*/"42" + a) {}
     97  if (/*S*/"42" + /*B*/a()) {}
     98  if (/*S*/"42" + obj.prop) {}
     99  if (/*S*/"42" + obj./*B*/prop()) {}
    100 `);
    101 
    102 // DoWhile
    103 assertBreakpoints(`
    104  do {
    105    /*S*/fn();
    106  } while(/*S*/a)
    107  do {
    108    /*S*/fn();
    109  } while(/*S*/"42" + /*B*/a());
    110 `);
    111 
    112 // While
    113 assertBreakpoints(`
    114  while(/*S*/a) {
    115    /*S*/fn();
    116  }
    117  while(/*S*/"42" + /*B*/a()) {
    118    /*S*/fn();
    119  }
    120 `);
    121 
    122 // ForExpr
    123 assertBreakpoints(`
    124  for (/*S*/b = 42; /*S*/c; /*S*/d) /*S*/fn();
    125  for (var b = /*S*/42; /*S*/c; /*S*/d) /*S*/fn();
    126  for (let b = /*S*/42; /*S*/c; /*S*/d) /*S*/fn();
    127  for (const b = /*S*/42; /*S*/c; /*S*/d) /*S*/fn();
    128  for (b in /*S*/d) /*S*/fn();
    129  for (var b in /*S*/d) /*S*/fn();
    130  for (let b in /*S*/d) /*S*/fn();
    131  for (const b in /*S*/d) /*S*/fn();
    132  for (b of /*S*/d) /*S*/fn();
    133  for (var b of /*S*/d) /*S*/fn();
    134  for (let b of /*S*/d) /*S*/fn();
    135  for (const b of /*S*/d) /*S*/fn();
    136 `);
    137 
    138 // SwitchStatement
    139 assertBreakpoints(`
    140  switch (/*S*/d) {
    141    case 42:
    142      /*S*/fn();
    143  }
    144 `);
    145 
    146 // ContinueStatement
    147 assertBreakpoints(`
    148  while (/*S*/a) {
    149    /*S*/continue;
    150  }
    151 `);
    152 
    153 // BreakStatement
    154 assertBreakpoints(`
    155  while (/*S*/a) {
    156    /*S*/break;
    157  }
    158 `);
    159 
    160 // ReturnStatement
    161 assertBreakpoints(`
    162  /*S*/return a + /*B*/b();
    163 `);
    164 
    165 // WithStatement
    166 assertBreakpoints(`
    167  with (/*S*/a) {
    168    /*S*/fn();
    169  }
    170 `);
    171 
    172 // ThrowStatement
    173 assertBreakpoints(`
    174  /*S*/throw /*B*/fn();
    175  /*S*/throw "42" + /*B*/fn();
    176 `);
    177 
    178 // DebuggerStatement
    179 assertBreakpoints(`
    180  /*S*/debugger;
    181  /*S*/debugger;
    182 `);
    183 
    184 // BlockStatent wrapper
    185 assertBreakpoints(`
    186  {
    187    /*S*/a();
    188  }
    189 `);
    190 
    191 // ClassDeclaration
    192 assertBreakpoints(`
    193  class Foo2 {}
    194  /*S*/class Foo extends ("" + o.a + /*B*/a() + /*B*/b()) { }
    195 `);
    196 
    197 // Misc examples
    198 assertBreakpoints(`
    199  /*S*/void /*B*/a();
    200 `);
    201 assertBreakpoints(`
    202  /*S*/a() + /*B*/b();
    203 `);
    204 assertBreakpoints(`
    205  for (
    206    var i = /*S*/0;
    207    /*S*/i < n;  // 4
    208    /*S*/++i
    209  ) {
    210    /*S*/console./*B*/log("omg");
    211  }
    212 `);
    213 assertBreakpoints(`
    214  function * gen(){
    215    var foo = (
    216      (/*S*/console./*B*/log('before', /*B*/a())),
    217      (yield console./*B*/log('mid', /*B*/b())),
    218      (console./*B*/log('after', /*B*/a()))
    219    );
    220    var foo2 = /*S*/a() + /*B*/b();
    221    /*S*/console./*B*/log(foo);
    222  /*B*/}
    223  var i = /*S*/0;
    224  for (var foo of /*S*/gen()) {
    225    /*S*/console./*B*/log(i++);
    226  }
    227 `);
    228 assertBreakpoints(`
    229  var fn = /*S*/() => {
    230    /*S*/console./*B*/log("fn");
    231    /*S*/return /*B*/new Proxy({ prop: 42 }, {
    232      deleteProperty() {
    233        /*S*/console./*B*/log("delete");
    234      /*B*/}
    235    });
    236  /*B*/};
    237 `);
    238 assertBreakpoints(`
    239  var fn = /*S*/async (arg) => {
    240    /*S*/console./*B*/log("fn");
    241  /*B*/};
    242 `);
    243 assertBreakpoints(`
    244  var fn = /*S*/arg => {
    245    /*S*/console./*B*/log("fn");
    246  /*B*/};
    247 `);
    248 assertBreakpoints(`
    249  var fn = /*S*/async arg => {
    250    /*S*/console./*B*/log("fn");
    251  /*B*/};
    252 `);
    253 assertBreakpoints(`
    254  var fn = /*S*/(arg) => /*S*/console./*B*/log("fn");
    255  var fn = /*S*/async (arg) => /*S*/console./*B*/log("fn");
    256  var fn = /*S*/arg => /*S*/console./*B*/log("fn");
    257  var fn = /*S*/async arg => /*S*/console./*B*/log("fn");
    258 `);
    259 assertBreakpoints(`
    260  if ((/*S*/delete /*B*/fn().prop) + /*B*/b()) {
    261    /*S*/console./*B*/log("foo");
    262  }
    263 `);
    264 assertBreakpoints(`
    265  for (var j = /*S*/0; (/*S*/o.a) < 3; (/*S*/j++, /*B*/a(), /*B*/b())) {
    266    /*S*/console./*B*/log(i);
    267  }
    268 `);
    269 assertBreakpoints(`
    270  function fn2(
    271    [a, b] = (/*B*/a(), /*B*/b())
    272  ) {
    273    /*S*/a();
    274    /*S*/b();
    275  /*B*/}
    276 
    277  ({ a, b } = (/*S*/a(), /*B*/b()));
    278 `);
    279 assertBreakpoints(`
    280  /*S*/o.a + "42" + /*B*/a() + /*B*/b();
    281 `);
    282 assertBreakpoints(`
    283  /*S*/a();
    284  /*S*/o./*B*/a(/*B*/b());
    285 `);
    286 assertBreakpoints(`
    287  (/*S*/{}[obj.a] = 42 + /*B*/a());
    288 `);
    289 assertBreakpoints(`
    290  var {
    291    foo = o.a
    292  } = /*S*/{};
    293 `);
    294 assertBreakpoints(`
    295  var ack = /*S*/[
    296    o.a,
    297    o.b,
    298    /*B*/a(),
    299    /*B*/a(),
    300    /*B*/a(),
    301    /*B*/a(),
    302    /*B*/a(),
    303    /*B*/a(),
    304    /*B*/a(),
    305  ];
    306 `);
    307 
    308 function assertBreakpoints(expected) {
    309  const input = expected.replace(/\/\*[BS]\*\//g, "");
    310 
    311  var global = newGlobal({ newCompartment: true });
    312  var dbg = Debugger(global);
    313  dbg.onDebuggerStatement = function(frame) {
    314    const fScript = frame.environment.parent.getVariable("f").script;
    315 
    316    let positions = [];
    317    (function recurse(script) {
    318      const bps = script.getPossibleBreakpoints();
    319      const offsets = script.getPossibleBreakpointOffsets();
    320 
    321      assertEq(offsets.length, bps.length);
    322      for (let i = 0; i < bps.length; i++) {
    323        assertEq(offsets[i], bps[i].offset);
    324      }
    325 
    326      positions = positions.concat(bps);
    327      script.getChildScripts().forEach(recurse);
    328    })(fScript);
    329 
    330    const result = annotateOffsets(input, positions);
    331    assertEq(result, expected + "/*B*/");
    332  };
    333 
    334  global.eval(`function f(){${input}} debugger;`);
    335 }
    336 
    337 function annotateOffsets(code, positions) {
    338  const offsetLookup = createOffsetLookup(code);
    339 
    340  positions = positions.slice();
    341  positions.sort((a, b) => {
    342    const lineDiff = a.lineNumber - b.lineNumber;
    343    return lineDiff === 0 ? a.columnNumber - b.columnNumber : lineDiff;
    344  });
    345  positions.reverse();
    346 
    347  let output = "";
    348  let last = code.length;
    349  for (const { lineNumber, columnNumber, isStepStart } of positions) {
    350    const offset = offsetLookup(lineNumber, columnNumber);
    351 
    352    output =
    353      "/*" +
    354      (isStepStart ? "S" : "B") +
    355      "*/" +
    356      code.slice(offset, last) +
    357      output;
    358    last = offset;
    359  }
    360  return code.slice(0, last) + output;
    361 }
    362 
    363 function createOffsetLookup(code) {
    364  const lines = code.split(/(\r?\n|\r|\u2028|\u2029)/g);
    365  const lineOffsets = [];
    366 
    367  let count = 0;
    368  for (const [i, str] of lines.entries()) {
    369    if (i % 2 === 0) {
    370      lineOffsets[i / 2] = count;
    371    }
    372    count += str.length;
    373  }
    374 
    375  return function(line, column) {
    376    // Lines from getAllColumnOffsets are 1-based.
    377    line = line - 1;
    378 
    379    if (!lineOffsets.hasOwnProperty(line)) {
    380      throw new Error("Unknown line " + line + " column " + column);
    381    }
    382    return lineOffsets[line] + column - 1;
    383  };
    384 }