I am trying to create a copy of a method at runtime using reflection.
I have the following code.
public static R CopyMethod<T, R>(Func<T, R> f, T t) { AppDomain currentDom = Thread.GetDomain(); AssemblyName asm = new AssemblyName(); asm.Name = "DynamicAssembly"; AssemblyBuilder abl = currentDom.DefineDynamicAssembly(asm, AssemblyBuilderAccess.Run); ModuleBuilder mbl = abl.DefineDynamicModule("Module"); TypeBuilder tbl = mbl.DefineType("Type"); var info = f.GetMethodInfo(); MethodBuilder mtbl = tbl.DefineMethod(info.Name, info.Attributes, info.CallingConvention, info.ReturnType, info.GetParameters().Select(x => x.ParameterType).ToArray()); byte[] il = f.Method.GetMethodBody().GetILAsByteArray(); mtbl.CreateMethodBody(il, il.Length); Type type = tbl.CreateType(); Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>; return method(t); }
The last line throws an exception with the message:
The Common Language runtime detected an invalid program.
Is there any other way to do this? I would rather be able to get the method parsing tree instead of using IL directly.
EDIT 1 :
I am testing the following function.
public static int Fib(int n) { return n; }
Testing with the next line.
int x = Copy.CopyMethod(Copy.Fib, 10);
EDIT 2 :
Rob's answer helps solve the above problem. However, when using the Fib() method, which is a bit more complicated (for example, the commented Fibonacci method), the program crashes with the following message.
Index not found. (Exception from HRESULT: 0x80131124)
EDIT 3 :
I tried several suggestions from comments, but the metadata token cannot be located in the dynamic assembly.
public static R CopyMethod<T, R>(Func<T, R> f, T t) { AppDomain currentDom = Thread.GetDomain(); AssemblyName asm = new AssemblyName("DynamicAssembly"); AssemblyBuilder abl = currentDom.DefineDynamicAssembly(asm, AssemblyBuilderAccess.Run); ModuleBuilder mbl = abl.DefineDynamicModule("Module"); TypeBuilder tbl = mbl.DefineType("Type"); MethodInfo info = f.GetMethodInfo(); MethodBuilder mtbl = tbl.DefineMethod(info.Name, info.Attributes, info.CallingConvention, info.ReturnType, info.GetParameters().Select(x => x.ParameterType).ToArray()); MethodBody mb = f.Method.GetMethodBody(); byte[] il = mb.GetILAsByteArray(); OpCode[] opCodes = GetOpCodes(il); Globals.LoadOpCodes(); MethodBodyReader mbr = new MethodBodyReader(info); string code = mbr.GetBodyCode(); Console.WriteLine(code); ILGenerator ilg = mtbl.GetILGenerator(); ilg.DeclareLocal(typeof(int[])); ilg.DeclareLocal(typeof(int)); for (int i = 0; i < opCodes.Length; ++i) { if (opCodes[i].OperandType == OperandType.InlineType) { int token; Type tp = info.Module.ResolveType(token = BitConverter.ToInt32(il, i + 1), info.DeclaringType.GetGenericArguments(), info.GetGenericArguments()); ilg.Emit(opCodes[i], tp.MetadataToken); i += 4; continue; } if (opCodes[i].FlowControl == FlowControl.Call) { int token; MethodBase mi = info.Module.ResolveMethod(token = BitConverter.ToInt32(il, i + 1)); ilg.Emit(opCodes[i], mi.MetadataToken); i += 4; continue; } ilg.Emit(opCodes[i]); } Type type = tbl.CreateType(); Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>; return method(t); }
The following does not work either.
var sigHelp = SignatureHelper.GetLocalVarSigHelper(mtbl.Module); mtbl.SetMethodBody(il, mb.MaxStackSize, sigHelp.GetSignature(), null, new int[] { 3 });
I can fix recursive function calls by changing the metadata token as follows (I understand that this will not work in all cases, but I'm trying to get it to work somehow).
if (opCodes[i].FlowControl == FlowControl.Call) { ilg.Emit(opCodes[i], mtbl); i += 4; }
I can build a dynamic method using the approach proposed in the answer to the corresponding question: Link to the collection from the constructed IL method . However, trying to do the same here, he fails.