tor-browser

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

Frame-onStep-12.js (4042B)


      1 // Check that stepping doesn't make it look like unreachable code is running.
      2 
      3 // Because our script source notes record only those bytecode offsets
      4 // at which source positions change, the default behavior in the
      5 // absence of a source note is to attribute a bytecode instruction to
      6 // the same source location as the preceding instruction. When control
      7 // flows from the preceding bytecode to the one we're emitting, that's
      8 // usually plausible. But successors in the bytecode stream are not
      9 // necessarily successors in the control flow graph. If the preceding
     10 // bytecode was a back edge of a loop, or the jump at the end of a
     11 // 'then' clause, its source position can be completely unrelated to
     12 // that of its successor.
     13 //
     14 // We try to avoid showing such nonsense offsets to the user by
     15 // requiring breakpoints and single-stepping to stop only at a line's
     16 // entry points, as reported by Debugger.Script.prototype.getLineOffsets;
     17 // and then ensuring that those entry points are all offsets mentioned
     18 // explicitly in the source notes, and hence deliberately attributed
     19 // to the given bytecode.
     20 //
     21 // This bit of JavaScript compiles to bytecode ending in a branch
     22 // instruction whose source position is the body of an unreachable
     23 // loop. The first instruction of the bytecode we emit following it
     24 // will inherit this nonsense position, if we have not explicitly
     25 // emitted a source note for said instruction.
     26 //
     27 // This test steps across such code and verifies that control never
     28 // appears to enter the unreachable loop.
     29 
     30 var bitOfCode = `debugger;                    // +0
     31                 if(false) {                  // +1
     32                   for(var b=0; b<0; b++) {   // +2
     33                      c = 2                   // +3
     34                    }                         // +4
     35                 }`;                          // +5
     36 
     37 var g = newGlobal({newCompartment: true});
     38 var dbg = Debugger(g);
     39 
     40 g.eval("function nothing() { }\n");
     41 
     42 var log = '';
     43 dbg.onDebuggerStatement = function(frame) {
     44  let debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber;
     45  frame.onStep = function() {
     46    let foundLine = this.script.getOffsetLocation(this.offset).lineNumber;
     47    if (this.script.getLineOffsets(foundLine).indexOf(this.offset) >= 0) {
     48      log += (foundLine - debugLine).toString(16);
     49    }
     50  };
     51 };
     52 
     53 function testOne(name, body, expected) {
     54  print(name);
     55  log = '';
     56  g.eval(`function ${name} () { ${body} }`);
     57  g.eval(`${name}();`);
     58  assertEq(log, expected);
     59 }
     60 
     61 
     62 
     63 // Test the instructions at the end of a "try".
     64 testOne("testTryFinally",
     65        `try {
     66           ${bitOfCode}
     67         } finally {            // +6
     68         }                      // +7
     69         nothing();             // +8
     70        `, "1689");
     71 
     72 // The same but without a finally clause.
     73 testOne("testTryCatch",
     74        `try {
     75           ${bitOfCode}
     76         } catch (e) {          // +6
     77         }                      // +7
     78         nothing();             // +8
     79        `, "189");
     80 
     81 // Test the instructions at the end of a "catch".
     82 testOne("testCatchFinally",
     83        `try {
     84           throw new TypeError();
     85         } catch (e) {
     86           ${bitOfCode}
     87         } finally {            // +6
     88         }                      // +7
     89         nothing();             // +8
     90        `, "1689");
     91 
     92 // Test the instruction at the end of a "finally" clause.
     93 testOne("testFinally",
     94        `try {
     95         } finally {
     96           ${bitOfCode}
     97         }                      // +6
     98         nothing();             // +7
     99        `, "178");
    100 
    101 // Test the instruction at the end of a "then" clause.
    102 testOne("testThen",
    103        `if (1 === 1) {
    104           ${bitOfCode}
    105         } else {               // +6
    106         }                      // +7
    107         nothing();             // +8
    108        `, "189");
    109 
    110 // Test the instructions leaving a switch block.
    111 testOne("testSwitch",
    112        `var x = 5;
    113         switch (x) {
    114           case 5:
    115             ${bitOfCode}
    116         }                      // +6
    117         nothing();             // +7
    118        `, "178");