blob: 6ecf04f3d99414e61e19ebba6349f74c9d63d7a7 [file] [log] [blame]
Ben Murdoch097c5b22016-05-18 11:27:45 +01001// 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
Ben Murdochc5610432016-08-08 18:44:38 +01006// Flags: --harmony-do-expressions
7
Ben Murdoch097c5b22016-05-18 11:27:45 +01008"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}
Ben Murdochda12d292016-06-02 14:46:10 +010025%NeverOptimizeFunction(CheckStackTrace);
26
Ben Murdoch097c5b22016-05-18 11:27:45 +010027
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();
Ben Murdochda12d292016-06-02 14:46:10 +010076 test();
Ben Murdoch097c5b22016-05-18 11:27:45 +010077 %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();
Ben Murdochda12d292016-06-02 14:46:10 +0100119 test();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100120 %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();
Ben Murdochda12d292016-06-02 14:46:10 +0100171 test();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100172 %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();
Ben Murdochda12d292016-06-02 14:46:10 +0100222 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
Ben Murdochda12d292016-06-02 14:46:10 +0100264 function f(v) {
Ben Murdochc5610432016-08-08 18:44:38 +0100265 return g();
Ben Murdochda12d292016-06-02 14:46:10 +0100266 }
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();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100304 %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
Ben Murdochc5610432016-08-08 18:44:38 +0100323 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
Ben Murdoch097c5b22016-05-18 11:27:45 +0100363 function test() {
364 assertEquals(true, g1());
365 assertEquals(true, g2());
366 assertEquals(153, g3());
Ben Murdochc5610432016-08-08 18:44:38 +0100367 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());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100374 }
375 test();
Ben Murdochda12d292016-06-02 14:46:10 +0100376 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();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100423 %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();
Ben Murdochda12d292016-06-02 14:46:10 +0100466 test();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100467 %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();
Ben Murdochda12d292016-06-02 14:46:10 +0100508 test();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100509 %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();
Ben Murdochda12d292016-06-02 14:46:10 +0100569 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
Ben Murdochc5610432016-08-08 18:44:38 +0100585 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
Ben Murdochda12d292016-06-02 14:46:10 +0100592 function test() {
593 assertEquals(153, g1());
594 assertEquals(153, g2());
Ben Murdochc5610432016-08-08 18:44:38 +0100595 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());
Ben Murdochda12d292016-06-02 14:46:10 +0100613 }
614 test();
615 test();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100616 %OptimizeFunctionOnNextCall(test);
617 test();
618})();