blob: 44936a4b22015a16775c0b5f34c42d215cb2991e [file] [log] [blame]
Ben Murdochc5610432016-08-08 18:44:38 +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-explicit-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 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})();