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