Search code examples
c#roslynroslyn-code-analysis

Exception line number in CSharpScript.EvaluateAsync


I'm evaluating an script using the CSharpScript.EvaluatyAsync<T> method and passing some C# code. I can easily see the line number of errors when there is a parsing issue, e.g. a syntax error, but when there is a runtime exception all I get is an AggregateException wrapping my exception (NullReferenceException) in this case, but there is no clue of how to get the line number for me (3 in this example below).

Console.WriteLine(CSharpScript.EvaluateAsync<int>(
    @"string s = null; 
// some comment at line 2
var upper = s.ToUpper(); // Null reference exception at line 3
// more code").Result);

EDIT:

I've been researching this and found that the Scripting API creates an assembly without pdb information here line 127 so this will be impossible to know where the exception happened. Am I right?


Solution

  • In some version of CSharpScript the team added a solution: now you can add ScriptOptions.Default.WithEmitDebugInformation(true) to the EvaluateAsync method.

    See my test cases below on how to extract the exception line number:

    [TestMethod]
    public void LineNumberInStackTrace()
    {
        try
        {
            var result = CSharpScript.EvaluateAsync<int>(
                @"string s = null; 
    // some comment at line 2
    var upper = s.ToUpper(); // Null reference exception at line 3
    // more code", ScriptOptions.Default.WithEmitDebugInformation(true)).Result;
    
        }
        catch (AggregateException e)
        {
            if (e.InnerException is NullReferenceException inner)
            {
                var startIndex = inner.StackTrace.IndexOf(":line ", StringComparison.Ordinal) + 6;
                var lineNumberStr = inner.StackTrace.Substring(
                    startIndex, inner.StackTrace.IndexOf("\r", StringComparison.Ordinal) - startIndex);
                var lineNumber = Int32.Parse(lineNumberStr);
    
                Assert.AreEqual(3, lineNumber);
                return;
            }
        }
        Assert.Fail();
    }
    [TestMethod]
    public void LineNumberNotInStackTrace()
    {
        try
        {
            var result = CSharpScript.EvaluateAsync<int>(
                @"string s = null; 
    // some comment at line 2
    var upper = s.ToUpper(); // Null reference exception at line 3
    // more code").Result;
    
        }
        catch (AggregateException e)
        {
            if (e.InnerException is NullReferenceException inner)
            {
                var startIndex = inner.StackTrace.IndexOf(":line ", StringComparison.Ordinal);
    
                Assert.AreEqual(-1, startIndex);
                return;
            }
        }
        Assert.Fail();
    }