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
10Error.prepareStackTrace = (error,stack) => {
11  error.strace = stack;
12  return error.message + "\n    at " + stack.join("\n    at ");
13}
14
15
16function 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
28function f(expected_call_stack, a, b) {
29  CheckStackTrace(expected_call_stack);
30  return a;
31}
32
33function 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