Generating Assemblies at runtime using IL emit

Let’s start with a bold statement: IL rocks! Fair enough, but can it do Jazz or Mozart too? Turns out, yes it can :)

While thinking of a way to magically create an instance out of an interface for my little MData project, I figured i’d have to go with IL generating such a class. It seemed like a daunting task at first. Really, look at this code:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Offset OpCode
0 ldnull
1 ldarg.0
2 ldc.i4.2
3 newarr System.Object
8 stloc.0
9 ldloc.0
10 ldc.i4.0
11 ldarg.1
12 stelem.ref
13 ldloc.0
14 ldc.i4.1
15 ldarg.2
16 stelem.ref
17 ldloc.0
18 call System.String System.String::Format(System.IFormatProvider,System.String,System.Object[])
23 ret
view raw gistfile1.txt hosted with ❤ by GitHub

Can you guess which method this is? probably not, well here it is:

1 2 3 4 5 6 7 8
public static string Format(string format, object arg0, object arg1)
{
return Format(null, format, new object[] { arg0, arg1 });
}
 
 
view raw gistfile1.cs hosted with ❤ by GitHub

If you’ve never really looked into IL (or MSIL) this code may seem very hard to understand. Very handy tools to look at the IL code of a given .Net method, are ILSpy (free), Reflector (in combination with Reflexil) or Graywolf (free). These tools let you compare the classic C#/Vb.net code to the compiled IL code. This is the best way to ‘learn’ IL.

Generating a dynamic assembly at runtime

This goes just one step further then just looking at IL code, this is actually writing IL code. While it is not possible to alter existing Assemblies (not in my knowledge), it is certainly possible to create a new one. This is used in several popular frameworks which support creating proxy classes, ducktyping etc. The key here is to *not* use the default System.Reflection.Emit namespace :) . I personally don’t like the structure and approach in the standard BCL, instead I love to use BLToolkit from Igor Tkachev. It allows me to create a ‘Hello world’ example using a very natural flow:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
EmitHelper emit = new AssemblyBuilderHelper("HelloWorld.dll")
.DefineType ("Hello", typeof(object), typeof(IHello))
.DefineMethod(typeof(IHello).GetMethod("SayHello"))
.Emitter;
 
emit
// string.Format("Hello, {0}!", toWhom)
//
.ldstr ("Hello, {0}!")
.ldarg_1
.call (typeof(string), "Format", typeof(string), typeof(object))
 
// Console.WriteLine("Hello, World!");
//
.call (typeof(Console), "WriteLine", typeof(string))
.ret()
;
view raw gistfile1.cs hosted with ❤ by GitHub

And that’s it, we created a new assembly with one type containing one method. As you can see we can define a baseclass for the type, we could also add which interfaces to implement etc.

TIP: To put a certain class in a namespace, define the name as follows: Path.Of.Your.Namespace.[GeneratedTypeName], this will trigger the emit system to extract the namespace from the class name.

Now let’s try to make a working example of a ducktyping system. the purpose of our little library will be to cast any given class to a interface it doesn’t explicitly implement. Something like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
class Program
{
static void Main(string[] args)
{
A a = new A();
ITest aAsITest = a.Duck<ITest>();
}
}
 
public class A
{
string Data { get; set; }
 
public void Method()
{
}
}
public interface ITest
{
string Data { get; set; }
void Method();
}
view raw gistfile1.txt hosted with ❤ by GitHub

The notice how class A does not implement ITest, after ‘ducking’ however, I can have an instance of ITest working on the A instance.
The first step would be creating some kind of workflow and scope for this project:

  • The targetType should have all members of the interface defined as public
  • We will not take into account that only a subset of members is implemented by the targetType, so only one on one matches
  • We will map methods, properties and events.

First step is actually creating an ‘AssemblyBuilder’ from the BLToolkit:

1 2 3 4 5 6 7 8 9
static ClassFactory()
{
_assemblyBuilder = new AssemblyBuilderHelper(GetGeneratedAssemblyPath());
}
 
private static string GetGeneratedAssemblyPath()
{
return @".\Duck.Tape.Generated.dll";
}
view raw gistfile1.txt hosted with ❤ by GitHub

That was easy! Now here is the flow of creating all methods, properties and event on the newly generated type:

1 2 3 4 5 6 7 8 9 10 11 12
var typeBuilder = _assemblyBuilder.DefineType(GetGeneratedTypeName(), typeof(object), InterfaceToImplement);
 
//create private field
var classToWrapField = typeBuilder.DefineField(GetGeneratedToWrapFieldName(), ClassToWrap, FieldAttributes.Private);
 
//map interface members
MapInterfaceProperties(classToWrapField, typeBuilder);
MapInterfaceEvents(classToWrapField, typeBuilder);
MapInterfaceMethods(classToWrapField, typeBuilder);
 
//define constructor
DefineConstructor(classToWrapField, typeBuilder);
view raw gistfile1.txt hosted with ❤ by GitHub

As you can see, we have a clear overview of what is happening during the creation of our new Type. This is very important when dealing with the whole Emit thing. Keep things as simple as possible, and please don’t create methods larger then 20-25 lines. It can get real messy, real fast.

Now for some IL magic, let’s look at how the constructor is defined:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
private void DefineConstructor(FieldInfo classToWrapField, TypeBuilderHelper typeBuilder)
{
typeBuilder
//take an instance of 'ClassToWrap' as constructor parameter
.DefinePublicConstructor(ClassToWrap)
.Emitter
//: base()
.ldarg_0
.call(typeof(object).GetConstructors()[0])
//this.[fieldName] = [constructorParameter]
.ldarg_0
.ldarg_1
.stfld(classToWrapField)
//return
.ret();
}
view raw gistfile1.txt hosted with ❤ by GitHub

Most important thing here is that ldarg_0 will always be the equivalent of ‘this’ in a non static environment. For an instance method, parameter loading starts with ldarg_1.

Last example is mapping methods of the interface to methods of the targetType:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
private void MapInterfaceMethods(FieldInfo classToWrapField, TypeBuilderHelper typeBuilder)
{
//loop al interface methods
foreach (var methodInfo in InterfaceToImplement.GetMethods())
{
//define the method in the generated type
var emitter = typeBuilder.DefineMethod(methodInfo)
.Emitter
//this.[fieldName]
.ldarg_0
.ldfld(classToWrapField);
 
for (var index = 0; index < methodInfo.GetParameters().Length; index++)
//pushs all method arguments on the stack, so we can use them to
//call the targetMethod
emitter.ldarg(index + 1);
 
emitter
//this.[fieldName].[MethodToImplement]([parameters])
.callvirt(GetMappedMethod(methodInfo))
//return
.ret();
}
}
view raw gistfile1.txt hosted with ❤ by GitHub

If you are interested in this library, please download/fork it here!

Please don’t send me e-mails that is doesn’t work for you, just open an issue on github, or create a pull request and solve it yourself ;)

Share On

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">