javascriptoptimizationwebkittail-recursion

Is it possible to detect tail call optimization in WebKit?


I have a recursive function and exhausting the call stack is an issue I run into sometimes. I know I can use streams, promises with setTimeout, but I would like to just write code that triggers tail call optimization. So far only Webkit seem to have implemented tail call optimization (TCO). Other than knowing the theory, is there any way do check if my code will trigger TCO, either with devtools or by examining the output of the Webkit compiler?


Solution

  • I know it's not perfect, but I think it's the best option we have currently.

    Consider this function:

    "use strict";
    
    function detectTCO() {
        const outerStackLen = new Error().stack.length;
        // name of the inner function mustn't be longer than the outer!
        return (function inner() {
            const innerStackLen = new Error().stack.length;
            return innerStackLen <= outerStackLen;
        }());
    }
    

    Error.stack is not standard, but it has basic support in all modern browsers. It has different values in different browsers, nonetheless, when a longer named function tail-calls a shorter named one, the stack trace:

    As of now, this returns true for Safari, and false for other browsers, which is the current TCO support.

    In node REPL, Error.stackTraceLimit is 10 by default, and that is already full by the time detectTCO is called. So, when the inner stack frame is pushed to the top, the bottom frame is pushed out, so the overall length can become smaller. To prevent this, either increase the limit by Error.stackTraceLimit = 30; or run detectCTO with a cleanish stack by calling it from a setTimeout for example.

    "use strict";
    
    function detectTCO() {
        const outerStackLen = new Error().stack.length;
        // name of the inner function mustn't be longer than the outer!
        return (function inner() {
            const innerStackLen = new Error().stack.length;
            return innerStackLen <= outerStackLen;
        }());
    }
    
    document.getElementById('result').innerText = detectTCO() ? 'Yes' : 'No';
    #result {
        color: blue;
        font-weight: bold;
    }
    Is TCO available? 
    <div id="result"></div>