On 10/18/2012 2:22 AM, Dmitry A. Kazakov wrote:
> On Wed, 17 Oct 2012 21:25:28 -0500, BGB wrote:
>> On 10/17/2012 8:01 PM, Andy Walker wrote:
>>> On 18/10/12 00:05, BGB wrote:
>>> [...]
>>>> throw a "StackOverflowException"?...
>>>> like, in script code:
>>>> try {
>>>> ...
>>>> }catch(ex:StackOverflowException)
>>>> {
>>>> printf("Stack blew up!\n");
>>>> }
>>> If you've just run out of storage, then calling a
>>> procedure-with-parameter, which may itself require large
>>> amounts of storage, is probably not a good idea. OK, you
>>> could reserve a small amount of storage specifically for
>>> "catching", but then the "catch" isn't normal user code,
>>> which has its own problems. You would get away with it
>>> [probably!] if the "try" uses large amounts of stack which
>>> is recovered by the "throw", but that's not the general
>>> case.
>> presumably, this try/catch block would occur outside of the code which
>> used up all the stack-space and triggered the exception, and also the
>> exception handling does not itself use up stack space (throwing
>> basically causes all stack-frames to be unwound until it gets back to
>> the exception handler, and the exception-object is heap-allocated...).
> A counterexample:
> {
> int Boo; // Allocated on the stack
> try
> { ...
> }
> catch (ex:StackOverflowException)
> {
> printf("Stack blew up when Boo=%d!\n", Boo);
> } }
> If you switch stacks in exception handlers, you would have a problem with
> accessing the stuff allocated on the stack by the enclosing context.
the variable declaration syntax would be more like:
var boo:int;
but, the stack issue is not actually an issue.
this is partly because local variables are not "only" stored on the
stack, but any captured bindings (due to a closure or otherwise) will
typically end up in heap-allocated environment frames.
actually, even then, within the current interpreter, variables have
their own stack, and there are parallel stacks for various tasks:
value-stack (holds intermediate values);
mark-stack (holds "marks" for the value-stack);
flow-control stack (holds call frames);
local variable stack (holds non-captured local variables and similar);
dynamic variable stack (holds any dynamically-scoped variables);
...
actually, the bytecode makes the lexical environment itself look like a
single big stack, except that this stack is not physically contiguous in
memory whenever closures are made (the lexical environment is more of a
linked-list structure).
currently, different threaded code is generated based on whether or not
the current function has captured bindings or is itself a closure
(captures bindings from a parent function).
in the case of FRIR, it would actually keep track of the difference
between lexical and local variables, and identifies lexical variables
via a pair of numbers (indicating how many levels to walk outwards, and
the binding within that level).
ex: "3, 8" means "up 3 levels, entry 8".
currently a stack-overflow exception doesn't actually indicate which
stack overflowed.
> Anyway. Handling exceptions in this way is much like co-routine calls. A
> co-routine may have stack of its own. I wonder what could become of that.
> (Not returning to on-clauses of early PL/1 of course.)
actually, it is not that the exception handling has its own stack, but
rather that "printf" has its own stack.
this is mostly because the current interpreter is internally implemented
using CPS (continuation-passing-style), and uses its own internal
stacks. similarly, calls within the VM don't actually use any more space
on the CPU stack, mostly because in C land, it is all just a single
trampoline loop which spins in place (so, the interpreter is not
recursive in this case).
when a call is made into C land (such as for "printf()"), it will use
the native CPU stack as the stack, rather than the stack internal to the
interpreter.