Search code examples
javascriptcrecursionstackcalling-convention

Could a brute-force algorithm cause a stack overflow? (recursion)


So let's say we have a recursive brute-force function.
(I especially wondering about brute-force functions because they can easily call themselves a million times recursively.)
Like this, for example:

function BruteForce(chars, min, max, prefix, stage) {
    for (var i = 0, len = chars.length; i < len; i++) {
        if (stage >= min-1)
            console.log(prefix + chars[i])
        if (stage < max-1)
            BruteForce(chars, min, max, prefix + chars[i], stage+1)
   }
}

BruteForce("abc", 1, 5, "", 0)

(Consider this pseudo code for now, as my question isn't about JavaScript only.)

Wouldn't a function like that fill the stack with new arguments until it overflows?

What would happen if you'd execute something like that in C/C++ and the like?
Does the calling convention matter? How well would the different calling conventions be able to handle this?

And why doesn't the JavaScript code above cause a stack overflow or so?


Solution

  • Let's try to answer your questions one by one:

    Wouldn't a function like that fill the stack with new arguments until it overflows?

    Under some circumstances yes. It depends on the arguments themselves and how the stack is handled, which is very implementation-dependent.

    What would happen if you'd execute something like that in C/C++ and the like?

    In general, C/C++ don't provide much in the way of "stack handling". Stack overflow is undefined behaviour.

    On embedded systems, you basically allocate some memory to the stack and if you overflow it... well, you keep writing over some other part of memory (heap, static data...). Eventually, you will run out of available memory, and what happens then depends on the CPU and your runtime - a crash, stall, or some such thing.

    On a hosted system (Windows, POSIX...), stack overflow will probably involve a page fault exception, which may prompt the runtime to allocate new pages and add them to the stack, if possible. But, only if your runtime works like that, it might as well behave like an embedded system, or simply not handle the page fault (thus your program will crash).

    Does the calling convention matter? How well would the different calling conventions be able to handle this?

    Not much, as this pretty much has to use a calling convention that puts arguments on the stack. Which is first and which is last doesn't really matter (neither does do you put an underscore before the name of the function). A "register" calling convention ("fast call" or whatever your compiler calls it) will either be ignored for a recursive function, or simply the parameters will be put on stack, then copied to registers and then the function will start.

    Even on sliding register window CPUs (like SPARC), it won't matter, as the register window is mostly shallow, and you will again get back to using the stack.

    And why doesn't the JavaScript code above cause a stack overflow or so?

    AFAICT, the code above doesn't have a very deep stack "nesting" (you "nest" as high as max and it is 5), and since pretty much anything in Javascript is an int, double or pointer (from a low-level memory POV), your five parameters don't occupy much memory (probably ~40 bytes). Now, if you would to increase the max to something like 2^31, I'm guessing there could be trouble. :)

    Also, keep in mind that a Javascript interpreter may do funky things, like figure out that you never change chars, min or max and simply don't put them on a stack each time (thus saving space). In theory, even C/C++ compiler might do that, but only as a part of some "Whole program optimization".