Error information and JavaScriptException

Mar 8, 2014 at 11:01 AM
First of all, thank you for this library. I've found it very useful and very easy to integrate.

One thing I found, however, is that error information is not always as accessible as I'd hope.
If I make a little test script:
// test.js

blarg();
And then a little program to run it:
static void Main(string[] args)
{
    ScriptEngine engine = new ScriptEngine();
    try
    {
        engine.ExecuteFile("test.js");
    }
    catch (JavaScriptException e)
    {
        System.Console.WriteLine(String.Format("Exception on line {0} of \"{1}\": {2}",
            e.LineNumber, e.SourcePath, e.Message));
    }
    System.Console.ReadLine();
}
I get a nice error message:
Exception on line 3 of "test.js": ReferenceError: blarg is not defined
But now if I define the blarg() function:
engine.SetGlobalFunction("blarg", new Action(() => { throw new JavaScriptException(engine, "Error", "Blarg!"); }));
The error becomes much less nice:
Exception on line 0 of "native": Error: Blarg!
Doing some digging, I found that the file and line information I'd like to get back is present in the stack information inside the ScriptEngine, but it doesn't seem to be exposed in any way.

So I've done a rather ugly hack
class DetailedException : JavaScriptException
{
    public DetailedException(ScriptEngine engine, string name, string message)
        : base(engine, name, message)
    {
        // ScriptEngine.stackFrames has the info we're after, but it's private
        // with no way to access it.  So here comes the reflection.
        FieldInfo stackInfo = typeof(ScriptEngine).GetField("stackFrames", BindingFlags.Instance | BindingFlags.NonPublic);
        if (stackInfo == null) { return; }
        object stack = stackInfo.GetValue(engine);

        MethodInfo peekInfo = stack.GetType().GetMethod("Peek");
        if (peekInfo == null) { return; }
        object frame = peekInfo.Invoke(stack, null);

        FieldInfo lineInfo = frame.GetType().GetField("Line");
        if (lineInfo != null)
        {
            // It would be better to set this.LineNumber, but the settor is
            // internal so its contents are duplicated here
            this.Data["LineNumber"] = lineInfo.GetValue(frame);
        }

        FieldInfo pathInfo = frame.GetType().GetField("Path");
        if (pathInfo != null)
        {
            this.Data["SourcePath"] = pathInfo.GetValue(frame);
        }
    }
}
Is there some hidden danger here? Is there a reason this kind of functionality isn't already in the library? (Or is it there and I missed it?)