/Please skip to Update,
Using Net Framework 3.5 (Mono) with C# 7.0
I'm trying to create Dynamic method what take its parameters, box them to object array and call it on simple method.
Then in runtime I replace method via unsafe context. (Doing it for better API)
I created code how I imagine it and I posted that code in LINQPad 5.
void TestCall(string a, int b) {
NetworkInvoke(a, b);
}
void NetworkInvoke(params object[] parameters) {
}
In LINQPad I've got IL Code:
g__TestCall0_0:
IL_0000: nop
IL_0001: ldc.i4.2
IL_0002: newarr System.Object
IL_0007: dup
IL_0008: ldc.i4.0
IL_0009: ldarg.0
IL_000A: stelem.ref
IL_000B: dup
IL_000C: ldc.i4.1
IL_000D: ldarg.1
IL_000E: box System.Int32
IL_0013: stelem.ref
IL_0014: call g__NetworkInvoke0_1
IL_0019: nop
IL_001A: ret
Now lets recreate this in ILGenerator. (I have already code for supporting all paramaters but it don't works too. Then I created this simple static code what don't work too)
private MethodInfo CreateMethodFrom(MethodInfo method) {
var name = method.Name;
var attributes = method.Attributes;
var callConv = method.CallingConvention;
var returnType = method.ReturnType;
var parameters = new Type[method.GetParameters().Length];
for (int i = 0; i < parameters.Length; i++)
parameters[i] = method.GetParameters()[i].ParameterType;
Debug.Log("Creating dynamic method!");
var dynMethod = new DynamicMethod(name, attributes, callConv, returnType, parameters, GetType().BaseType, false);
for (int i = 0; i < parameters.Length; i++) {
dynMethod.DefineParameter(i, method.GetParameters()[i].Attributes, method.GetParameters()[i].Name);
}
var networkInvoker = GetType().BaseType.GetMethod("NetworkInvoke", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Log("Creating ILGenerator!");
ILGenerator il = dynMethod.GetILGenerator();
//ILCode(il, parameters, networkInvoker);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Box, typeof(System.Int32));
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Call, networkInvoker);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
return dynMethod;
}
Lets invoke our dynamic method:
forEach { //Iterating over methods
MethodInfo ourCorrectMethodToBeReplaced = it; //This method is for a debug, its signature is: (string testString, int testInt)
var replacedMethod = CreateMethodFrom(ourCorrectMethodToBeReplaced);
string test = "test1";
int test2 = 69;
replacedMethod.Invoke(methodInstance, new object[] {test, test2});
}
When I call it, I receive InvalidProgramException
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) SioFramework.Script:TestCall (string,int): IL_000b: stelem.ref
I tried to remove reference - (Stelem_Ref) Then I've got
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) SioFramework.Script:TestCall (string,int): IL_000d: ldarg.2
Any ideas What I'm doing wrong? Thanks for any tip.
Update, well, it looks like I posted wrong code here. (I tried to solve this problem almost full day of yesterday)
When I rewrited the ILCode to the correct (sorry, my mistake)
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Box, typeof(System.Int32));
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Call, networkInvoker);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
I've got
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) SioFramework.Script:TestCall (string,int): IL_0014: call
0x00000005
This is the correct error.
Update 2 I created Debug, when comparing both methods they are looks same.
private void DebugM(MethodInfo method) {
var builder = new StringBuilder();
builder.Append("Name: ").Append(method.Name);
builder.Append("\nParameters(").Append(method.GetParameters().Length).Append("): ");
foreach (var parameter in method.GetParameters()) {
builder.Append("(").Append(parameter.ParameterType.FullName).Append(")");
}
builder.Append("\nReturnType: ").Append(method.ReturnType.FullName);
builder.Append("\nMemberType: ").Append(method.MemberType);
builder.Append("\nCC: ").Append(method.CallingConvention);
builder.Append("\nModule: ").Append(method.Module.Name);
Debug.Log(builder.ToString());
}
Name: TestCall Parameters(2): (System.String)(System.Int32) ReturnType: System.Void MemberType: Method CC: Standard, HasThis Module: Assembly-CSharp.dll
I created exactly same method what is being recreated: To detect if it isn't due to parameters count Method what is being recreated:
public void TestCall(string testString, int testInt) {
Debug.Log("Called TestCAll with params: " + testInt + ", " + testInt);
}
Method to call:
internal void TestMethod(string a, int b) {
Debug.Log("Called TestMethod!!");
}
I tried to invoke it with delegate:
public delegate void TestDelegate(String a, int b);
//In end of CreateMethodFrom
var delTest = (TestDelegate) dynMethod.CreateDelegate(typeof(TestDelegate));
delTest("TESTA", 31321321);
Result is same:
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) SioFramework.Script:TestCall (string,int): IL_0014: call
0x00000005
Stacktrace
System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:268)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:291)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:295)
System.Reflection.Emit.DynamicMethod.CreateDelegate (System.Type delegateType) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs:179)
SioFramework.Script.CreateMethodFrom (System.Reflection.MethodInfo method) (at Assets/SioFramework/Script.cs:167)
167:
var delTest = (TestDelegate) dynMethod.CreateDelegate(typeof(TestDelegate));
User contributions licensed under CC BY-SA 3.0