IL Method call with 2 array arguments using Reflection.Emit

First I have to think about being a noob with IL. I had a problem generating IL code to call a method with this signature:

public void CallMethod2(string name, object[] args, object[] genericArgs) 

I can call a method that has one array that looks like this:

 public void CallMethod1(string name, object[] args) 

using the following IL works:

 ILGenerator ilgen = myMethod.GetILGenerator(); var il = ilgen; MethodInfo invokerMethod = typeof(Proxy<T>).GetMethod("CallMethod1", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, method.Name); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Newarr, typeof(System.Object)); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ldarg, 1); il.Emit(OpCodes.Stelem_Ref); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, invokerMethod); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ret); 

But then I use the following IL to try to call CallMethod2 using this IL:

 ILGenerator ilgen = myMethod.GetILGenerator(); var il = ilgen; MethodInfo invokerMethod = typeof(Proxy<T>).GetMethod("CallMethod2", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, method.Name); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Newarr, typeof(System.Object)); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ldarg, 1); il.Emit(OpCodes.Stelem_Ref); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Newarr, typeof(System.Object)); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ldarg, 1); il.Emit(OpCodes.Stelem_Ref); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_2); il.Emit(OpCodes.Call, invokerMethod); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ret); 

This IL with an optional object [] I get an error:

The Common Language runtime detected an invalid program.

As you can see everything I did, a second block was added to fill the array and call the method, it looks like with StLoc_1 it just corrupts it.

I wrote the same method and called it ok and looked at ILDasm and the codes seem to be all related.

thanks

+4
source share
2 answers

I am very confused ... you see: this code should not work, since you actually did not allocate any local residents; for example, here is a poorly written (in that it uses unnecessary locals) multiply-by-4 method, which does not declare locals:

  var method = new DynamicMethod("MulBy4", typeof (int), new Type[] {typeof (int)}); var il = method.GetILGenerator(); il.Emit(OpCodes.Ldc_I4_4); il.Emit(OpCodes.Stloc_0); // this usage is il.Emit(OpCodes.Ldloc_0); // deliberately silly il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Mul); il.Emit(OpCodes.Stloc_1); // this usage is il.Emit(OpCodes.Ldloc_1); // deliberately silly il.Emit(OpCodes.Ret); var mulBy4= (Func<int,int>)method.CreateDelegate(typeof (Func<int, int>)); var twelve = mulBy4(3); 

This throws a VerificationException :

An operation can destabilize runtime.

since it is not checked. This is bad il! If we change it to:

  var method = new DynamicMethod("MulBy4", typeof (int), new Type[] {typeof (int)}); var il = method.GetILGenerator(); il.DeclareLocal(typeof (int)); il.DeclareLocal(typeof(int)); ... 

then it works now . This leads to an alternative to storing numbers - by storing and using the LocalBuilder returned from DeclareLocal :

  var method = new DynamicMethod("MulBy4", typeof (int), new Type[] {typeof (int)}); var il = method.GetILGenerator(); var multiplier = il.DeclareLocal(typeof (int)); var result = il.DeclareLocal(typeof(int)); il.Emit(OpCodes.Ldc_I4_4); il.Emit(OpCodes.Stloc, multiplier); il.Emit(OpCodes.Ldloc, multiplier); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Mul); il.Emit(OpCodes.Stloc, result); il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ret); var mulBy4= (Func<int,int>)method.CreateDelegate(typeof (Func<int, int>)); var twelve = mulBy4(3); 

If you are concerned that this uses a longer version of IL, you can use instead:

 static void LoadLocal(this ILGenerator il, LocalBuilder local) { switch(local.LocalIndex) { case 0: il.Emit(OpCodes.Ldloc_0); break; case 1: il.Emit(OpCodes.Ldloc_1); break; case 2: il.Emit(OpCodes.Ldloc_2); break; case 3: il.Emit(OpCodes.Ldloc_3); break; default: if(local.LocalIndex < 256) { il.Emit(OpCodes.Ldloc_S, (byte) local.LocalIndex); } else { il.Emit(OpCodes.Ldloc, (ushort) local.LocalIndex); } break; } } 

together with il.LoadLocal(multiplier); and il.LoadLocal(result); (and obviously something similar for Stloc )

+3
source

I think the problem is that these lines are before the method call:

 il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_2); 

it should be

 il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldloc_1); 

Your arrays are in stack positions 0 and 1, not 1 and 2.

+4
source

Source: https://habr.com/ru/post/1386049/


All Articles