Home > DeveloperSection > Forums > Saving C# Expression Tree to a File
marcel ethan
marcel ethan

Total Post:105

Points:735
Posted on    February-09-2014 11:11 PM

 C# C# 
Ratings:


 1 Reply(s)
 1564  View(s)
Rate this:

I want to debug an expression and save the expression tree to a file:

 

var generator = DebugInfoGenerator.CreatePdbGenerator();

var document = Expression.SymbolDocument(fileName: "MyDebug.txt");

var debugInfo = Expression.DebugInfo(document, 6, 9, 6, 22);

var expressionBlock = Expression.Block(debugInfo, fooExpression);

var lambda = Expression.Lambda(expressionBlock, parameters);

lambda.CompileToMethod(method, generator);

var bakedType = type.CreateType();

return (type)bakedType.GetMethod(method.Name).Invoke(null, parameters);

How can I find or save "MyDebug.txt"?



Pravesh Singh

Total Post:411

Points:2881
Posted on    February-09-2014 11:42 PM

Hi Marcel,

You didn't comprehend what SymbolDocument() is...

var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("foo"), System.Reflection.Emit.AssemblyBuilderAccess.RunAndSave);

var mod = asm.DefineDynamicModule("mymod", true);

var type = mod.DefineType("baz", TypeAttributes.Public);

var method = type.DefineMethod("go", MethodAttributes.Public | MethodAttributes.Static);

Expression fooExpression = Expression.Divide(Expression.Constant(0), Expression.Constant(0));

var parameters = new ParameterExpression[0];

var generator = DebugInfoGenerator.CreatePdbGenerator();

var document = Expression.SymbolDocument(fileName: "MyDebug.txt");

var debugInfo = Expression.DebugInfo(document, 6, 9, 6, 22);

var expressionBlock = Expression.Block(debugInfo, fooExpression);

var lambda = Expression.Lambda(expressionBlock, parameters);

lambda.CompileToMethod(method, generator);

var bakedType = type.CreateType();

Func<int> method2 = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), bakedType.GetMethod(method.Name));

try

{

    int res = method2();

}

catch (Exception e)

{

    Console.WriteLine(e.StackTrace);

}

The Expression I generated is something like return 0/0;. I execute it and trap the exception (note that I don't use the Invoke method, because the Invoke method would put the DivisionByZeroException as the InnerException).

The stack trace I output is something like:

in baz.go() in MyDebug.txt:row 6 in ConsoleApplication85.Program.Test()

You see the MyDebug.txt:row 6? They are your SymbolDocument and your DebugInfo.

A more complete example, taken from Debugging Dynamically Generated Code (Reflection.Emit).

static void Test()

{

    // create a dynamic assembly and module

    AssemblyName assemblyName = new AssemblyName("HelloWorld");

    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);

    // Mark generated code as debuggable.

    // See http://blogs.msdn.com/rmbyers/archive/2005/06/26/432922.aspx for explanation.       

    Type daType = typeof(DebuggableAttribute);

    ConstructorInfo daCtor = daType.GetConstructor(new Type[] { typeof(DebuggableAttribute.DebuggingModes) });

    CustomAttributeBuilder daBuilder = new CustomAttributeBuilder(daCtor, new object[] {

    DebuggableAttribute.DebuggingModes.DisableOptimizations |

    DebuggableAttribute.DebuggingModes.Default });

    assemblyBuilder.SetCustomAttribute(daBuilder);

    ModuleBuilder module = assemblyBuilder.DefineDynamicModule("HelloWorld.exe", true); // <-- pass 'true' to track debug info.

    var doc = Expression.SymbolDocument(@"Source.txt");

    // create a new type to hold our Main method

    TypeBuilder typeBuilder = module.DefineType("HelloWorldType", TypeAttributes.Public | TypeAttributes.Class);

    // create the Main(string[] args) method

    MethodBuilder methodbuilder = typeBuilder.DefineMethod("MyMethod", MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { typeof(string[]) });

    // Create a local variable of type 'string', and call it 'xyz'

    var localXYZ = Expression.Variable(typeof(string), "xyz"); // Provide name for the debugger.

    var generator = DebugInfoGenerator.CreatePdbGenerator();

    var debugInfo1 = Expression.DebugInfo(doc, 2, 1, 2, 100);

    // Emit sequence point before the IL instructions. This is start line, start col, end line, end column,

    // Line 2: xyz = "hello";

    var assign = Expression.Assign(localXYZ, Expression.Constant("Hello world!"));

    // Line 3: Write(xyz);

    MethodInfo infoWriteLine = typeof(System.Console).GetMethod("WriteLine", new Type[] { typeof(string) });

    var debugInfo2 = Expression.DebugInfo(doc, 3, 1, 3, 100);

    var write = Expression.Call(infoWriteLine, localXYZ);

    // Line 4: return;

    var debugInfo3 = Expression.DebugInfo(doc, 4, 1, 4, 100);

    var block = Expression.Block(new ParameterExpression[] { localXYZ }, new Expression[] { debugInfo1, assign, debugInfo2, write, debugInfo3 });

    var lambda = Expression.Lambda<Action<string[]>>(block, Expression.Parameter(typeof(string[])));

    // bake it

    lambda.CompileToMethod(methodbuilder, generator);

    Type helloWorldType = typeBuilder.CreateType();

    // This now calls the newly generated method. We can step into this and debug our emitted code!!

    var dm = (Action<string[]>)Delegate.CreateDelegate(typeof(Action<string[]>), helloWorldType.GetMethod("MyMethod"));

    dm(new string[] { null }); // <-- step into this call

}

Save this somewhere as Source.txt

  // Test

  xyz = "hello";

  Write(xyz);

  return;

then put a breakpoint on the dm(new string[] { null }), and press F11 when on the bp. It will ask you for the Source.txt. Note that if you don't select it, you'll have to delete the .suo file to get the window a second time.

Note the use of the DebuggableAttribute. It seems it's the trick to mark the dynamic assembly as debuggable.


Don't want to miss updates? Please click the below button!

Follow MindStick