Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Place an object on top of stack in ILGenerator

I have to pass a function an instance of an object, so obviously all the information to be taken as argument is to be loaded onto the evaluation stack Here is the code that i am looking for

someClass SomeObject = new someClass();

il.Emit(OpCodes.LoadObject, SomeObject);
il.Emit(OpCodes.CallVirt, MethodInfo Function);


public void Function(Object obj)
{
       Type type = typeof(obj);
       //do something w.r.t to the type
}

I dont require any information stored in the class just the type and i cannot use any of the primitive types to take my decision on

Last i read that i can use a pointer to load the type using some opcodes ... but i am completely lost here, any help or pointers to the right direction would be great :)

[UPDATE]

Well i found an answer to my own question, tried it and it works don't know if it is the correct way or not but i can successfully create and load an object into stack and pass it to a function

ConstructorInfo ci = typeof(SomeClass).GetConstructor(System.Type.EmptyTypes);
IL.Emit(OpCodes.Newobj, ci);
IL.Emit(OpCodes.Call, SomeFunctionMethodInfo);

SomeFunctionMethodInfo is a function that takes Object as an argument, i successfully have passed the object into the function and can manipulate it also and return back the class as an object.

Nowhere i could find the reference to this example, just figured it out through MSDN, am i doing anything wrong or is there any downside to it ? Experts please if you could correct it or provide a better answer

like image 641
Basit Anwer Avatar asked Feb 14 '11 07:02

Basit Anwer


2 Answers

You can't pluck a reference out of thin air in IL, unless you code the reference as an IntPtr literal, in which case:
        a. don't do it
        b. you'd need to pin, and
        c. don't do it.

The best approach depends on the signature of the method you are writing. If it is static and takes no arguments... well, that is a bit tricky. Personally I'd be inclined to pass an object into the generated method, and have the delegate fetch any external data it needs from there. But another approach is to instead generate a class, and write the method as an instance method that accesses fields on the types.

The difference (hence my preference) is that the first requires (at most) an object[] parameter on the method—and you can use DynamicMethod; the second requires MethodBuilder, TypeBuilder, ModuleBuilder, AssemblyBuilder, etc., and are thus more work.

The reason I mention object[] is that generally you want a common signature over the generated methods, even if they require different inputs. This lets you bind to a fixed delegate type and use the faster Invoke execution (DynamicInvoke is slow).

For example:

class SomeType { }
delegate void SomeDelegateType(params object[] args);
public class Program
{
    public static void Main()
    {
        var dn = new DynamicMethod("foo", (Type)null, new[] {typeof(object[])});
        var il = dn.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldc_I4_0);
        il.Emit(OpCodes.Ldelem_Ref);
        il.EmitCall(OpCodes.Call, typeof(Program).GetMethod("Function"), null);
        il.Emit(OpCodes.Ret);
        var action = (SomeDelegateType)dn.CreateDelegate(typeof(SomeDelegateType));

        var obj = new SomeType();
        action(obj);
    }
    public static void Function(object obj)
    {
        Type type = obj.GetType();
        Console.WriteLine(type);
    }
}

If you can't have an input argument, then you'll have to use fields on a type you create—which is actually exactly what the compiler does if you write (for example)

object someObj = ...
Action action = () => Function(someObj);

This is created as:

class <>somehorriblename {
    public object someObj;
    public void SomeGeneratedName() { Function(someObj); }
}
...
var captureClass = new <>somehorriblename();
captureClass.someObj = ...
Action action = captureClass.SomeGeneratedName;
like image 148
Marc Gravell Avatar answered Oct 07 '22 16:10

Marc Gravell


One easy method I used was obtaining the GCHandle, then obtaining its IntPtr (via static method GCHandle.ToIntPtr) and then converting that to a long or integer (using either ToPointer or ToInt64).

That way I was able call ILGenerator.Emit(OpCodes.Ldc_I8, ptr).

like image 33
jsa Avatar answered Oct 07 '22 16:10

jsa