August 16, 2007

DynamicMethod, IL Code, CreateDelegate

In a previous post, dotNET - Under the Hood, IL Assembly, I briefly described how to write some IL Code to an Assembly, but what if at a later occasion you'll want to invoke these methods from the dll.
This could be achieved by loading the assembly in an AppDomain and then invoking the methods within ????
Or you could use what are known as DynamicMethods to invoke the methods.

DynamicMethod is a class that also resides in the System.Reflection.Emit namespace. Why would you want to use DynamicMethod? it's useful when you want to load methods into memory or write methods in memory and invoke later.
We can write IL code in memory in the exact same was as we wrote IL code earlier using the MethodBuilder.

In my experience DynamicMethod is a bit tricky to get right especially when you try to invoke your Dynamic method. The problem I found was with the constructor parameters, what do they mean;
There are 6 constructor overloads in all;
half of these are associated with an class instance i.e. you supply a Type as one of the constructor parameters, the significance of this is that it gives you access to reference private data within the type.
The other half of the constructor overloads are associated with the Module i.e. the dll and not a type, the significance of this is that it does not give you access to private types data.
from msdn:
DynamicMethod (String, Type, Type[], Module) Creates a dynamic method that is global to a module, specifying the method name, return type, parameter types, and module.
DynamicMethod (String, Type, Type[], Type) Creates a dynamic method, specifying the method name, return type, parameter types, and the type with which the dynamic method is logically associated.

There's also another caveat which is a boolean option to skip the JIT checking. This means some verification of the parameter types which is usually done by the JIT is skipped.
DynamicMethod (String, Type, Type[], Module, Boolean) Creates a dynamic method that is global to a module, specifying the method name, return type, parameter types, module, and whether just-in-time (JIT) visibility checks should be skipped for members of all types in the module.
DynamicMethod (String, Type, Type[], Type, Boolean) Creates a dynamic method, specifying the method name, return type, parameter types, the type with which the dynamic method is logically associated, and whether just-in-time (JIT) visibility checks should be skipped for members of other types in the module.

And there's one more caveat, CallingConvention,......

DynamicMethod dynamicMethod = new DynamicMethod(

"MyMethod"),
typeof(object),
new Type[] { typeof(object[]), typeof(object[]) },
typeof(MyClass));

The instance of DynamicMethod results in a method with the following signature
MyClass
{
object MyMethod(object[], object[])
{
}
}
You now have to insert your own IL code is the methods like Emit, again see my other post, dotNET - Under the Hood, IL Assembly, you write the IL code in the same manner.

Now you want to Invoke the method you've just created. This can be done in 2 ways, one call Invoke on your instance i.e.
dynamicMethod.Invoke();
or create a Delegate and then call Invoke on that delegate. The advantage of this over the first option is that you supply the correct parameters here and then just call Invoke on it later, the first option requires you to call Invoke here and now.
I'll explain the second option here:
DynamicMethod.CreateDelegate is a method used to invoke a piece of IL code. It creates a Delegate as the name suggests. The Delegate must have the identical signature to the DynamicMethod instance which is calling CreateDelegate.

public delegate object CompiledMethod(object[] arguments);

//As the delegate above defines, our DynamicMethod must return an object, and take an array of objects as parameters, it's also tied to the type "Example".
dynamicMethod = new DynamicMethod(
"",
typeof(object),
new Type[] { typeof(Example), typeof(object) },
typeof(Example)
);

// Get a FieldInfo for the private field 'id'.
FieldInfo dynamicMethodFid = typeof(Example).GetField(
"id",
BindingFlags.NonPublic | BindingFlags.Instance
);

ILGenerator dynamicMethodIlg = d.dynamicMethod.GetILGenerator();

dynamicMethodIlg.Emit(OpCodes.Ldarg_0);
dynamicMethodIlg.Emit(OpCodes.Ldfld, dynamicMethodFid);
dynamicMethodIlg.Emit(OpCodes.Ldarg_0);
dynamicMethodIlg.Emit(OpCodes.Ldarg_1);
dynamicMethodIlg.Emit(OpCodes.Stfld, dynamicMethodFid);
dynamicMethodIlg.Emit(OpCodes.Ret);

CompiledMethod result = (CompiledMethod)d.dynamicMethod.CreateDelegate(typeof(CompiledMethod), d.ex);

The CreateDelegate has 2 constructor overloads, one which takes and instance of an object as the second parameter (Instance) and another which does not (Static).
The difference being at invokation time, if you created your delegate using the instance method you do not have to supply the instance at invokation time.
If you created your delegate using the Static method you must supply the instance at invokation time e.g.

Instance
public delegate int UseLikeInstance(int newID);//delegate declaration,i.e. when your invoking the delegate you must supply a value only, no instance.
UseLikeInstance uli = (UseLikeInstance) d.changeID.CreateDelegate(typeof(UseLikeInstance, d.ex));//instantiation, instance required

uli(1492); //Invokation, no instance

Static
public delegate int UseLikeStatic(Example ex, int newID);//delegate declaration,i.e. when your invoking the delegate you must supply an Instance and a value.
UseLikeStatic uls = (UseLikeStatic) d.changeID.CreateDelegate(typeof(UseLikeStatic));//instantiation, NO instance required
uls(d.ex, 1492); //Invokation, instance required

The following I'm not too sure about!

The resultant delegate looks the same, it has a Method and a Target. The Method is the DynamicMethod you've just created and the Target is the instance you provided. When the Delegate is invoked if the IL code within the DynamicMethod requires an instance of an object in order to run then it looks in the Target. If at invokation time this Target is null or is of the wrong type an Exception will be thrown.
The Target is created when you use the CreateDelegate, at this time the instance must exist, you can later set this same instance to null before invokation of the delegate, this must mean that a copy of the instance is added to the Target at runtime and the original can be done away with.

The Target object (instance) is a point of confusion for me! when is it required and when is it not? It's when the DynamicMethod's IL uses instances that are already on the stack, this is a achieved with
Idlem.ref
this gets a reference to an object which resides on the stack.
This is where the Target comes it, it is the instance that gets used.
Then this instance is used for something such as a call to another external method.



The above is not yet complete

No comments: