function callerMustBeRun() { if (!Object.is(callerMustBeRun.caller, runTests)) throw new Error("Wrong caller, expected run but got ", callerMustBeRun.caller); } function callerMustBeStrict() { var errorThrown = false; try { callerMustBeStrict.caller; } catch (e) { errorThrown = true; } if (!errorThrown) throw Error("Wrong caller, expected strict caller but got ", callerMustBeStrict.caller); } function runTests() { // Statement tests (function simpleTailCall() { "use strict"; return callerMustBeRun(); })(); (function noTailCallInTry() { "use strict"; try { return callerMustBeStrict(); } catch (e) { throw e; } })(); (function tailCallInCatch() { "use strict"; try { } catch (e) { return callerMustBeRun(); } })(); (function tailCallInFinally() { "use strict"; try { } finally { return callerMustBeRun(); } })(); (function tailCallInFinallyWithCatch() { "use strict"; try { } catch (e) { } finally { return callerMustBeRun(); } })(); (function tailCallInFinallyWithCatchTaken() { "use strict"; try { throw null; } catch (e) { } finally { return callerMustBeRun(); } })(); (function noTailCallInCatchIfFinally() { "use strict"; try { throw null; } catch (e) { return callerMustBeStrict(); } finally { } })(); (function tailCallInFor() { "use strict"; for (var i = 0; i < 10; ++i) return callerMustBeRun(); })(); (function tailCallInWhile() { "use strict"; while (true) return callerMustBeRun(); })(); (function tailCallInDoWhile() { "use strict"; do return callerMustBeRun(); while (true); })(); (function noTailCallInForIn() { "use strict"; for (var x in [1, 2]) return callerMustBeStrict(); })(); (function noTailCallInForOf() { "use strict"; for (var x of [1, 2]) return callerMustBeStrict(); })(); (function tailCallInIf() { "use strict"; if (true) return callerMustBeRun(); })(); (function tailCallInElse() { "use strict"; if (false) throw new Error("WTF"); else return callerMustBeRun(); })(); (function tailCallInSwitchCase() { "use strict"; switch (0) { case 0: return callerMustBeRun(); } })(); (function tailCallInSwitchDefault() { "use strict"; switch (0) { default: return callerMustBeRun(); } })(); (function tailCallWithLabel() { "use strict"; dummy: return callerMustBeRun(); })(); (function tailCallInTaggedTemplateString() { "use strict"; return callerMustBeRun`test`; })(); // Expression tests, we don't enumerate all the cases where there // *shouldn't* be a tail call (function tailCallComma() { "use strict"; return callerMustBeStrict(), callerMustBeRun(); })(); (function tailCallTernaryLeft() { "use strict"; return true ? callerMustBeRun() : unreachable(); })(); (function tailCallTernaryRight() { "use strict"; return false ? unreachable() : callerMustBeRun(); })(); (function tailCallLogicalAnd() { "use strict"; return true && callerMustBeRun(); })(); (function tailCallLogicalOr() { "use strict"; return false || callerMustBeRun(); })(); (function memberTailCall() { "use strict"; return { f: callerMustBeRun }.f(); })(); (function bindTailCall() { "use strict"; return callerMustBeRun.bind()(); })(); // Function.prototype tests (function applyTailCall() { "use strict"; return callerMustBeRun.apply(); })(); (function callTailCall() { "use strict"; return callerMustBeRun.call(); })(); // No tail call for constructors (function noTailConstruct() { "use strict"; return new callerMustBeStrict(); })(); } for (var i = 0; i < 10000; ++i) runTests();