I'm not familiar with F# but I did write an ECMAScript-262 v.5 interpreter in C# so I can relate to some of your issues. As you know, the StackOverFlowException can't be caught in .NET apps since v2.0. There is a fairly reliable workaround though and it's fast.
If you declare a variable on the stack, for instance an int, the address of that variable represents the top of the stack and lets you know how much space is left. So, if you record that variable at startup while the stack is basically empty, you can reference it each time you enter a new execution context.
Here are some exerpts from my interpreter that address this issue.
C#:
These are static variables declared in the main Interpreter class.
private static int TopOfStack;
private const int STACK_SIZE = 1000000;
This is the static constructor of the main Interpreter class.
static Interpreter() {
InitializeGlobalEnvironment();
//---------------------------------------------------
// Get the address of a new variable allocated on the stack
// to represent the amount of memory available. Record
// the address.
//---------------------------------------------------
int stackVariable;
TopOfStack = (int)&stackVariable;
}
This code gets called before an ECMAScript function is interpreted. If the address of a new stack allocated variable is less than short.Max, I throw the catchable exception. You need to leave some space for the call stack to unwind.
internal static ExecutionContext EnterFunctionContext(IValue thisArg, LIST args, FUNCTION function) {
...
LexicalEnvironment localEnv = ECMA.NewDeclarativeEnvironment(function.Scope);
ExecutionContext context = new ExecutionContext() {
Strict = function.IsStrict,
VariableEnvironment = localEnv,
LexicalEnvironment = localEnv
};
int remainingStackSpace;
if (STACK_SIZE - (TopOfStack - (int)&remainingStackSpace) < short.MaxValue)
throw new ECMARuntimeException("stack overflow", RuntimeErrorType.RangeError);
CallStack.Push(context);
LexicalEnvironment env = CurrentContext.VariableEnvironment;
...
}
When the following code is interpreted, the exception is thrown around iteration 1200.
Update: In release build it is around 4100 iterations.
ECMAScript:
RecursiveCall(0);
function RecursiveCall(counter){
return RecursiveCall(++counter);
}
Output: RangeError: stack overflow
You could increase the stack size in the Thread using the Thread(ParameterizedThreadStart, Int32)
constructor. I just didn't feel the need.
Good luck with your project. I hope this helps.