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-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 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(); 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 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(); 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 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(); 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 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(); 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 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 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 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) || 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 323 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 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 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(); 423 %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(); 466 test(); 467 %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(); 508 test(); 509 %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(); 569 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 585 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 592 function test() { 593 assertEquals(153, g1()); 594 assertEquals(153, g2()); 595 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()); 613 } 614 test(); 615 test(); 616 %OptimizeFunctionOnNextCall(test); 617 test(); 618 })(); 619