Home | History | Annotate | Download | only in es8
      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-explicit-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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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 continue 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) || continue f([f, test], true);
    313   }
    314 
    315   function g2(a) {
    316     return f([f, g2, test], true) && continue f([f, test], true);
    317   }
    318 
    319   function g3(a) {
    320     return f([f, g3, test], 13), continue f([f, test], 153);
    321   }
    322 
    323   function g4(a) {
    324     return f([f, g4, test], false) ||
    325         (f([f, g4, test], true) && continue f([f, test], true));
    326   }
    327 
    328   function g5(a) {
    329     return f([f, g5, test], true) &&
    330         (f([f, g5, test], false) || continue f([f, test], true));
    331   }
    332 
    333   function g6(a) {
    334     return f([f, g6, test], 13), f([f, g6, test], 42),
    335         continue f([f, test], 153);
    336   }
    337 
    338   function g7(a) {
    339     return f([f, g7, test], false) ||
    340         (f([f, g7, test], false) ? continue f([f, test], true)
    341              : continue f([f, test], true));
    342   }
    343 
    344   function g8(a) {
    345     return f([f, g8, test], false) || f([f, g8, test], true) &&
    346         continue f([f, test], true);
    347   }
    348 
    349   function g9(a) {
    350     return f([f, g9, test], true) && f([f, g9, test], false) ||
    351         continue 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                 continue f([f, test], true) :
    359             f([f, g10, test], true) && f([f, g10, test], false) ||
    360                 continue 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 g3() {
    385     for (var i = 0; i < 10; i++) {
    386       return continue f_153([f_153, test]);
    387     }
    388   }
    389 
    390   function g4() {
    391     while (true) {
    392       return continue f_153([f_153, test]);
    393     }
    394   }
    395 
    396   function g5() {
    397     do {
    398       return continue f_153([f_153, test]);
    399     } while (true);
    400   }
    401 
    402   function test() {
    403     assertEquals(153, g3());
    404     assertEquals(153, g4());
    405     assertEquals(153, g5());
    406   }
    407   test();
    408   test();
    409   %OptimizeFunctionOnNextCall(test);
    410   test();
    411 })();
    412 
    413 
    414 // Test tail calls from try-catch constructs.
    415 (function() {
    416   function tc1(a) {
    417     try {
    418       f_153([f_153, tc1, test]);
    419       return f_153([f_153, tc1, test]);
    420     } catch(e) {
    421       f_153([f_153, tc1, test]);
    422     }
    423   }
    424 
    425   function tc2(a) {
    426     try {
    427       f_153([f_153, tc2, test]);
    428       throw new Error("boom");
    429     } catch(e) {
    430       f_153([f_153, tc2, test]);
    431       return continue f_153([f_153, test]);
    432     }
    433   }
    434 
    435   function tc3(a) {
    436     try {
    437       f_153([f_153, tc3, test]);
    438       throw new Error("boom");
    439     } catch(e) {
    440       f_153([f_153, tc3, test]);
    441     }
    442     f_153([f_153, tc3, test]);
    443     return continue f_153([f_153, test]);
    444   }
    445 
    446   function test() {
    447     assertEquals(153, tc1());
    448     assertEquals(153, tc2());
    449     assertEquals(153, tc3());
    450   }
    451   test();
    452   test();
    453   %OptimizeFunctionOnNextCall(test);
    454   test();
    455 })();
    456 
    457 
    458 // Test tail calls from try-finally constructs.
    459 (function() {
    460   function tf1(a) {
    461     try {
    462       f_153([f_153, tf1, test]);
    463       return f_153([f_153, tf1, test]);
    464     } finally {
    465       f_153([f_153, tf1, test]);
    466     }
    467   }
    468 
    469   function tf2(a) {
    470     try {
    471       f_153([f_153, tf2, test]);
    472       throw new Error("boom");
    473     } finally {
    474       f_153([f_153, tf2, test]);
    475       return continue f_153([f_153, test]);
    476     }
    477   }
    478 
    479   function tf3(a) {
    480     try {
    481       f_153([f_153, tf3, test]);
    482     } finally {
    483       f_153([f_153, tf3, test]);
    484     }
    485     return continue f_153([f_153, test]);
    486   }
    487 
    488   function test() {
    489     assertEquals(153, tf1());
    490     assertEquals(153, tf2());
    491     assertEquals(153, tf3());
    492   }
    493   test();
    494   test();
    495   %OptimizeFunctionOnNextCall(test);
    496   test();
    497 })();
    498 
    499 
    500 // Test tail calls from try-catch-finally constructs.
    501 (function() {
    502   function tcf1(a) {
    503     try {
    504       f_153([f_153, tcf1, test]);
    505       return f_153([f_153, tcf1, test]);
    506     } catch(e) {
    507     } finally {
    508       f_153([f_153, tcf1, test]);
    509     }
    510   }
    511 
    512   function tcf2(a) {
    513     try {
    514       f_153([f_153, tcf2, test]);
    515       throw new Error("boom");
    516     } catch(e) {
    517       f_153([f_153, tcf2, test]);
    518       return f_153([f_153, tcf2, test]);
    519     } finally {
    520       f_153([f_153, tcf2, test]);
    521     }
    522   }
    523 
    524   function tcf3(a) {
    525     try {
    526       f_153([f_153, tcf3, test]);
    527       throw new Error("boom");
    528     } catch(e) {
    529       f_153([f_153, tcf3, test]);
    530     } finally {
    531       f_153([f_153, tcf3, test]);
    532       return continue f_153([f_153, test]);
    533     }
    534   }
    535 
    536   function tcf4(a) {
    537     try {
    538       f_153([f_153, tcf4, test]);
    539       throw new Error("boom");
    540     } catch(e) {
    541       f_153([f_153, tcf4, test]);
    542     } finally {
    543       f_153([f_153, tcf4, test]);
    544     }
    545     return continue f_153([f_153, test]);
    546   }
    547 
    548   function test() {
    549     assertEquals(153, tcf1());
    550     assertEquals(153, tcf2());
    551     assertEquals(153, tcf3());
    552     assertEquals(153, tcf4());
    553   }
    554   test();
    555   test();
    556   %OptimizeFunctionOnNextCall(test);
    557   test();
    558 })();
    559 
    560 
    561 // Test tail calls from arrow functions.
    562 (function () {
    563   function g1(a) {
    564     return continue (() => { return continue f_153([f_153, test]); })();
    565   }
    566 
    567   function g2(a) {
    568     return continue (() => continue f_153([f_153, test]))();
    569   }
    570 
    571   function g3(a) {
    572     var closure = () => f([f, closure, test], true)
    573                               ? continue f_153([f_153, test])
    574                               : continue f_153([f_153, test]);
    575     return continue closure();
    576   }
    577 
    578   function test() {
    579     assertEquals(153, g1());
    580     assertEquals(153, g2());
    581     assertEquals(153, g3());
    582   }
    583   test();
    584   test();
    585   %OptimizeFunctionOnNextCall(test);
    586   test();
    587 })();
    588 
    589 
    590 // Test tail calls from do expressions.
    591 (function () {
    592   function g1(a) {
    593     var a = do { return continue f_153([f_153, test]); 42; };
    594     return a;
    595   }
    596 
    597   function test() {
    598     assertEquals(153, g1());
    599   }
    600   test();
    601   test();
    602   %OptimizeFunctionOnNextCall(test);
    603   test();
    604 })();
    605