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 语句。或者,在调用前面的方法之一之前,您可以自己发出leaveleave.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;
}

Reflection.Emit.ILGenerator Exception Handling "Leave&q

据我所知,您无法在 .NET 4.0 中执行此操作。在不使用 ILGenerator 的情况下创建方法主体的唯一方法是使用 MethodBuilder.CreateMethodBody ,但这不允许设置异常处理信息。ILGenerator强制你问leave指令。

但是,如果 .NET 4.5 是您的一个选项(似乎是(,请查看MethodBuilder.SetMethodBody 。这允许您自己创建 IL,但仍会传递异常处理信息。您可以将其包装在自己的自定义ILGenerator类中,Emit方法采用OpCode参数,并读取OpCode.SizeOpCode.Value以获取相应的字节。

当然,总是有Mono.Cecil,但这可能需要对你已经编写的代码进行更广泛的更改。

编辑:你似乎自己已经想通了,但你把这个问题留了下来。您可以发布自己问题的答案并接受它们,如果您自己想通了。这会让我知道我不应该浪费时间搜索,这会让其他有相同问题的人知道该怎么做。