Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making a CLR/.NET Language Debuggable

What are some resources for making a CLR/.NET language debuggable? I'm developing an ActionScript 3 to IL compiler, which uses DLR CallSites and CallSiteBinders to handle the dynamic aspects of the otherwise static programming language. I'm looking for any information on making the emitted IL map back to the source code, and I'd also like to know how I can make the dynamic call sites map back as well.

So this is ultimately two questions:

  • How can I make the IL debuggable?
  • How can I make the DLR call sites debuggable?

Any help would be greatly appreciated!

What I'm looking for in terms of "debuggabilty"

Within attached Visual Studio instance:

  • Step through code
  • View locals
  • View stack trace
like image 605
Charles Avatar asked Dec 05 '10 04:12

Charles


1 Answers

To make IL debuggable you need to compile the code into a debuggable assembly. There's also an immediate downside in that the assembly will not be collectible by GC. To do this you do AppDomain.CurrentDomain.DefineDynamicAssembly, then you call DefineDynamicModule and define a module in the assembly. To make it debuggable you need to set some attributes on it:

DebuggableAttribute.DebuggingModes attrs =
    DebuggableAttribute.DebuggingModes.Default |
    DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
    DebuggableAttribute.DebuggingModes.DisableOptimizations;

Type[] argTypes = new Type[] { typeof(DebuggableAttribute.DebuggingModes) };
Object[] argValues = new Object[] { attrs };

_myAssembly.SetCustomAttribute(new CustomAttributeBuilder(
   typeof(DebuggableAttribute).GetConstructor(argTypes), argValues)
);

_myModule.SetCustomAttribute(new CustomAttributeBuilder(
    typeof(DebuggableAttribute).GetConstructor(argTypes), argValues)
);

Finally while emitting the IL you call MarkSequencePoint to mark the lines for the following IL instructions.

Making DLR call sites debuggable seems weird to me – generally your call site is not going to contain any user code. Rather it will contain code to perform an operation and there is no source code associated with that code. But let's say you really want to have something to step through that's associated with the expression trees you're generating for a call site. To do that you'll need to do two things. First is store the debugging info in the expression tree – you do this using a DebugInfoExpression. The next is compiling the method into a debuggable method and providing that delegate to the DLR.

For compiling the method you need to use LambdaExpression<T>.CompileToMethod. The MethodBuilder that you'll need to provide will need to be a static method defined in a type in the debuggable assembly that you created earlier.

For providing that delegate to the DLR you have two options. Probably the easiest would be to actually return an expression which invokes the compiled debuggable delegate (just holding onto it via constant). The harder but in some ways more elegant way would be to override BindDelegate<T> on the call site and return the compiled delegate. That starts getting into creating appropriate argument Expressions and calling the Bind* methods to produce the expression tree your self though.

All of this is done in the DLR outer layer/IronPython/IronRuby – all available at ironpython.codeplex.com. You can look at CompilerHelpers.CompileToMethod as an example of doing the compilation, the Snippets class (and the associated AssemblyGen/TypeGen/ILGen classes for creating the debuggable assemblies, and even the DLR expression tree compiler (in Runtime\Microsoft.Scripting.Core\Compiler) for an example of emitting the line info.

like image 83
Dino Viehland Avatar answered Oct 18 '22 02:10

Dino Viehland