Merge V8 5.2.361.47  DO NOT MERGE

https://chromium.googlesource.com/v8/v8/+/5.2.361.47

FPIIM-449

Change-Id: Ibec421b85a9b88cb3a432ada642e469fe7e78346
(cherry picked from commit bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8)
diff --git a/test/mjsunit/es8/syntactic-tail-call-parsing-sloppy.js b/test/mjsunit/es8/syntactic-tail-call-parsing-sloppy.js
new file mode 100644
index 0000000..d026086
--- /dev/null
+++ b/test/mjsunit/es8/syntactic-tail-call-parsing-sloppy.js
@@ -0,0 +1,410 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --harmony-explicit-tailcalls
+// Flags: --harmony-do-expressions
+
+var SyntaxErrorTests = [
+  { msg: "Unexpected expression inside tail call",
+    tests: [
+      { src: `()=>{ return continue  foo ; }`,
+        err: `                       ^^^`,
+      },
+      { src: `()=>{ return  continue 42 ; }`,
+        err: `                       ^^`,
+      },
+      { src: `()=>{ return  continue   new foo ()  ; }`,
+        err: `                         ^^^^^^^^^^`,
+      },
+      { src: `()=>{ loop: return  continue  loop ; }`,
+        err: `                              ^^^^`,
+      },
+      { src: `class A { foo() { return  continue   super.x ; } }`,
+        err: `                                     ^^^^^^^`,
+      },
+      { src: `()=>{ return  continue  this  ; }`,
+        err: `                        ^^^^`,
+      },
+      { src: `()=>{ return  continue class A {} ; }`,
+        err: `                       ^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue class A extends B {} ; }`,
+        err: `                       ^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue function A() { } ; }`,
+        err: `                       ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue { a: b, c: d} ; }`,
+        err: `                       ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue function* Gen() { yield 1; } ; }`,
+        err: `                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `function A() { return  continue new.target ; }`,
+        err: `                                ^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue () ; }`,
+        err: `                       ^^`,
+      },
+      { src: `()=>{ return  continue ( 42 ) ; }`,
+        err: `                       ^^^^^^`,
+      },
+      { src: "()=>{ return continue `123 ${foo} 34lk` ;  }",
+        err: `                      ^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue do { x ? foo() : bar() ; } }`,
+        err: `                      ^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Tail call expression is not allowed here",
+    tests: [
+      { src: `class A {}; class B extends A { constructor() { return continue foo () ; } }`,
+        err: `                                                       ^^^^^^^^^^^^^^^`,
+      },
+      { src: `class A extends continue f () {}; }`,
+        err: `                ^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Tail call expressions are not allowed in non-strict mode",
+    tests: [
+      { src: `()=>{ return  continue continue continue b()  ; }`,
+        err: `                                ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue ( continue b() ) ; }`,
+        err: `                         ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return   continue  f()   - a ; }`,
+        err: `               ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return b + continue   f()  ; }`,
+        err: `                 ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return 1, 2, 3,   continue  f() , 4  ; }`,
+        err: `                        ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ var x =  continue  f ( ) ; }`,
+        err: `               ^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return   continue f () ? 1 : 2 ; }`,
+        err: `               ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return (1, 2, 3, continue f()), 4; }`,
+        err: `                       ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return [1, 2, continue f() ] ;  }`,
+        err: `                    ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return [1, 2, ... continue f() ] ;  }`,
+        err: `                        ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return [1, 2, continue f(), 3 ] ;  }`,
+        err: `                    ^^^^^^^^^^^^`,
+      },
+      { src: "()=>{ return `123 ${a} ${ continue foo ( ) } 34lk` ;  }",
+        err: `                          ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return g( 1, 2, continue f() ); }`,
+        err: `                      ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f() || a; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a || b || c || continue f() || d; }`,
+        err: `                            ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a && b && c && continue f() && d; }`,
+        err: `                            ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a && b || c && continue f() ? d : e; }`,
+        err: `                            ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a ? b : c && continue f() && d || e; }`,
+        err: `                          ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue foo() instanceof bar ; }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return bar instanceof continue foo() ; }`,
+        err: `                            ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue foo() in bar ; }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return bar in continue foo() ; }`,
+        err: `                    ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ function* G() { yield continue foo(); } }`,
+        err: `                            ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ (1, 2, 3, continue f() ) => {} }`,
+        err: `                ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ (... continue f()) => {}  }`,
+        err: `           ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ (a, b, c, ... continue f() ) => {}  }`,
+        err: `                    ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a <= continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return b > continue f(); }`,
+        err: `                 ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a << continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return b >> continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return c >>> continue f(); }`,
+        err: `                   ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f() = a ; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a = continue f() ; }`,
+        err: `                 ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a += continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a ** continue f() ; }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return delete continue foo() ; }`,
+        err: `                    ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ typeof continue foo()  ; }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return ~ continue foo() ; }`,
+        err: `               ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return void  continue foo() ; }`,
+        err: `                   ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return !continue foo() ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return -continue foo() ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return +continue foo() ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return ++ continue f( ) ; }`,
+        err: `                ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f()  ++; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f() --; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return (continue foo()) () ;  }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var i = continue foo(); i < 10; i++) bar(); }`,
+        err: `                   ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var i = 0; i < continue foo(); i++) bar(); }`,
+        err: `                          ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var i = 0; i < 10; continue foo()) bar(); }`,
+        err: `                              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ if (continue foo()) bar(); }`,
+        err: `          ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ while (continue foo()) bar(); }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ do { smth; } while (continue foo()) ; }`,
+        err: `                          ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ throw continue foo() ; }`,
+        err: `            ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ switch (continue foo()) { case 1: break; } ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ with (continue foo()) { smth; } }`,
+        err: `            ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ let x = continue foo() }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ const c = continue  foo() }`,
+        err: `                ^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { return  continue   f ( ) ; } catch(e) {} }`,
+        err: `                    ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { try { smth; } catch(e) { return  continue  f( ) ; } }`,
+        err: `                                             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { try { smth; } catch(e) { return  continue  f( ) ; } } finally { bla; } }`,
+        err: `                                             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { smth; } catch(e) { return  continue   f ( ) ; } finally { blah; } }`,
+        err: `                                       ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { smth; } catch(e) { try { smth; } catch (e) { return  continue   f ( ) ; } } finally { blah; } }`,
+        err: `                                                                 ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var v in {a:0}) { return continue  foo () ; } }`,
+        err: `                                    ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var v of [1, 2, 3]) { return continue  foo () ; } }`,
+        err: `                                        ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue  a.b.c.foo () ; }`,
+        err: `             ^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue  a().b.c().d.foo () ; }`,
+        err: `             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue  foo (1)(2)(3, 4) ; }`,
+        err: `             ^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return ( continue b() ) ; }`,
+        err: `               ^^^^^^^^^^^^`,
+      },
+      { src: "()=>{ return continue bar`ab cd ef` ; }",
+        err: `             ^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: "()=>{ return continue bar`ab ${cd} ef` ; }",
+        err: `             ^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a || continue f() ; }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a && continue f() ; }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a , continue f() ; }`,
+        err: `                 ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ function* G() { return continue foo(); } }`,
+        err: `                             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ function B() { return continue new.target() ; } }`,
+        err: `                            ^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue do { x ? foo() : bar() ; }() }`,
+        err: `             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue (do { x ? foo() : bar() ; })() }`,
+        err: `             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return do { 1, continue foo() } }`,
+        err: `                     ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return do { x ? continue foo() : y } }`,
+        err: `                      ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a || (b && continue c()); }`,
+        err: `                        ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a && (b || continue c()); }`,
+        err: `                        ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a || (b ? c : continue d()); }`,
+        err: `                           ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return 1, 2, 3, a || (b ? c : continue d()); }`,
+        err: `                                    ^^^^^^^^^^^^`,
+      },
+      { src: `()=> continue  (foo ()) ;`,
+        err: `     ^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=> a || continue  foo () ;`,
+        err: `          ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=> a && continue  foo () ;`,
+        err: `          ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=> a ? continue  foo () : b;`,
+        err: `         ^^^^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Undefined label 'foo'",
+    tests: [
+      { src: `()=>{ continue  foo () ; }`,
+        err: `                ^^^`,
+      },
+    ],
+  },
+];
+
+
+// Should parse successfully.
+var NoErrorTests = [
+  `()=>{ class A { foo() { return continue super.f() ; } } }`,
+  `()=>{ class A { foo() { return continue f() ; } } }`,
+  `()=>{ class A { foo() { return a || continue f() ; } } }`,
+  `()=>{ class A { foo() { return b && continue f() ; } } }`,
+];
+
+
+(function() {
+  for (var test_set of SyntaxErrorTests) {
+    var expected_message = "SyntaxError: " + test_set.msg;
+    for (var test of test_set.tests) {
+      var passed = true;
+      var e = null;
+      try {
+        Realm.eval(0, test.src);
+      } catch (ee) {
+        e = ee;
+      }
+      print("=======================================");
+      print("Expected | " + expected_message);
+      print("Source   | " + test.src);
+      print("         | " + test.err);
+
+      if (e === null) {
+        print("FAILED");
+        throw new Error("SyntaxError was not thrown");
+      }
+
+      var details = %GetExceptionDetails(e);
+      if (details.start_pos == undefined ||
+          details.end_pos == undefined) {
+        throw new Error("Bad message object returned");
+      }
+      var underline = " ".repeat(details.start_pos) +
+                      "^".repeat(details.end_pos - details.start_pos);
+      var passed = expected_message === e.toString() &&
+                   test.err === underline;
+
+      if (passed) {
+        print("PASSED");
+        print();
+      } else {
+        print("---------------------------------------");
+        print("Actual   | " + e);
+        print("Source   | " + test.src);
+        print("         | " + underline);
+        print("FAILED");
+        throw new Error("Test failed");
+      }
+    }
+  }
+})();
+
+
+(function() {
+  for (var src of NoErrorTests) {
+    print("=======================================");
+    print("Source   | " + src);
+    Realm.eval(0, src);
+    print("PASSED");
+    print();
+  }
+})();
diff --git a/test/mjsunit/es8/syntactic-tail-call-parsing.js b/test/mjsunit/es8/syntactic-tail-call-parsing.js
new file mode 100644
index 0000000..9ceff9c
--- /dev/null
+++ b/test/mjsunit/es8/syntactic-tail-call-parsing.js
@@ -0,0 +1,388 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --harmony-explicit-tailcalls
+// Flags: --harmony-do-expressions
+"use strict";
+
+var SyntaxErrorTests = [
+  { msg: "Unexpected expression inside tail call",
+    tests: [
+      { src: `()=>{ return continue  foo ; }`,
+        err: `                       ^^^`,
+      },
+      { src: `()=>{ return  continue 42 ; }`,
+        err: `                       ^^`,
+      },
+      { src: `()=>{ return  continue   new foo ()  ; }`,
+        err: `                         ^^^^^^^^^^`,
+      },
+      { src: `()=>{ loop: return  continue  loop ; }`,
+        err: `                              ^^^^`,
+      },
+      { src: `class A { foo() { return  continue   super.x ; } }`,
+        err: `                                     ^^^^^^^`,
+      },
+      { src: `()=>{ return  continue  this  ; }`,
+        err: `                        ^^^^`,
+      },
+      { src: `()=>{ return  continue class A {} ; }`,
+        err: `                       ^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue class A extends B {} ; }`,
+        err: `                       ^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue function A() { } ; }`,
+        err: `                       ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue { a: b, c: d} ; }`,
+        err: `                       ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue function* Gen() { yield 1; } ; }`,
+        err: `                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `function A() { return  continue new.target ; }`,
+        err: `                                ^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue () ; }`,
+        err: `                       ^^`,
+      },
+      { src: `()=>{ return  continue ( 42 ) ; }`,
+        err: `                       ^^^^^^`,
+      },
+      { src: "()=>{ return continue `123 ${foo} 34lk` ;  }",
+        err: `                      ^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue do { x ? foo() : bar() ; } }`,
+        err: `                      ^^^^^^^^^^^^^^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Tail call expression is not allowed here",
+    tests: [
+      { src: `()=>{ return  continue continue continue b()  ; }`,
+        err: `                                ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  continue ( continue b() ) ; }`,
+        err: `                         ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return   continue  f()   - a ; }`,
+        err: `               ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return b + continue   f()  ; }`,
+        err: `                 ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return 1, 2, 3,   continue  f() , 4  ; }`,
+        err: `                        ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ var x =  continue  f ( ) ; }`,
+        err: `               ^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return   continue f () ? 1 : 2 ; }`,
+        err: `               ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return (1, 2, 3, continue f()), 4; }`,
+        err: `                       ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return [1, 2, continue f() ] ;  }`,
+        err: `                    ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return [1, 2, ... continue f() ] ;  }`,
+        err: `                        ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return [1, 2, continue f(), 3 ] ;  }`,
+        err: `                    ^^^^^^^^^^^^`,
+      },
+      { src: "()=>{ return `123 ${a} ${ continue foo ( ) } 34lk` ;  }",
+        err: `                          ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return g( 1, 2, continue f() ); }`,
+        err: `                      ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f() || a; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a || b || c || continue f() || d; }`,
+        err: `                            ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a && b && c && continue f() && d; }`,
+        err: `                            ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a && b || c && continue f() ? d : e; }`,
+        err: `                            ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a ? b : c && continue f() && d || e; }`,
+        err: `                          ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue foo() instanceof bar ; }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return bar instanceof continue foo() ; }`,
+        err: `                            ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue foo() in bar ; }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return bar in continue foo() ; }`,
+        err: `                    ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ function* G() { yield continue foo(); } }`,
+        err: `                            ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ (1, 2, 3, continue f() ) => {} }`,
+        err: `                ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ (... continue f()) => {}  }`,
+        err: `           ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ (a, b, c, ... continue f() ) => {}  }`,
+        err: `                    ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a <= continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return b > continue f(); }`,
+        err: `                 ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a << continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return b >> continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return c >>> continue f(); }`,
+        err: `                   ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f() = a ; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a = continue f() ; }`,
+        err: `                 ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a += continue f(); }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return a ** continue f() ; }`,
+        err: `                  ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return delete continue foo() ; }`,
+        err: `                    ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ typeof continue foo()  ; }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return ~ continue foo() ; }`,
+        err: `               ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return void  continue foo() ; }`,
+        err: `                   ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return !continue foo() ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return -continue foo() ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return +continue foo() ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return ++ continue f( ) ; }`,
+        err: `                ^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f()  ++; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return continue f() --; }`,
+        err: `             ^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return (continue foo()) () ;  }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var i = continue foo(); i < 10; i++) bar(); }`,
+        err: `                   ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var i = 0; i < continue foo(); i++) bar(); }`,
+        err: `                          ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var i = 0; i < 10; continue foo()) bar(); }`,
+        err: `                              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ if (continue foo()) bar(); }`,
+        err: `          ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ while (continue foo()) bar(); }`,
+        err: `             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ do { smth; } while (continue foo()) ; }`,
+        err: `                          ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ throw continue foo() ; }`,
+        err: `            ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ switch (continue foo()) { case 1: break; } ; }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ let x = continue foo() }`,
+        err: `              ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ const c = continue  foo() }`,
+        err: `                ^^^^^^^^^^^^^^^`,
+      },
+      { src: `class A {}; class B extends A { constructor() { return continue foo () ; } }`,
+        err: `                                                       ^^^^^^^^^^^^^^^`,
+      },
+      { src: `class A extends continue f () {}; }`,
+        err: `                ^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Tail call expression in try block",
+    tests: [
+      { src: `()=>{ try { return  continue   f ( ) ; } catch(e) {} }`,
+        err: `                    ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { try { smth; } catch(e) { return  continue  f( ) ; } }`,
+        err: `                                             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { try { smth; } catch(e) { return  continue  f( ) ; } } finally { bla; } }`,
+        err: `                                             ^^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Tail call expression in catch block when finally block is also present",
+    tests: [
+      { src: `()=>{ try { smth; } catch(e) { return  continue   f ( ) ; } finally { blah; } }`,
+        err: `                                       ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ try { smth; } catch(e) { try { smth; } catch (e) { return  continue   f ( ) ; } } finally { blah; } }`,
+        err: `                                                                 ^^^^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Tail call expression in for-in/of body",
+    tests: [
+      { src: `()=>{ for (var v in {a:0}) { return continue  foo () ; } }`,
+        err: `                                    ^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ for (var v of [1, 2, 3]) { return continue  foo () ; } }`,
+        err: `                                        ^^^^^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Tail call of a direct eval is not allowed",
+    tests: [
+      { src: `()=>{ return  continue  eval(" foo () " )  ; }`,
+        err: `                        ^^^^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  a || continue  eval("", 1, 2)  ; }`,
+        err: `                             ^^^^^^^^^^^^^^`,
+      },
+      { src: `()=>{ return  a, continue  eval  ( )  ; }`,
+        err: `                           ^^^^^^^^^`,
+      },
+      { src: `()=> a, continue  eval  ( )  ; `,
+        err: `                  ^^^^^^^^^`,
+      },
+      { src: `()=> a || continue  eval  (' ' )  ; `,
+        err: `                    ^^^^^^^^^^^^`,
+      },
+    ],
+  },
+  { msg: "Undefined label 'foo'",
+    tests: [
+      { src: `()=>{ continue  foo () ; }`,
+        err: `                ^^^`,
+      },
+    ],
+  },
+];
+
+
+// Should parse successfully.
+var NoErrorTests = [
+  `()=>{ return continue  a.b.c.foo () ; }`,
+  `()=>{ return continue  a().b.c().d.foo () ; }`,
+  `()=>{ return continue  foo (1)(2)(3, 4) ; }`,
+  `()=>{ return continue (0, eval)(); }`,
+  `()=>{ return ( continue b() ) ; }`,
+  "()=>{ return continue bar`ab cd ef` ; }",
+  "()=>{ return continue bar`ab ${cd} ef` ; }",
+  `()=>{ return a || continue f() ; }`,
+  `()=>{ return a && continue f() ; }`,
+  `()=>{ return a , continue f() ; }`,
+  `()=>{ function* G() { return continue foo(); } }`,
+  `()=>{ class A { foo() { return continue super.f() ; } } }`,
+  `()=>{ function B() { return continue new.target() ; } }`,
+  `()=>{ return continue do { x ? foo() : bar() ; }() }`,
+  `()=>{ return continue (do { x ? foo() : bar() ; })() }`,
+  `()=>{ return do { 1, continue foo() } }`,
+  `()=>{ return do { x ? continue foo() : y } }`,
+  `()=>{ return a || (b && continue c()); }`,
+  `()=>{ return a && (b || continue c()); }`,
+  `()=>{ return a || (b ? c : continue d()); }`,
+  `()=>{ return 1, 2, 3, a || (b ? c : continue d()); }`,
+  `()=> continue  (foo ()) ;`,
+  `()=> a || continue  foo () ;`,
+  `()=> a && continue  foo () ;`,
+  `()=> a ? continue  foo () : b;`,
+];
+
+
+(function() {
+  for (var test_set of SyntaxErrorTests) {
+    var expected_message = "SyntaxError: " + test_set.msg;
+    for (var test of test_set.tests) {
+      var passed = true;
+      var e = null;
+      try {
+        eval(test.src);
+      } catch (ee) {
+        e = ee;
+      }
+      print("=======================================");
+      print("Expected | " + expected_message);
+      print("Source   | " + test.src);
+      print("         | " + test.err);
+
+      if (e === null) {
+        print("FAILED");
+        throw new Error("SyntaxError was not thrown");
+      }
+
+      var details = %GetExceptionDetails(e);
+      if (details.start_pos == undefined ||
+          details.end_pos == undefined) {
+        throw new Error("Bad message object returned");
+      }
+      var underline = " ".repeat(details.start_pos) +
+                      "^".repeat(details.end_pos - details.start_pos);
+      var passed = expected_message === e.toString() &&
+                   test.err === underline;
+
+      if (passed) {
+        print("PASSED");
+        print();
+      } else {
+        print("---------------------------------------");
+        print("Actual   | " + e);
+        print("Source   | " + test.src);
+        print("         | " + underline);
+        print("FAILED");
+        throw new Error("Test failed");
+      }
+    }
+  }
+})();
+
+
+(function() {
+  for (var src of NoErrorTests) {
+    print("=======================================");
+    print("Source   | " + src);
+    src = `"use strict"; ` + src;
+    Realm.eval(0, src);
+    print("PASSED");
+    print();
+  }
+})();
diff --git a/test/mjsunit/es8/syntactic-tail-call-simple.js b/test/mjsunit/es8/syntactic-tail-call-simple.js
new file mode 100644
index 0000000..ec7ade6
--- /dev/null
+++ b/test/mjsunit/es8/syntactic-tail-call-simple.js
@@ -0,0 +1,143 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --harmony-explicit-tailcalls --stack-size=100
+
+//
+// Tail calls work only in strict mode.
+//
+(function() {
+  function f(n) {
+    if (n <= 0) {
+      return "foo";
+    }
+    return f(n - 1);
+  }
+  assertThrows(()=>{ f(1e5) });
+  %OptimizeFunctionOnNextCall(f);
+  assertThrows(()=>{ f(1e5) });
+})();
+
+
+//
+// Tail call normal functions.
+//
+(function() {
+  "use strict";
+  function f(n) {
+    if (n <= 0) {
+      return "foo";
+    }
+    return continue f(n - 1);
+  }
+  assertEquals("foo", f(1e5));
+  %OptimizeFunctionOnNextCall(f);
+  assertEquals("foo", f(1e5));
+})();
+
+
+(function() {
+  "use strict";
+  function f(n) {
+    if (n <= 0) {
+      return  "foo";
+    }
+    return continue f(n - 1, 42);  // Call with arguments adaptor.
+  }
+  assertEquals("foo", f(1e5));
+  %OptimizeFunctionOnNextCall(f);
+  assertEquals("foo", f(1e5));
+})();
+
+
+(function() {
+  "use strict";
+  function f(n){
+    if (n <= 0) {
+      return "foo";
+    }
+    return continue g(n - 1);
+  }
+  function g(n){
+    if (n <= 0) {
+      return "bar";
+    }
+    return continue f(n - 1);
+  }
+  assertEquals("foo", f(1e5));
+  assertEquals("bar", f(1e5 + 1));
+  %OptimizeFunctionOnNextCall(f);
+  assertEquals("foo", f(1e5));
+  assertEquals("bar", f(1e5 + 1));
+})();
+
+
+(function() {
+  "use strict";
+  function f(n){
+    if (n <= 0) {
+      return "foo";
+    }
+    return continue g(n - 1, 42);  // Call with arguments adaptor.
+  }
+  function g(n){
+    if (n <= 0) {
+      return "bar";
+    }
+    return continue f(n - 1, 42);  // Call with arguments adaptor.
+  }
+  assertEquals("foo", f(1e5));
+  assertEquals("bar", f(1e5 + 1));
+  %OptimizeFunctionOnNextCall(f);
+  assertEquals("foo", f(1e5));
+  assertEquals("bar", f(1e5 + 1));
+})();
+
+
+//
+// Tail call bound functions.
+//
+(function() {
+  "use strict";
+  function f0(n) {
+    if (n <= 0) {
+      return "foo";
+    }
+    return continue f_bound(n - 1);
+  }
+  var f_bound = f0.bind({});
+  function f(n) {
+    return continue f_bound(n);
+  }
+  assertEquals("foo", f(1e5));
+  %OptimizeFunctionOnNextCall(f);
+  assertEquals("foo", f(1e5));
+})();
+
+
+(function() {
+  "use strict";
+  function f0(n){
+    if (n <= 0) {
+      return "foo";
+    }
+    return continue g_bound(n - 1);
+  }
+  function g0(n){
+    if (n <= 0) {
+      return "bar";
+    }
+    return continue f_bound(n - 1);
+  }
+  var f_bound = f0.bind({});
+  var g_bound = g0.bind({});
+  function f(n) {
+    return continue f_bound(n);
+  }
+  assertEquals("foo", f(1e5));
+  assertEquals("bar", f(1e5 + 1));
+  %OptimizeFunctionOnNextCall(f);
+  assertEquals("foo", f(1e5));
+  assertEquals("bar", f(1e5 + 1));
+})();
diff --git a/test/mjsunit/es8/syntactic-tail-call.js b/test/mjsunit/es8/syntactic-tail-call.js
new file mode 100644
index 0000000..44936a4
--- /dev/null
+++ b/test/mjsunit/es8/syntactic-tail-call.js
@@ -0,0 +1,604 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --harmony-explicit-tailcalls
+// Flags: --harmony-do-expressions
+
+"use strict";
+
+Error.prepareStackTrace = (error,stack) => {
+  error.strace = stack;
+  return error.message + "\n    at " + stack.join("\n    at ");
+}
+
+
+function CheckStackTrace(expected) {
+  var e = new Error();
+  e.stack;  // prepare stack trace
+  var stack = e.strace;
+  assertEquals("CheckStackTrace", stack[0].getFunctionName());
+  for (var i = 0; i < expected.length; i++) {
+    assertEquals(expected[i].name, stack[i + 1].getFunctionName());
+  }
+}
+%NeverOptimizeFunction(CheckStackTrace);
+
+
+function f(expected_call_stack, a, b) {
+  CheckStackTrace(expected_call_stack);
+  return a;
+}
+
+function f_153(expected_call_stack, a) {
+  CheckStackTrace(expected_call_stack);
+  return 153;
+}
+
+
+// Tail call when caller does not have an arguments adaptor frame.
+(function() {
+  // Caller and callee have same number of arguments.
+  function f1(a) {
+    CheckStackTrace([f1, test]);
+    return 10 + a;
+  }
+  function g1(a) { return continue f1(2); }
+
+  // Caller has more arguments than callee.
+  function f2(a) {
+    CheckStackTrace([f2, test]);
+    return 10 + a;
+  }
+  function g2(a, b, c) { return continue f2(2); }
+
+  // Caller has less arguments than callee.
+  function f3(a, b, c) {
+    CheckStackTrace([f3, test]);
+    return 10 + a + b + c;
+  }
+  function g3(a) { return continue f3(2, 3, 4); }
+
+  // Callee has arguments adaptor frame.
+  function f4(a, b, c) {
+    CheckStackTrace([f4, test]);
+    return 10 + a;
+  }
+  function g4(a) { return continue f4(2); }
+
+  function test() {
+    assertEquals(12, g1(1));
+    assertEquals(12, g2(1, 2, 3));
+    assertEquals(19, g3(1));
+    assertEquals(12, g4(1));
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail call when caller has an arguments adaptor frame.
+(function() {
+  // Caller and callee have same number of arguments.
+  function f1(a) {
+    CheckStackTrace([f1, test]);
+    return 10 + a;
+  }
+  function g1(a) { return continue f1(2); }
+
+  // Caller has more arguments than callee.
+  function f2(a) {
+    CheckStackTrace([f2, test]);
+    return 10 + a;
+  }
+  function g2(a, b, c) { return continue f2(2); }
+
+  // Caller has less arguments than callee.
+  function f3(a, b, c) {
+    CheckStackTrace([f3, test]);
+    return 10 + a + b + c;
+  }
+  function g3(a) { return continue f3(2, 3, 4); }
+
+  // Callee has arguments adaptor frame.
+  function f4(a, b, c) {
+    CheckStackTrace([f4, test]);
+    return 10 + a;
+  }
+  function g4(a) { return continue f4(2); }
+
+  function test() {
+    assertEquals(12, g1());
+    assertEquals(12, g2());
+    assertEquals(19, g3());
+    assertEquals(12, g4());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail call bound function when caller does not have an arguments
+// adaptor frame.
+(function() {
+  // Caller and callee have same number of arguments.
+  function f1(a) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f1, test]);
+    return 10 + a;
+  }
+  var b1 = f1.bind({a: 153});
+  function g1(a) { return continue b1(2); }
+
+  // Caller has more arguments than callee.
+  function f2(a) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f2, test]);
+    return 10 + a;
+  }
+  var b2 = f2.bind({a: 153});
+  function g2(a, b, c) { return continue b2(2); }
+
+  // Caller has less arguments than callee.
+  function f3(a, b, c) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f3, test]);
+    return 10 + a + b + c;
+  }
+  var b3 = f3.bind({a: 153});
+  function g3(a) { return continue b3(2, 3, 4); }
+
+  // Callee has arguments adaptor frame.
+  function f4(a, b, c) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f4, test]);
+    return 10 + a;
+  }
+  var b4 = f4.bind({a: 153});
+  function g4(a) { return continue b4(2); }
+
+  function test() {
+    assertEquals(12, g1(1));
+    assertEquals(12, g2(1, 2, 3));
+    assertEquals(19, g3(1));
+    assertEquals(12, g4(1));
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail call bound function when caller has an arguments adaptor frame.
+(function() {
+  // Caller and callee have same number of arguments.
+  function f1(a) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f1, test]);
+    return 10 + a;
+  }
+  var b1 = f1.bind({a: 153});
+  function g1(a) { return continue b1(2); }
+
+  // Caller has more arguments than callee.
+  function f2(a) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f2, test]);
+    return 10 + a;
+  }
+  var b2 = f2.bind({a: 153});
+  function g2(a, b, c) { return continue b2(2); }
+
+  // Caller has less arguments than callee.
+  function f3(a, b, c) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f3, test]);
+    return 10 + a + b + c;
+  }
+  var b3 = f3.bind({a: 153});
+  function g3(a) { return continue b3(2, 3, 4); }
+
+  // Callee has arguments adaptor frame.
+  function f4(a, b, c) {
+    assertEquals(153, this.a);
+    CheckStackTrace([f4, test]);
+    return 10 + a;
+  }
+  var b4 = f4.bind({a: 153});
+  function g4(a) { return continue b4(2); }
+
+  function test() {
+    assertEquals(12, g1());
+    assertEquals(12, g2());
+    assertEquals(19, g3());
+    assertEquals(12, g4());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail calling from getter.
+(function() {
+  function g(v) {
+    CheckStackTrace([g, test]);
+    %DeoptimizeFunction(test);
+    return 153;
+  }
+  %NeverOptimizeFunction(g);
+
+  function f(v) {
+    return continue g();
+  }
+  %SetForceInlineFlag(f);
+
+  function test() {
+    var o = {};
+    o.__defineGetter__('p', f);
+    assertEquals(153, o.p);
+  }
+
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail calling from setter.
+(function() {
+  function g() {
+    CheckStackTrace([g, test]);
+    %DeoptimizeFunction(test);
+    return 153;
+  }
+  %NeverOptimizeFunction(g);
+
+  function f(v) {
+    return continue g();
+  }
+  %SetForceInlineFlag(f);
+
+  function test() {
+    var o = {};
+    o.__defineSetter__('q', f);
+    assertEquals(1, o.q = 1);
+  }
+
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail calling from constructor.
+(function() {
+  function g(context) {
+    CheckStackTrace([g, test]);
+    %DeoptimizeFunction(test);
+    return {x: 153};
+  }
+  %NeverOptimizeFunction(g);
+
+  function A() {
+    this.x = 42;
+    return continue g();
+  }
+
+  function test() {
+    var o = new A();
+    %DebugPrint(o);
+    assertEquals(153, o.x);
+  }
+
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail calling via various expressions.
+(function() {
+  function g1(a) {
+    return f([f, g1, test], false) || continue f([f, test], true);
+  }
+
+  function g2(a) {
+    return f([f, g2, test], true) && continue f([f, test], true);
+  }
+
+  function g3(a) {
+    return f([f, g3, test], 13), continue f([f, test], 153);
+  }
+
+  function g4(a) {
+    return f([f, g4, test], false) ||
+        (f([f, g4, test], true) && continue f([f, test], true));
+  }
+
+  function g5(a) {
+    return f([f, g5, test], true) &&
+        (f([f, g5, test], false) || continue f([f, test], true));
+  }
+
+  function g6(a) {
+    return f([f, g6, test], 13), f([f, g6, test], 42),
+        continue f([f, test], 153);
+  }
+
+  function g7(a) {
+    return f([f, g7, test], false) ||
+        (f([f, g7, test], false) ? continue f([f, test], true)
+             : continue f([f, test], true));
+  }
+
+  function g8(a) {
+    return f([f, g8, test], false) || f([f, g8, test], true) &&
+        continue f([f, test], true);
+  }
+
+  function g9(a) {
+    return f([f, g9, test], true) && f([f, g9, test], false) ||
+        continue f([f, test], true);
+  }
+
+  function g10(a) {
+    return f([f, g10, test], true) && f([f, g10, test], false) ||
+        f([f, g10, test], true) ?
+            f([f, g10, test], true) && f([f, g10, test], false) ||
+                continue f([f, test], true) :
+            f([f, g10, test], true) && f([f, g10, test], false) ||
+                continue f([f, test], true);
+  }
+
+  function test() {
+    assertEquals(true, g1());
+    assertEquals(true, g2());
+    assertEquals(153, g3());
+    assertEquals(true, g4());
+    assertEquals(true, g5());
+    assertEquals(153, g6());
+    assertEquals(true, g7());
+    assertEquals(true, g8());
+    assertEquals(true, g9());
+    assertEquals(true, g10());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Tail calling from various statements.
+(function() {
+  function g3() {
+    for (var i = 0; i < 10; i++) {
+      return continue f_153([f_153, test]);
+    }
+  }
+
+  function g4() {
+    while (true) {
+      return continue f_153([f_153, test]);
+    }
+  }
+
+  function g5() {
+    do {
+      return continue f_153([f_153, test]);
+    } while (true);
+  }
+
+  function test() {
+    assertEquals(153, g3());
+    assertEquals(153, g4());
+    assertEquals(153, g5());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Test tail calls from try-catch constructs.
+(function() {
+  function tc1(a) {
+    try {
+      f_153([f_153, tc1, test]);
+      return f_153([f_153, tc1, test]);
+    } catch(e) {
+      f_153([f_153, tc1, test]);
+    }
+  }
+
+  function tc2(a) {
+    try {
+      f_153([f_153, tc2, test]);
+      throw new Error("boom");
+    } catch(e) {
+      f_153([f_153, tc2, test]);
+      return continue f_153([f_153, test]);
+    }
+  }
+
+  function tc3(a) {
+    try {
+      f_153([f_153, tc3, test]);
+      throw new Error("boom");
+    } catch(e) {
+      f_153([f_153, tc3, test]);
+    }
+    f_153([f_153, tc3, test]);
+    return continue f_153([f_153, test]);
+  }
+
+  function test() {
+    assertEquals(153, tc1());
+    assertEquals(153, tc2());
+    assertEquals(153, tc3());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Test tail calls from try-finally constructs.
+(function() {
+  function tf1(a) {
+    try {
+      f_153([f_153, tf1, test]);
+      return f_153([f_153, tf1, test]);
+    } finally {
+      f_153([f_153, tf1, test]);
+    }
+  }
+
+  function tf2(a) {
+    try {
+      f_153([f_153, tf2, test]);
+      throw new Error("boom");
+    } finally {
+      f_153([f_153, tf2, test]);
+      return continue f_153([f_153, test]);
+    }
+  }
+
+  function tf3(a) {
+    try {
+      f_153([f_153, tf3, test]);
+    } finally {
+      f_153([f_153, tf3, test]);
+    }
+    return continue f_153([f_153, test]);
+  }
+
+  function test() {
+    assertEquals(153, tf1());
+    assertEquals(153, tf2());
+    assertEquals(153, tf3());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Test tail calls from try-catch-finally constructs.
+(function() {
+  function tcf1(a) {
+    try {
+      f_153([f_153, tcf1, test]);
+      return f_153([f_153, tcf1, test]);
+    } catch(e) {
+    } finally {
+      f_153([f_153, tcf1, test]);
+    }
+  }
+
+  function tcf2(a) {
+    try {
+      f_153([f_153, tcf2, test]);
+      throw new Error("boom");
+    } catch(e) {
+      f_153([f_153, tcf2, test]);
+      return f_153([f_153, tcf2, test]);
+    } finally {
+      f_153([f_153, tcf2, test]);
+    }
+  }
+
+  function tcf3(a) {
+    try {
+      f_153([f_153, tcf3, test]);
+      throw new Error("boom");
+    } catch(e) {
+      f_153([f_153, tcf3, test]);
+    } finally {
+      f_153([f_153, tcf3, test]);
+      return continue f_153([f_153, test]);
+    }
+  }
+
+  function tcf4(a) {
+    try {
+      f_153([f_153, tcf4, test]);
+      throw new Error("boom");
+    } catch(e) {
+      f_153([f_153, tcf4, test]);
+    } finally {
+      f_153([f_153, tcf4, test]);
+    }
+    return continue f_153([f_153, test]);
+  }
+
+  function test() {
+    assertEquals(153, tcf1());
+    assertEquals(153, tcf2());
+    assertEquals(153, tcf3());
+    assertEquals(153, tcf4());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Test tail calls from arrow functions.
+(function () {
+  function g1(a) {
+    return continue (() => { return continue f_153([f_153, test]); })();
+  }
+
+  function g2(a) {
+    return continue (() => continue f_153([f_153, test]))();
+  }
+
+  function g3(a) {
+    var closure = () => f([f, closure, test], true)
+                              ? continue f_153([f_153, test])
+                              : continue f_153([f_153, test]);
+    return continue closure();
+  }
+
+  function test() {
+    assertEquals(153, g1());
+    assertEquals(153, g2());
+    assertEquals(153, g3());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();
+
+
+// Test tail calls from do expressions.
+(function () {
+  function g1(a) {
+    var a = do { return continue f_153([f_153, test]); 42; };
+    return a;
+  }
+
+  function test() {
+    assertEquals(153, g1());
+  }
+  test();
+  test();
+  %OptimizeFunctionOnNextCall(test);
+  test();
+})();