从IL创建方法的副本

编程入门 行业动态 更新时间:2024-10-28 13:23:11
本文介绍了从IL创建方法的副本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在尝试在运行时使用反射创建方法的副本.

I am trying to create a copy of a method during runtime using reflection.

我有以下代码.

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 message:

公共语言运行库检测到无效程序.

Common Language Runtime detected an invalid program.

还有另一种方法吗?我希望能够获得该方法的语法树,而不是直接使用IL.

Is there another way of doing this? I would prefer being able to get the parse tree of the method instead of using IL directly.

编辑1 :

我正在测试以下功能.

public static int Fib(int n) { /*if (n < 2) return 1; return Fib(n - 1) + Fib(n - 2);*/ return n; }

使用以下行进行测试.

int x = Copy.CopyMethod(Copy.Fib, 10);

编辑2 :

Rob的答案有助于解决上述问题.但是,当使用稍微复杂一些的 Fib()方法(例如注释的Fibonacci方法)时,程序将崩溃并显示以下消息.

Rob's answer helps address the above issue. However, when using the Fib() method that is slightly more complicated (e.g. the commented Fibonacci method), the program crashes with the following message.

找不到索引.(来自HRESULT的异常:0x80131124)

Index not found. (Exception from HRESULT: 0x80131124)

编辑3 :

我尝试了一些来自注释的建议,但是元数据令牌无法在动态程序集中找到.

I have tried several suggestions from comments, but the metadata token cannot be located within 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); }

以下内容也不起作用.

var sigHelp = SignatureHelper.GetLocalVarSigHelper(mtbl.Module); mtbl.SetMethodBody(il, mb.MaxStackSize, sigHelp.GetSignature(), null, new int[] { 3 });

我可以通过以下方式更改元数据令牌来修复递归函数调用(我意识到这并非在所有情况下都有效,但我正在尝试使其以某种方式起作用).

I can fix the recursive function calls by changing the metadata token the following way (I realize that this will not work in all cases, but I am trying to get it to work in some way).

if (opCodes[i].FlowControl == FlowControl.Call) { ilg.Emit(opCodes[i], mtbl); i += 4; }

我可以使用相关问题的答案中建议的方法来构建动态方法:引用IL构造方法中的集合.但是,在此处尝试执行相同操作时,它将失败.

I can build a dynamic method using the approach suggested in the answer to the related question: Reference a collection from IL constructed method. However, when trying to do the same here, it fails.

推荐答案

基于评论中非常有帮助的讨论,我设法实现了重建.它不能解决所有可能的情况,但是可以很好地说明解决方案.

I managed to implement the reconstruction based on the very helpful discussion in the comments. It does not address all possible scenarios, but illustrates the solution very well.

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(); ILGenerator ilg = mtbl.GetILGenerator(); foreach (var local in mb.LocalVariables) ilg.DeclareLocal(local.LocalType); for (int i = 0; i < opCodes.Length; ++i) { if (!opCodes[i].code.HasValue) continue; OpCode opCode = opCodes[i].code.Value; if (opCode.OperandType == OperandType.InlineBrTarget) { ilg.Emit(opCode, BitConverter.ToInt32(il, i + 1)); i += 4; continue; } if (opCode.OperandType == OperandType.ShortInlineBrTarget) { ilg.Emit(opCode, il[i + 1]); ++i; continue; } if (opCode.OperandType == OperandType.InlineType) { Type tp = info.Module.ResolveType(BitConverter.ToInt32(il, i + 1), info.DeclaringType.GetGenericArguments(), info.GetGenericArguments()); ilg.Emit(opCode, tp); i += 4; continue; } if (opCode.FlowControl == FlowControl.Call) { MethodInfo mi = info.Module.ResolveMethod(BitConverter.ToInt32(il, i + 1)) as MethodInfo; if (mi == info) ilg.Emit(opCode, mtbl); else ilg.Emit(opCode, mi); i += 4; continue; } ilg.Emit(opCode); } Type type = tbl.CreateType(); Func<T, R> method = type.GetMethod(info.Name).CreateDelegate(typeof(Func<T, R>)) as Func<T, R>; return method(t); } static OpCodeContainer[] GetOpCodes(byte[] data) { List<OpCodeContainer> opCodes = new List<OpCodeContainer>(); foreach (byte opCodeByte in data) opCodes.Add(new OpCodeContainer(opCodeByte)); return opCodes.ToArray(); } class OpCodeContainer { public OpCode? code; byte data; public OpCodeContainer(byte opCode) { data = opCode; try { code = (OpCode)typeof(OpCodes).GetFields().First(t => ((OpCode)(t.GetValue(null))).Value == opCode).GetValue(null); } catch { } } }

更多推荐

从IL创建方法的副本

本文发布于:2023-11-17 03:42:25,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1608694.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:副本   方法   IL

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!