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 Expression
s 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.