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 })(); 605