Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting simple class to IL failed due to some invalid IL code?

I'm trying to convert this simple class to IL code:

public class IL {
  Dictionary<string, int> props = new Dictionary<string, int>() { {"1",1} };
}

In fact I used ILDasm to know the IL instructions before trying to use Emit to create the class dynamically. The result it shows is:

.class public auto ansi beforefieldinit IL
   extends [mscorlib]System.Object
{
 .field private class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> props
 .method public hidebysig specialname rtspecialname 
      instance void  .ctor() cil managed
 {
 // 
 .maxstack  4
 .locals init (class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> V_0)
 IL_0000:  ldarg.0
 IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::.ctor()
 IL_0006:  stloc.0
 IL_0007:  ldloc.0
 IL_0008:  ldstr      "1"
 IL_000d:  ldc.i4.1
 IL_000e:  callvirt   instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
                                                                                                              !1)
 IL_0013:  ldloc.0
 IL_0014:  stfld      class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> IL::props
 IL_0019:  ldarg.0
 IL_001a:  call       instance void [mscorlib]System.Object::.ctor()
 IL_001f:  ret
 } // end of method IL::.ctor

} // end of class IL

From that, I tried using Emit like this:

var aBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new System.Reflection.AssemblyName("test"), AssemblyBuilderAccess.Run);
var mBuilder = aBuilder.DefineDynamicModule("module");
var tBuilder = mBuilder.DefineType("IL");        
var field = tBuilder.DefineField("props", typeof(Dictionary<string, int>), System.Reflection.FieldAttributes.Private);
var con = tBuilder.DefineConstructor(System.Reflection.MethodAttributes.Public | 
                       System.Reflection.MethodAttributes.HideBySig | 
                       System.Reflection.MethodAttributes.SpecialName | 
                       System.Reflection.MethodAttributes.RTSpecialName, 
                       System.Reflection.CallingConventions.HasThis, Type.EmptyTypes);

var conIL = con.GetILGenerator();            
conIL.Emit(OpCodes.Ldarg_0);            
conIL.Emit(OpCodes.Newobj, typeof(Dictionary<string,int>).GetConstructor(Type.EmptyTypes));                        
conIL.Emit(OpCodes.Stloc_0);                
conIL.Emit(OpCodes.Ldloc_0);
conIL.Emit(OpCodes.Ldstr, "1");
conIL.Emit(OpCodes.Ldc_I4_1);
conIL.Emit(OpCodes.Callvirt, typeof(Dictionary<string, int>).GetMethod("Add"));
conIL.Emit(OpCodes.Ldloc_0);
conIL.Emit(OpCodes.Stfld, field);

conIL.Emit(OpCodes.Ldarg_0);
conIL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
conIL.Emit(OpCodes.Ret);

Now try using it:

var t =  tBuilder.CreateType();
var instance = Activator.CreateInstance(t);//exception has been thrown here 
               //saying "Common Language Runtime detected an invalid program."

That means the IL code would be wrong at some point. But comparing it with what is actually generated by ILDasm, I don't see any difference. What is wrong here?

like image 895
Hopeless Avatar asked Aug 24 '15 00:08

Hopeless


1 Answers

You missed to declare the local variable that is referenced in line IL_0006, IL_0007 and IL_0013. Add the following line and it will work.

conIL.DeclareLocal(typeof (Dictionary<string, int>), false);

Most likely this local was introduced by the compiler, because the code was compiled in debug mode.

like image 162
thehennyy Avatar answered Oct 08 '22 12:10

thehennyy