Home | History | Annotate | Download | only in es6
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Flags: --allow-natives-syntax --harmony-tailcalls
      6 // Flags: --harmony-do-expressions
      7 
      8 "use strict";
      9 
     10 Error.prepareStackTrace = (error,stack) => {
     11   error.strace = stack;
     12   return error.message + "\n    at " + stack.join("\n    at ");
     13 }
     14 
     15 
     16 function CheckStackTrace(expected) {
     17   var e = new Error();
     18   e.stack;  // prepare stack trace
     19   var stack = e.strace;
     20   assertEquals("CheckStackTrace", stack[0].getFunctionName());
     21   for (var i = 0; i < expected.length; i++) {
     22     assertEquals(expected[i].name, stack[i + 1].getFunctionName());
     23   }
     24 }
     25 %NeverOptimizeFunction(CheckStackTrace);
     26 
     27 
     28 function f(expected_call_stack, a, b) {
     29   CheckStackTrace(expected_call_stack);
     30   return a;
     31 }
     32 
     33 function f_153(expected_call_stack, a) {
     34   CheckStackTrace(expected_call_stack);
     35   return 153;
     36 }
     37 
     38 
     39 // Tail call when caller does not have an arguments adaptor frame.
     40 (function() {
     41   // Caller and callee have same number of arguments.
     42   function f1(a) {
     43     CheckStackTrace([f1, test]);
     44     return 10 + a;
     45   }
     46   function g1(a) { return f1(2); }
     47 
     48   // Caller has more arguments than callee.
     49   function f2(a) {
     50     CheckStackTrace([f2, test]);
     51     return 10 + a;
     52   }
     53   function g2(a, b, c) { return f2(2); }
     54 
     55   // Caller has less arguments than callee.
     56   function f3(a, b, c) {
     57     CheckStackTrace([f3, test]);
     58     return 10 + a + b + c;
     59   }
     60   function g3(a) { return f3(2, 3, 4); }
     61 
     62   // Callee has arguments adaptor frame.
     63   function f4(a, b, c) {
     64     CheckStackTrace([f4, test]);
     65     return 10 + a;
     66   }
     67   function g4(a) { return f4(2); }
     68 
     69   function test() {
     70     assertEquals(12, g1(1));
     71     assertEquals(12, g2(1, 2, 3));
     72     assertEquals(19, g3(1));
     73     assertEquals(12, g4(1));
     74   }
     75   test();
     76   test();
     77   %OptimizeFunctionOnNextCall(test);
     78   test();
     79 })();
     80 
     81 
     82 // Tail call when caller has an arguments adaptor frame.
     83 (function() {
     84   // Caller and callee have same number of arguments.
     85   function f1(a) {
     86     CheckStackTrace([f1, test]);
     87     return 10 + a;
     88   }
     89   function g1(a) { return f1(2); }
     90 
     91   // Caller has more arguments than callee.
     92   function f2(a) {
     93     CheckStackTrace([f2, test]);
     94     return 10 + a;
     95   }
     96   function g2(a, b, c) { return f2(2); }
     97 
     98   // Caller has less arguments than callee.
     99   function f3(a, b, c) {
    100     CheckStackTrace([f3, test]);
    101     return 10 + a + b + c;
    102   }
    103   function g3(a) { return f3(2, 3, 4); }
    104 
    105   // Callee has arguments adaptor frame.
    106   function f4(a, b, c) {
    107     CheckStackTrace([f4, test]);
    108     return 10 + a;
    109   }
    110   function g4(a) { return f4(2); }
    111 
    112   function test() {
    113     assertEquals(12, g1());
    114     assertEquals(12, g2());
    115     assertEquals(19, g3());
    116     assertEquals(12, g4());
    117   }
    118   test();
    119   test();
    120   %OptimizeFunctionOnNextCall(test);
    121   test();
    122 })();
    123 
    124 
    125 // Tail call bound function when caller does not have an arguments
    126 // adaptor frame.
    127 (function() {
    128   // Caller and callee have same number of arguments.
    129   function f1(a) {
    130     assertEquals(153, this.a);
    131     CheckStackTrace([f1, test]);
    132     return 10 + a;
    133   }
    134   var b1 = f1.bind({a: 153});
    135   function g1(a) { return b1(2); }
    136 
    137   // Caller has more arguments than callee.
    138   function f2(a) {
    139     assertEquals(153, this.a);
    140     CheckStackTrace([f2, test]);
    141     return 10 + a;
    142   }
    143   var b2 = f2.bind({a: 153});
    144   function g2(a, b, c) { return b2(2); }
    145 
    146   // Caller has less arguments than callee.
    147   function f3(a, b, c) {
    148     assertEquals(153, this.a);
    149     CheckStackTrace([f3, test]);
    150     return 10 + a + b + c;
    151   }
    152   var b3 = f3.bind({a: 153});
    153   function g3(a) { return b3(2, 3, 4); }
    154 
    155   // Callee has arguments adaptor frame.
    156   function f4(a, b, c) {
    157     assertEquals(153, this.a);
    158     CheckStackTrace([f4, test]);
    159     return 10 + a;
    160   }
    161   var b4 = f4.bind({a: 153});
    162   function g4(a) { return b4(2); }
    163 
    164   function test() {
    165     assertEquals(12, g1(1));
    166     assertEquals(12, g2(1, 2, 3));
    167     assertEquals(19, g3(1));
    168     assertEquals(12, g4(1));
    169   }
    170   test();
    171   test();
    172   %OptimizeFunctionOnNextCall(test);
    173   test();
    174 })();
    175 
    176 
    177 // Tail call bound function when caller has an arguments adaptor frame.
    178 (function() {
    179   // Caller and callee have same number of arguments.
    180   function f1(a) {
    181     assertEquals(153, this.a);
    182     CheckStackTrace([f1, test]);
    183     return 10 + a;
    184   }
    185   var b1 = f1.bind({a: 153});
    186   function g1(a) { return b1(2); }
    187 
    188   // Caller has more arguments than callee.
    189   function f2(a) {
    190     assertEquals(153, this.a);
    191     CheckStackTrace([f2, test]);
    192     return 10 + a;
    193   }
    194   var b2 = f2.bind({a: 153});
    195   function g2(a, b, c) { return b2(2); }
    196 
    197   // Caller has less arguments than callee.
    198   function f3(a, b, c) {
    199     assertEquals(153, this.a);
    200     CheckStackTrace([f3, test]);
    201     return 10 + a + b + c;
    202   }
    203   var b3 = f3.bind({a: 153});
    204   function g3(a) { return b3(2, 3, 4); }
    205 
    206   // Callee has arguments adaptor frame.
    207   function f4(a, b, c) {
    208     assertEquals(153, this.a);
    209     CheckStackTrace([f4, test]);
    210     return 10 + a;
    211   }
    212   var b4 = f4.bind({a: 153});
    213   function g4(a) { return b4(2); }
    214 
    215   function test() {
    216     assertEquals(12, g1());
    217     assertEquals(12, g2());
    218     assertEquals(19, g3());
    219     assertEquals(12, g4());
    220   }
    221   test();
    222   test();
    223   %OptimizeFunctionOnNextCall(test);
    224   test();
    225 })();
    226 
    227 
    228 // Tail calling from getter.
    229 (function() {
    230   function g(v) {
    231     CheckStackTrace([g, test]);
    232     %DeoptimizeFunction(test);
    233     return 153;
    234   }
    235   %NeverOptimizeFunction(g);
    236 
    237   function f(v) {
    238     return g();
    239   }
    240   %SetForceInlineFlag(f);
    241 
    242   function test() {
    243     var o = {};
    244     o.__defineGetter__('p', f);
    245     assertEquals(153, o.p);
    246   }
    247 
    248   test();
    249   test();
    250   %OptimizeFunctionOnNextCall(test);
    251   test();
    252 })();
    253 
    254 
    255 // Tail calling from setter.
    256 (function() {
    257   function g() {
    258     CheckStackTrace([g, test]);
    259     %DeoptimizeFunction(test);
    260     return 153;
    261   }
    262   %NeverOptimizeFunction(g);
    263 
    264   function f(v) {
    265     return g();
    266   }
    267   %SetForceInlineFlag(f);
    268 
    269   function test() {
    270     var o = {};
    271     o.__defineSetter__('q', f);
    272     assertEquals(1, o.q = 1);
    273   }
    274 
    275   test();
    276   test();
    277   %OptimizeFunctionOnNextCall(test);
    278   test();
    279 })();
    280 
    281 
    282 // Tail calling from constructor.
    283 (function() {
    284   function g(context) {
    285     CheckStackTrace([g, test]);
    286     %DeoptimizeFunction(test);
    287     return {x: 153};
    288   }
    289   %NeverOptimizeFunction(g);
    290 
    291   function A() {
    292     this.x = 42;
    293     return g();
    294   }
    295 
    296   function test() {
    297     var o = new A();
    298     %DebugPrint(o);
    299     assertEquals(153, o.x);
    300   }
    301 
    302   test();
    303   test();
    304   %OptimizeFunctionOnNextCall(test);
    305   test();
    306 })();
    307 
    308 
    309 // Tail calling via various expressions.
    310 (function() {
    311   function g1(a) {
    312     return f([f, g1, test], false) || f([f, test], true);
    313   }
    314 
    315   function g2(a) {
    316     return f([f, g2, test], true) && f([f, test], true);
    317   }
    318 
    319   function g3(a) {
    320     return f([f, g3, test], 13), f([f, test], 153);
    321   }
    322 
    323   function g4(a) {
    324     return f([f, g4, test], false) ||
    325         (f([f, g4, test], true) && f([f, test], true));
    326   }
    327 
    328   function g5(a) {
    329     return f([f, g5, test], true) &&
    330         (f([f, g5, test], false) || f([f, test], true));
    331   }
    332 
    333   function g6(a) {
    334     return f([f, g6, test], 13), f([f, g6, test], 42),
    335         f([f, test], 153);
    336   }
    337 
    338   function g7(a) {
    339     return f([f, g7, test], false) ||
    340         (f([f, g7, test], false) ? f([f, test], true)
    341              : f([f, test], true));
    342   }
    343 
    344   function g8(a) {
    345     return f([f, g8, test], false) || f([f, g8, test], true) &&
    346         f([f, test], true);
    347   }
    348 
    349   function g9(a) {
    350     return f([f, g9, test], true) && f([f, g9, test], false) ||
    351         f([f, test], true);
    352   }
    353 
    354   function g10(a) {
    355     return f([f, g10, test], true) && f([f, g10, test], false) ||
    356         f([f, g10, test], true) ?
    357             f([f, g10, test], true) && f([f, g10, test], false) ||
    358                 f([f, test], true) :
    359             f([f, g10, test], true) && f([f, g10, test], false) ||
    360                 f([f, test], true);
    361   }
    362 
    363   function test() {
    364     assertEquals(true, g1());
    365     assertEquals(true, g2());
    366     assertEquals(153, g3());
    367     assertEquals(true, g4());
    368     assertEquals(true, g5());
    369     assertEquals(153, g6());
    370     assertEquals(true, g7());
    371     assertEquals(true, g8());
    372     assertEquals(true, g9());
    373     assertEquals(true, g10());
    374   }
    375   test();
    376   test();
    377   %OptimizeFunctionOnNextCall(test);
    378   test();
    379 })();
    380 
    381 
    382 // Tail calling from various statements.
    383 (function() {
    384   function g1() {
    385     for (var v in {a:0}) {
    386       return f_153([f_153, g1, test]);
    387     }
    388   }
    389 
    390   function g2() {
    391     for (var v of [1, 2, 3]) {
    392       return f_153([f_153, g2, test]);
    393     }
    394   }
    395 
    396   function g3() {
    397     for (var i = 0; i < 10; i++) {
    398       return f_153([f_153, test]);
    399     }
    400   }
    401 
    402   function g4() {
    403     while (true) {
    404       return f_153([f_153, test]);
    405     }
    406   }
    407 
    408   function g5() {
    409     do {
    410       return f_153([f_153, test]);
    411     } while (true);
    412   }
    413 
    414   function test() {
    415     assertEquals(153, g1());
    416     assertEquals(153, g2());
    417     assertEquals(153, g3());
    418     assertEquals(153, g4());
    419     assertEquals(153, g5());
    420   }
    421   test();
    422   test();
    423   %OptimizeFunctionOnNextCall(test);
    424   test();
    425 })();
    426 
    427 
    428 // Test tail calls from try-catch constructs.
    429 (function() {
    430   function tc1(a) {
    431     try {
    432       f_153([f_153, tc1, test]);
    433       return f_153([f_153, tc1, test]);
    434     } catch(e) {
    435       f_153([f_153, tc1, test]);
    436     }
    437   }
    438 
    439   function tc2(a) {
    440     try {
    441       f_153([f_153, tc2, test]);
    442       throw new Error("boom");
    443     } catch(e) {
    444       f_153([f_153, tc2, test]);
    445       return f_153([f_153, test]);
    446     }
    447   }
    448 
    449   function tc3(a) {
    450     try {
    451       f_153([f_153, tc3, test]);
    452       throw new Error("boom");
    453     } catch(e) {
    454       f_153([f_153, tc3, test]);
    455     }
    456     f_153([f_153, tc3, test]);
    457     return f_153([f_153, test]);
    458   }
    459 
    460   function test() {
    461     assertEquals(153, tc1());
    462     assertEquals(153, tc2());
    463     assertEquals(153, tc3());
    464   }
    465   test();
    466   test();
    467   %OptimizeFunctionOnNextCall(test);
    468   test();
    469 })();
    470 
    471 
    472 // Test tail calls from try-finally constructs.
    473 (function() {
    474   function tf1(a) {
    475     try {
    476       f_153([f_153, tf1, test]);
    477       return f_153([f_153, tf1, test]);
    478     } finally {
    479       f_153([f_153, tf1, test]);
    480     }
    481   }
    482 
    483   function tf2(a) {
    484     try {
    485       f_153([f_153, tf2, test]);
    486       throw new Error("boom");
    487     } finally {
    488       f_153([f_153, tf2, test]);
    489       return f_153([f_153, test]);
    490     }
    491   }
    492 
    493   function tf3(a) {
    494     try {
    495       f_153([f_153, tf3, test]);
    496     } finally {
    497       f_153([f_153, tf3, test]);
    498     }
    499     return f_153([f_153, test]);
    500   }
    501 
    502   function test() {
    503     assertEquals(153, tf1());
    504     assertEquals(153, tf2());
    505     assertEquals(153, tf3());
    506   }
    507   test();
    508   test();
    509   %OptimizeFunctionOnNextCall(test);
    510   test();
    511 })();
    512 
    513 
    514 // Test tail calls from try-catch-finally constructs.
    515 (function() {
    516   function tcf1(a) {
    517     try {
    518       f_153([f_153, tcf1, test]);
    519       return f_153([f_153, tcf1, test]);
    520     } catch(e) {
    521     } finally {
    522       f_153([f_153, tcf1, test]);
    523     }
    524   }
    525 
    526   function tcf2(a) {
    527     try {
    528       f_153([f_153, tcf2, test]);
    529       throw new Error("boom");
    530     } catch(e) {
    531       f_153([f_153, tcf2, test]);
    532       return f_153([f_153, tcf2, test]);
    533     } finally {
    534       f_153([f_153, tcf2, test]);
    535     }
    536   }
    537 
    538   function tcf3(a) {
    539     try {
    540       f_153([f_153, tcf3, test]);
    541       throw new Error("boom");
    542     } catch(e) {
    543       f_153([f_153, tcf3, test]);
    544     } finally {
    545       f_153([f_153, tcf3, test]);
    546       return f_153([f_153, test]);
    547     }
    548   }
    549 
    550   function tcf4(a) {
    551     try {
    552       f_153([f_153, tcf4, test]);
    553       throw new Error("boom");
    554     } catch(e) {
    555       f_153([f_153, tcf4, test]);
    556     } finally {
    557       f_153([f_153, tcf4, test]);
    558     }
    559     return f_153([f_153, test]);
    560   }
    561 
    562   function test() {
    563     assertEquals(153, tcf1());
    564     assertEquals(153, tcf2());
    565     assertEquals(153, tcf3());
    566     assertEquals(153, tcf4());
    567   }
    568   test();
    569   test();
    570   %OptimizeFunctionOnNextCall(test);
    571   test();
    572 })();
    573 
    574 
    575 // Test tail calls from arrow functions.
    576 (function () {
    577   function g1(a) {
    578     return (() => { return f_153([f_153, test]); })();
    579   }
    580 
    581   function g2(a) {
    582     return (() => f_153([f_153, test]))();
    583   }
    584 
    585   function g3(a) {
    586     var closure = () => f([f, closure, test], true)
    587                               ? f_153([f_153, test])
    588                               : f_153([f_153, test]);
    589     return closure();
    590   }
    591 
    592   function test() {
    593     assertEquals(153, g1());
    594     assertEquals(153, g2());
    595     assertEquals(153, g3());
    596   }
    597   test();
    598   test();
    599   %OptimizeFunctionOnNextCall(test);
    600   test();
    601 })();
    602 
    603 
    604 // Test tail calls from do expressions.
    605 (function () {
    606   function g1(a) {
    607     var a = do { return f_153([f_153, test]); 42; };
    608     return a;
    609   }
    610 
    611   function test() {
    612     assertEquals(153, g1());
    613   }
    614   test();
    615   test();
    616   %OptimizeFunctionOnNextCall(test);
    617   test();
    618 })();
    619