我正在为一个学校项目编写一个编译器.它已经在运行,我花费了大量的精力来修复和/或优化它.最近遇到一个问题是我发现当您调用以下任何成员方法时,ILGenerator对象会生成一个额外的休假指令:
BeginCatchBlock() BeginExceptFilterBlock() BeginFaultBlock() BeginFinallyBlock() EndExceptionBlock()
所以,通过调用BeginExceptionBlock()来启动一个try语句,用BeginCatchBlock()添加一些catch子句,可以用BeginFinallyBlock()添加一个finally子句,然后用EndExceptionBlock()结束受保护的代码区域.
我列出的方法自动生成一条在try语句之后分支到第一条指令的leave指令.我不想要这些,有两个原因.一个,因为它总是生成一个未优化的休假指令,而不是一个leave.s指令,即使它的分支只有两个字节.另外两个,因为你不能控制休假指令的位置.
所以,如果你想分支到你的代码中的其他位置,你必须添加一个编译器生成的本地变量,根据你想要进入try语句的位置来设置它,让EndExceptionBlock()自动生成leave指令,然后在try块下面生成一个switch语句.或者,您可以在调用之前的方法之前自己发出leave或leave.s指令,导致一个丑陋和不可访问的额外5个字节,如下所示:
L_00ca: leave.s L_00e5 L_00cc: leave L_00d1
这两个选项都是我不能接受的.有没有办法阻止自动生成假指示,还是以其他方式指定保护区,而不是使用这些方法(这是非常烦人的,实际上没有记录的)?
编辑
注意:C#编译器本身就是这样做的,所以它不像有一个很好的理由强迫我们.例如,如果您有.NET 4.5 beta,请反汇编以下代码并检查其实现:(内部添加异常块)
public static async Task<bool> TestAsync(int ms) { var local = ms / 1000; Console.WriteLine("In async call,before await " + local.ToString() + "-second delay."); await System.Threading.Tasks.Task.Delay(ms); Console.WriteLine("In async call,after await " + local.ToString() + "-second delay."); Console.WriteLine(); Console.WriteLine("Press any key to continue."); Console.ReadKey(false); return true; }
解决方法
但是,如果.NET 4.5是您的选项(似乎是),请查看MethodBuilder.SetMethodBody
.这允许您自己创建IL,但仍然通过异常处理信息.您可以将其包装在您自己的自定义ILGenerator类中,Emit方法使用OpCode参数,并读取OpCode.Size和OpCode.Value以获取相应的字节.
当然,总是有Mono.Cecil,但是这可能需要对您已经编写的代码进行更广泛的更改.
编辑:you appear to have already figured this out yourself,但是你把这个问题放开了.您可以发布自己的问题的答案,并接受他们,如果你自己弄清楚了.这样可以让我知道我不应该浪费时间搜寻,而让其他同样问题的人知道该怎么做.