tor-browser

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

Debugger-findScripts-delazify.js (13567B)


      1 // |jit-test| skip-if: isLcovEnabled()
      2 
      3 // findScript should try to avoid delazifying unnecessarily.
      4 
      5 function newTestcase(code) {
      6  var g = newGlobal({newCompartment: true});
      7  var dbg = new Debugger();
      8  var gw = dbg.addDebuggee(g);
      9 
     10  var lines = code.split('\n');
     11  // Returns the line number of the line with "<= line".
     12  // + 1 for 1-origin.
     13  var line = lines.findIndex(x => x.includes("<= line")) + 1;
     14 
     15  g.eval(code);
     16 
     17  // Functions are relazified, and the top-level script is dropped.
     18  relazifyFunctions();
     19 
     20  return [dbg, g, line];
     21 }
     22 
     23 var url = thisFilename();
     24 var dbg, g, line, scripts;
     25 
     26 // If the specified line is inside the function body, only the function should
     27 // be delazified.
     28 [dbg, g, line] = newTestcase(`
     29 function f1() {
     30 }
     31 function f2() {
     32 // <= line
     33 }
     34 function f3() {
     35 }
     36 `);
     37 
     38 assertEq(g.eval(`isLazyFunction(f1)`), true);
     39 assertEq(g.eval(`isLazyFunction(f2)`), true);
     40 assertEq(g.eval(`isLazyFunction(f3)`), true);
     41 
     42 scripts = dbg.findScripts({url, line});
     43 assertEq(scripts.length, 1);
     44 assertEq(scripts.map(s => s.displayName).sort().join(","), "f2");
     45 
     46 assertEq(g.eval(`isLazyFunction(f1)`), true);
     47 assertEq(g.eval(`isLazyFunction(f2)`), false);
     48 assertEq(g.eval(`isLazyFunction(f3)`), true);
     49 
     50 // If the functions starts at the specified line, the function shouldn't be
     51 // the first function to delazify.
     52 [dbg, g, line] = newTestcase(`
     53 function f1() {
     54 }
     55 function f2() {
     56 }
     57 function f3() { // <= line
     58 }
     59 function f4() {
     60 }
     61 `);
     62 
     63 assertEq(g.eval(`isLazyFunction(f1)`), true);
     64 assertEq(g.eval(`isLazyFunction(f2)`), true);
     65 assertEq(g.eval(`isLazyFunction(f3)`), true);
     66 assertEq(g.eval(`isLazyFunction(f4)`), true);
     67 
     68 scripts = dbg.findScripts({url, line});
     69 assertEq(scripts.length, 1);
     70 assertEq(scripts.map(s => s.displayName).sort().join(","), "f3");
     71 
     72 assertEq(g.eval(`isLazyFunction(f1)`), true);
     73 // f2 is delazified because f3 cannot be the first function to delazify.
     74 assertEq(g.eval(`isLazyFunction(f2)`), false);
     75 assertEq(g.eval(`isLazyFunction(f3)`), false);
     76 assertEq(g.eval(`isLazyFunction(f4)`), true);
     77 
     78 // Multiple functions in the specified line, and one of them starts before
     79 // the specified line.
     80 // All functions should be returned, and others shouldn't be delazified.
     81 [dbg, g, line] = newTestcase(`
     82 function f1() {}
     83 function f2() {
     84 } function f3() {} function f4() {} function f5() { // <= line
     85 }
     86 function f6() {}
     87 `);
     88 
     89 assertEq(g.eval(`isLazyFunction(f1)`), true);
     90 assertEq(g.eval(`isLazyFunction(f2)`), true);
     91 assertEq(g.eval(`isLazyFunction(f3)`), true);
     92 assertEq(g.eval(`isLazyFunction(f4)`), true);
     93 assertEq(g.eval(`isLazyFunction(f5)`), true);
     94 assertEq(g.eval(`isLazyFunction(f6)`), true);
     95 
     96 scripts = dbg.findScripts({url, line});
     97 assertEq(scripts.length, 4);
     98 assertEq(scripts.map(s => s.displayName).sort().join(","), "f2,f3,f4,f5");
     99 
    100 assertEq(g.eval(`isLazyFunction(f1)`), true);
    101 assertEq(g.eval(`isLazyFunction(f2)`), false);
    102 assertEq(g.eval(`isLazyFunction(f3)`), false);
    103 assertEq(g.eval(`isLazyFunction(f4)`), false);
    104 assertEq(g.eval(`isLazyFunction(f5)`), false);
    105 assertEq(g.eval(`isLazyFunction(f6)`), true);
    106 
    107 // The same rule should apply to inner functions.
    108 [dbg, g, line] = newTestcase(`
    109 function f1() {}
    110 function f2() {
    111  function g1() {
    112  }
    113  function g2() {
    114    function h1() {}
    115    function h2() {
    116    } function h3() {} function h4() {} function h5() { // <= line
    117    }
    118    function h6() {}
    119 
    120    return [h1, h2, h3, h4, h5, h6];
    121  }
    122  function g3() {
    123  }
    124 
    125  return [g1, g2, g3];
    126 }
    127 function f3() {}
    128 `);
    129 
    130 assertEq(g.eval(`isLazyFunction(f1)`), true);
    131 assertEq(g.eval(`isLazyFunction(f2)`), true);
    132 assertEq(g.eval(`isLazyFunction(f3)`), true);
    133 
    134 scripts = dbg.findScripts({url, line});
    135 assertEq(scripts.length, 6);
    136 assertEq(scripts.map(s => s.displayName).sort().join(","), "f2,g2,h2,h3,h4,h5");
    137 
    138 assertEq(g.eval(`isLazyFunction(f1)`), true);
    139 assertEq(g.eval(`isLazyFunction(f2)`), false);
    140 assertEq(g.eval(`isLazyFunction(f3)`), true);
    141 g.eval(`var [g1, g2, g3] = f2();`);
    142 assertEq(g.eval(`isLazyFunction(g1)`), true);
    143 assertEq(g.eval(`isLazyFunction(g2)`), false);
    144 assertEq(g.eval(`isLazyFunction(g3)`), true);
    145 g.eval(`var [h1, h2, h3, h4, h5, h6] = g2();`);
    146 assertEq(g.eval(`isLazyFunction(h1)`), true);
    147 assertEq(g.eval(`isLazyFunction(h2)`), false);
    148 assertEq(g.eval(`isLazyFunction(h3)`), false);
    149 assertEq(g.eval(`isLazyFunction(h4)`), false);
    150 assertEq(g.eval(`isLazyFunction(h5)`), false);
    151 assertEq(g.eval(`isLazyFunction(h6)`), true);
    152 
    153 // The same rule should apply to functions inside parameter expression.
    154 [dbg, g, line] = newTestcase(`
    155 function f1(
    156  a = function g1() {},
    157  b = function g2() {
    158  }, c = function g3() {}, d = function g4() { // <= line
    159  },
    160  e = function g5() {},
    161 ) {
    162  return [a, b, c, d, e];
    163 }
    164 `);
    165 
    166 assertEq(g.eval(`isLazyFunction(f1)`), true);
    167 
    168 scripts = dbg.findScripts({url, line});
    169 assertEq(scripts.length, 4);
    170 assertEq(scripts.map(s => s.displayName).sort().join(","), "f1,g2,g3,g4");
    171 
    172 assertEq(g.eval(`isLazyFunction(f1)`), false);
    173 
    174 g.eval(`var [g1, g2, g3, g4, g5] = f1();`);
    175 
    176 assertEq(g.eval(`isLazyFunction(g1)`), true);
    177 assertEq(g.eval(`isLazyFunction(g2)`), false);
    178 assertEq(g.eval(`isLazyFunction(g3)`), false);
    179 assertEq(g.eval(`isLazyFunction(g4)`), false);
    180 assertEq(g.eval(`isLazyFunction(g5)`), true);
    181 
    182 // The same should apply to function inside method with computed property.
    183 [dbg, g, line] = newTestcase(`
    184 var f1, f2, f3;
    185 var O = {
    186  [(f1 = () => 0, "m1")]() {
    187  },
    188  [(f2 = () => 0, "m2")](p2) { // <= line
    189  },
    190  [(f3 = () => 0, "m3")]() {
    191  },
    192 }
    193 `);
    194 
    195 assertEq(g.eval(`isLazyFunction(f1)`), true);
    196 assertEq(g.eval(`isLazyFunction(O.m2)`), true);
    197 assertEq(g.eval(`isLazyFunction(f2)`), true);
    198 assertEq(g.eval(`isLazyFunction(O.m1)`), true);
    199 assertEq(g.eval(`isLazyFunction(f3)`), true);
    200 assertEq(g.eval(`isLazyFunction(O.m3)`), true);
    201 
    202 scripts = dbg.findScripts({url, line});
    203 assertEq(scripts.length, 2);
    204 assertEq(scripts.map(s => s.displayName).sort().join(","), "f2,");
    205 // Use parameterNames because displayName isn't set for computed property.
    206 assertEq(scripts.map(s => `${s.displayName}(${s.parameterNames.join(",")})`)
    207         .sort().join(","), "f2(),undefined(p2)");
    208 
    209 assertEq(g.eval(`isLazyFunction(f1)`), true);
    210 // m1 is delazified because f2 cannot be the first function to delazify.
    211 assertEq(g.eval(`isLazyFunction(O.m1)`), false);
    212 assertEq(g.eval(`isLazyFunction(f2)`), false);
    213 assertEq(g.eval(`isLazyFunction(O.m2)`), false);
    214 assertEq(g.eval(`isLazyFunction(f3)`), true);
    215 assertEq(g.eval(`isLazyFunction(O.m3)`), true);
    216 
    217 [dbg, g, line] = newTestcase(`
    218 var f1, f2, f3;
    219 var O = {
    220  [(f1 = () => 0, "m1")]() {
    221  },
    222  [(f2 = () => 0 +
    223  1, "m2")](p2) { // <= line
    224  },
    225  [(f3 = () => 0, "m3")]() {
    226  },
    227 }
    228 `);
    229 
    230 assertEq(g.eval(`isLazyFunction(f1)`), true);
    231 assertEq(g.eval(`isLazyFunction(O.m1)`), true);
    232 assertEq(g.eval(`isLazyFunction(f2)`), true);
    233 assertEq(g.eval(`isLazyFunction(O.m2)`), true);
    234 assertEq(g.eval(`isLazyFunction(f3)`), true);
    235 assertEq(g.eval(`isLazyFunction(O.m3)`), true);
    236 
    237 scripts = dbg.findScripts({url, line});
    238 assertEq(scripts.length, 2);
    239 assertEq(scripts.map(s => `${s.displayName}(${s.parameterNames.join(",")})`)
    240         .sort().join(","), "f2(),undefined(p2)");
    241 
    242 assertEq(g.eval(`isLazyFunction(f1)`), true);
    243 assertEq(g.eval(`isLazyFunction(O.m1)`), true);
    244 assertEq(g.eval(`isLazyFunction(f2)`), false);
    245 assertEq(g.eval(`isLazyFunction(O.m2)`), false);
    246 assertEq(g.eval(`isLazyFunction(f3)`), true);
    247 assertEq(g.eval(`isLazyFunction(O.m3)`), true);
    248 
    249 // Class constructor shouldn't be delazified even if methods match.
    250 [dbg, g, line] = newTestcase(`
    251 // Use variable to access across eval.
    252 var C = class {
    253  constructor() {
    254  }
    255  m1() {}
    256  m2() {
    257  } m3() {} m4() { // <= line
    258  }
    259  m5() {}
    260 }
    261 `);
    262 
    263 assertEq(g.eval(`isLazyFunction(C)`), true);
    264 assertEq(g.eval(`isLazyFunction(C.prototype.m1)`), true);
    265 assertEq(g.eval(`isLazyFunction(C.prototype.m2)`), true);
    266 assertEq(g.eval(`isLazyFunction(C.prototype.m3)`), true);
    267 assertEq(g.eval(`isLazyFunction(C.prototype.m4)`), true);
    268 assertEq(g.eval(`isLazyFunction(C.prototype.m5)`), true);
    269 
    270 scripts = dbg.findScripts({url, line});
    271 assertEq(scripts.length, 3);
    272 assertEq(scripts.map(s => s.displayName).sort().join(","), "m2,m3,m4");
    273 
    274 assertEq(g.eval(`isLazyFunction(C)`), true);
    275 assertEq(g.eval(`isLazyFunction(C.prototype.m1)`), true);
    276 assertEq(g.eval(`isLazyFunction(C.prototype.m2)`), false);
    277 assertEq(g.eval(`isLazyFunction(C.prototype.m3)`), false);
    278 assertEq(g.eval(`isLazyFunction(C.prototype.m4)`), false);
    279 assertEq(g.eval(`isLazyFunction(C.prototype.m5)`), true);
    280 
    281 // If the line is placed before sourceStart, the function shouldn't match,
    282 // and the function shouldn't be delazified.
    283 [dbg, g, line] = newTestcase(`
    284 function f1() {
    285 }
    286 function f2() {
    287 }
    288 function
    289 // <= line
    290 f3() {
    291 }
    292 function f4() {
    293 }
    294 `);
    295 
    296 assertEq(g.eval(`isLazyFunction(f1)`), true);
    297 assertEq(g.eval(`isLazyFunction(f2)`), true);
    298 assertEq(g.eval(`isLazyFunction(f3)`), true);
    299 assertEq(g.eval(`isLazyFunction(f4)`), true);
    300 
    301 scripts = dbg.findScripts({url, line});
    302 assertEq(scripts.length, 0);
    303 
    304 assertEq(g.eval(`isLazyFunction(f1)`), true);
    305 // f2 is delazified because it's the first function before the specified line.
    306 assertEq(g.eval(`isLazyFunction(f2)`), false);
    307 assertEq(g.eval(`isLazyFunction(f3)`), true);
    308 assertEq(g.eval(`isLazyFunction(f4)`), true);
    309 
    310 [dbg, g, line] = newTestcase(`
    311 function f1() {
    312 }
    313 function f2() {
    314 }
    315 function f3
    316 // <= line
    317 () {
    318 }
    319 function f4() {
    320 }
    321 `);
    322 
    323 assertEq(g.eval(`isLazyFunction(f1)`), true);
    324 assertEq(g.eval(`isLazyFunction(f2)`), true);
    325 assertEq(g.eval(`isLazyFunction(f3)`), true);
    326 assertEq(g.eval(`isLazyFunction(f4)`), true);
    327 
    328 scripts = dbg.findScripts({url, line});
    329 assertEq(scripts.length, 0);
    330 
    331 assertEq(g.eval(`isLazyFunction(f1)`), true);
    332 // f2 is delazified because it's the first function before the specified line.
    333 assertEq(g.eval(`isLazyFunction(f2)`), false);
    334 assertEq(g.eval(`isLazyFunction(f3)`), true);
    335 assertEq(g.eval(`isLazyFunction(f4)`), true);
    336 
    337 [dbg, g, line] = newTestcase(`
    338 function f1() {
    339 }
    340 function f2() {
    341 }
    342 function f3
    343 ( // <= line
    344 ) {
    345 }
    346 function f4() {
    347 }
    348 `);
    349 
    350 assertEq(g.eval(`isLazyFunction(f1)`), true);
    351 assertEq(g.eval(`isLazyFunction(f2)`), true);
    352 assertEq(g.eval(`isLazyFunction(f3)`), true);
    353 assertEq(g.eval(`isLazyFunction(f4)`), true);
    354 
    355 scripts = dbg.findScripts({url, line});
    356 assertEq(scripts.length, 1);
    357 assertEq(scripts.map(s => s.displayName).sort().join(","), "f3");
    358 
    359 assertEq(g.eval(`isLazyFunction(f1)`), true);
    360 // f2 is delazified because it's the first function _before_ the specified line.
    361 assertEq(g.eval(`isLazyFunction(f2)`), false);
    362 assertEq(g.eval(`isLazyFunction(f3)`), false);
    363 assertEq(g.eval(`isLazyFunction(f4)`), true);
    364 
    365 [dbg, g, line] = newTestcase(`
    366 function f1() {
    367 }
    368 function f2() {
    369 }
    370 function f3
    371 (
    372 // <= line
    373 ) {
    374 }
    375 function f4() {
    376 }
    377 `);
    378 
    379 assertEq(g.eval(`isLazyFunction(f1)`), true);
    380 assertEq(g.eval(`isLazyFunction(f2)`), true);
    381 assertEq(g.eval(`isLazyFunction(f3)`), true);
    382 assertEq(g.eval(`isLazyFunction(f4)`), true);
    383 
    384 scripts = dbg.findScripts({url, line});
    385 assertEq(scripts.length, 1);
    386 assertEq(scripts.map(s => s.displayName).sort().join(","), "f3");
    387 
    388 assertEq(g.eval(`isLazyFunction(f1)`), true);
    389 assertEq(g.eval(`isLazyFunction(f2)`), true);
    390 assertEq(g.eval(`isLazyFunction(f3)`), false);
    391 assertEq(g.eval(`isLazyFunction(f4)`), true);
    392 
    393 // If the specified line is the next line after the function ends,
    394 // nothing should match, but the function should be delazified.
    395 [dbg, g, line] = newTestcase(`
    396 function f1() {
    397 }
    398 function f2() {
    399 }
    400 // <= line
    401 function f3() {
    402 }
    403 `);
    404 
    405 assertEq(g.eval(`isLazyFunction(f1)`), true);
    406 assertEq(g.eval(`isLazyFunction(f2)`), true);
    407 assertEq(g.eval(`isLazyFunction(f3)`), true);
    408 
    409 scripts = dbg.findScripts({url, line});
    410 assertEq(scripts.length, 0);
    411 
    412 assertEq(g.eval(`isLazyFunction(f1)`), true);
    413 assertEq(g.eval(`isLazyFunction(f2)`), false);
    414 assertEq(g.eval(`isLazyFunction(f3)`), true);
    415 
    416 // The matching non-lazy script should prevent the previous function's
    417 // delazification.
    418 [dbg, g, line] = newTestcase(`
    419 function f1() {
    420 }
    421 function f2() {
    422 }
    423 function f3() {
    424  // <= line
    425 }
    426 function f4() {
    427 }
    428 `);
    429 
    430 assertEq(g.eval(`isLazyFunction(f1)`), true);
    431 assertEq(g.eval(`isLazyFunction(f2)`), true);
    432 assertEq(g.eval(`isLazyFunction(f3)`), true);
    433 assertEq(g.eval(`isLazyFunction(f4)`), true);
    434 
    435 // Delazify f3.
    436 g.eval(`f3()`);
    437 
    438 assertEq(g.eval(`isLazyFunction(f1)`), true);
    439 assertEq(g.eval(`isLazyFunction(f2)`), true);
    440 assertEq(g.eval(`isLazyFunction(f3)`), false);
    441 assertEq(g.eval(`isLazyFunction(f4)`), true);
    442 
    443 scripts = dbg.findScripts({url, line});
    444 assertEq(scripts.length, 1);
    445 assertEq(scripts.map(s => s.displayName).sort().join(","), "f3");
    446 
    447 assertEq(g.eval(`isLazyFunction(f1)`), true);
    448 assertEq(g.eval(`isLazyFunction(f2)`), true);
    449 assertEq(g.eval(`isLazyFunction(f3)`), false);
    450 assertEq(g.eval(`isLazyFunction(f4)`), true);
    451 
    452 // The non-matching non-lazy script should prevent the previous function's
    453 // delazification.
    454 [dbg, g, line] = newTestcase(`
    455 function f1() {
    456 }
    457 function f2() {
    458 }
    459 function f3() {
    460 }
    461 // <= line
    462 function f4() {
    463 }
    464 `);
    465 
    466 assertEq(g.eval(`isLazyFunction(f1)`), true);
    467 assertEq(g.eval(`isLazyFunction(f2)`), true);
    468 assertEq(g.eval(`isLazyFunction(f3)`), true);
    469 assertEq(g.eval(`isLazyFunction(f4)`), true);
    470 
    471 // Delazify f3.
    472 g.eval(`f3()`);
    473 
    474 assertEq(g.eval(`isLazyFunction(f1)`), true);
    475 assertEq(g.eval(`isLazyFunction(f2)`), true);
    476 assertEq(g.eval(`isLazyFunction(f3)`), false);
    477 assertEq(g.eval(`isLazyFunction(f4)`), true);
    478 
    479 scripts = dbg.findScripts({url, line});
    480 assertEq(scripts.length, 0);
    481 
    482 assertEq(g.eval(`isLazyFunction(f1)`), true);
    483 assertEq(g.eval(`isLazyFunction(f2)`), true);
    484 assertEq(g.eval(`isLazyFunction(f3)`), false);
    485 assertEq(g.eval(`isLazyFunction(f4)`), true);