Reflection.Emit.ILGenerator Exception Handling "Leave&q
本文关键字:quot Leave Handling Emit ILGenerator Exception Reflection | 更新日期: 2023-09-27 18:27:22
首先,一些背景信息:
我正在为一个学校项目制作编译器。它已经在工作了,我花了很多精力来修复和/或优化它。我最近遇到了一个问题是,我发现当您调用以下任何成员方法时,ILGenerator 对象会生成额外的leave
指令:
BeginCatchBlock()
BeginExceptFilterBlock()
BeginFaultBlock()
BeginFinallyBlock()
EndExceptionBlock()
所以,你用调用 BeginExceptionBlock()
开始一个 try 语句,用 BeginCatchBlock()
添加几个 catch 子句,可能添加一个带有 BeginFinallyBlock()
的 finally 子句,然后用 EndExceptionBlock()
结束受保护的代码区域。
我列出的方法自动生成一条leave
指令,分支到 try 语句之后的第一条指令。我不想要这些,原因有两个。第一,因为它总是生成未优化的leave
指令,而不是leave.s
指令,即使它只在两个字节之外分支。第二,因为你无法控制休假指令的去向。
因此,如果要分支到代码中的其他位置,则必须添加一个编译器生成的局部变量,根据要在 try 语句中的位置对其进行设置,让EndExceptionBlock()
自动生成leave
指令,然后在 try 块下方生成一个 switch 语句。或者,在调用前面的方法之一之前,您可以自己发出leave
或leave.s
指令,从而导致丑陋且无法访问的额外 5 个字节,如下所示:
L_00ca: leave.s L_00e5
L_00cc: leave L_00d1
这两种选择对我来说都是不可接受的。有没有办法防止自动生成leave
指令,或者任何其他方法来指定受保护区域而不是使用这些方法(这些方法非常烦人且实际上没有记录(?
编辑注意:C# 编译器本身就是这样做的,所以好像没有充分的理由把它强加给我们。例如,如果您有 .NET 4.5 测试版,请反汇编以下代码并检查其实现:(内部添加的异常块(
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.0 中执行此操作。在不使用 ILGenerator
的情况下创建方法主体的唯一方法是使用 MethodBuilder.CreateMethodBody
,但这不允许设置异常处理信息。ILGenerator
强制你问leave
指令。
但是,如果 .NET 4.5 是您的一个选项(似乎是(,请查看MethodBuilder.SetMethodBody
。这允许您自己创建 IL,但仍会传递异常处理信息。您可以将其包装在自己的自定义ILGenerator
类中,Emit
方法采用OpCode
参数,并读取OpCode.Size
和OpCode.Value
以获取相应的字节。
当然,总是有Mono.Cecil,但这可能需要对你已经编写的代码进行更广泛的更改。
编辑:你似乎自己已经想通了,但你把这个问题留了下来。您可以发布自己问题的答案并接受它们,如果您自己想通了。这会让我知道我不应该浪费时间搜索,这会让其他有相同问题的人知道该怎么做。