SledgeHammer
SledgeHammer

Reputation: 7681

Create / assign object via C# EMIT IL

new to IL... trying to create IL for:

Dest CreateInstance(Source src)
{
   Dest d = new Dest();
   d.Test = src.Test;
   return d;
}

This is what I have so far:

ConstructorInfo ctor = typeof(Dest).GetConstructors()[0];
DynamicMethod method = new DynamicMethod("CreateIntance", typeof(Dest),
    new Type[] { typeof(Source) });
ILGenerator gen = method.GetILGenerator();
//gen.Emit(OpCodes.Ldarg_0);// source
gen.Emit(OpCodes.Newobj, ctor);// new Created
gen.Emit(OpCodes.Ret);
CreateCtor createdCtorDelegate;
createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));

this runs as above... but if I uncomment out the Ldarg_0, I get a "this operation may unstabilize the runtime" when I try to call the delegate.

Also, what do I need to copy the Test member over? assuming its a basic type.

Thanks!

EDIT:

Source and Dest and just simple POCOs.

public class Source
{
    public string S1 { get; set; }
    public string S2 { get; set; }
    public int I1 { get; set; }
    public int I2 { get; set; }
    public string S3 { get; set; }
    public string S4 { get; set; }
    public string S5 { get; set; }
}

public class Dest
{
    public string S1 { get; set; }
    public string S2 { get; set; }
    public int I1 { get; set; }
    public int I2 { get; set; }
    public string S3 { get; set; }
    public string S4 { get; set; }
    public string S5 { get; set; }
}

EDIT #2: Now, I have this... still get the destabalize error:

        ConstructorInfo ctor = typeof(Dest).GetConstructors()[0];
        DynamicMethod method = new DynamicMethod("CreateIntance", typeof(Dest),
            new Type[] { typeof(Source) });
        MethodInfo miSrc = tSource.GetProperty("S1").GetGetMethod();
        MethodInfo miDest = tDest.GetProperty("S1").GetSetMethod();

        ILGenerator gen = method.GetILGenerator();
        gen.Emit(OpCodes.Newobj, ctor);// new Created
        gen.Emit(OpCodes.Dup);
        gen.Emit(OpCodes.Ldarg_1);// source
        gen.Emit(OpCodes.Ldfld, miSrc);
        gen.Emit(OpCodes.Stfld, miDest);
        gen.Emit(OpCodes.Ret);
        CreateCtor createdCtorDelegate;
        createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));

        Dest dd = createdCtorDelegate(s);

Get the exception when I call the createdCtorDelegate.

EDIT3:

ILSpy shows this:

.method public hidebysig static 
    class ConsoleApplication3.Dest Test (
        class ConsoleApplication3.Source s
    ) cil managed 
{
    // Method begins at RVA 0x2148
    // Code size 26 (0x1a)
    .maxstack 2
    .locals init (
        [0] class ConsoleApplication3.Dest,
        [1] class ConsoleApplication3.Dest
    )

IL_0000: nop
IL_0001: newobj instance void ConsoleApplication3.Dest::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldarg.0
IL_0009: callvirt instance string ConsoleApplication3.Source::get_S1()
IL_000e: callvirt instance void ConsoleApplication3.Dest::set_S1(string)
IL_0013: nop
IL_0014: ldloc.0
IL_0015: stloc.1
IL_0016: br.s IL_0018

IL_0018: ldloc.1
IL_0019: ret
} // end of method Program::Test

So, I've adjusted my code to:

        ConstructorInfo ctor = typeof(Dest).GetConstructors()[0];
        DynamicMethod method = new DynamicMethod("CreateIntance", typeof(Dest),
            new Type[] { typeof(Source) });
        MethodInfo miSrc = tSource.GetProperty("S1").GetGetMethod();
        MethodInfo miDest = tDest.GetProperty("S1").GetSetMethod();

        ILGenerator gen = method.GetILGenerator();

        gen.Emit(OpCodes.Newobj, ctor);// new Created
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Ldloc_0);
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Callvirt, miSrc);
        gen.Emit(OpCodes.Callvirt, miDest);
        gen.Emit(OpCodes.Ldloc_0);
        gen.Emit(OpCodes.Stloc_1);
        gen.Emit(OpCodes.Ldloc_1);
        gen.Emit(OpCodes.Ret);

        CreateCtor createdCtorDelegate;
        createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));

Still crashing :(...

Upvotes: 1

Views: 1794

Answers (2)

thehennyy
thehennyy

Reputation: 4218

... but if I uncomment out the Ldarg_0 ...

Your method should return a value, that means when reaching the ret instruction the stack has to contain exactly one item. The newobj instruction will create the new object and loads a reference to it onto the stack, so if you add more items to the stack without consuming them before reaching the ret your code is invalid.

what do I need to copy the Test member over?

The fastest way to get valid IL instructions is to use a high level language, as well as a compiler and decompiler. When you do this you will get something like this:

IL_0000: newobj instance void Dest::.ctor() //create new Dest object and push to stack (stack->Dest)
IL_0005: dup                                //duplicate the reference (stack->Dest/Dest)
IL_0006: ldarg.1                            //push the Source object to copy (stack->Dest/Dest/Source)
IL_0007: ldfld object Source::Test          //replace Source object with value from Source::Test (stack->Dest/Dest/SValue)
IL_000c: stfld object Dest::Test            //pop value and Dest object, and save value to the Dest object's Test field (stack->Dest)
IL_0011: ret                                //pop Dest object from stack for return value

Upvotes: 1

IS4
IS4

Reputation: 13177

Your last code is close to correct, you just have to declare the locals you want to use:

ILGenerator gen = method.GetILGenerator();
gen.DeclareLocal(typeof(Dest));

Also note that the last pair of stloc.1, ldloc.1 at the end is unnecessary, and is the result of compiling your code in Debug mode. Always compile your code in Release when inspecting CIL, as the code is much shorter and easier to read.

Your second version of the method was also almost correct, the problem was here:

gen.Emit(OpCodes.Ldarg_1);// source

There is no "argument 1", since argument lists starts from 0 (for static methods, instance methods have this as the argument 0). The fix is simple:

gen.Emit(OpCodes.Ldarg_0);// source

Upvotes: 0

Related Questions