OLD | NEW |
| (Empty) |
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 })(); | |
OLD | NEW |