Reflection.Emit.ILGenerator异常处理"假"指令 [英] Reflection.Emit.ILGenerator Exception Handling "Leave" instruction

查看:506
本文介绍了Reflection.Emit.ILGenerator异常处理"假"指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,一些背景资料:



我提出了一个学校项目的编译器。它已经工作,而我花费了很多的努力,bug修复和/或优化。我最近碰到一个问题,就是我发现的ILGenerator对象产生一个额外的离开指令,当你调用任何成员如下方法:

  BeginCatchBlock()
BeginExceptFilterBlock()
BeginFaultBlock()
BeginFinallyBlock()
EndExceptionBlock()

所以,你开始一个try语句一起通话BeginExceptionBlock(),添加几个catch子句与 BeginCatchBlock(),可能添加一个最后用 BeginFinallyBlock(),然后结束与 EndExceptionBlock受保护的代码区域()



方法我列举自动生成一个离开指令try语句后,跳转到第一个指令。我不想要这些,有两个原因。一,因为它总是会产生一个未优化离开指令,而不是一个 leave.s 指令,即使它只是分支两个字节的路程。其二,因为你无法控制的地方leave指令去。



所以,如果你想转移到其他位置在你的代码,你必须添加一个编译器生成的局部变量,设置它取决于你想上哪儿去try语句里面,让 EndExceptionBlock()自动生成离开指令,然后生成try块下方的switch语句。或者,你可以只发出自己一个离开 leave.s 指令,称以前的方法之前,产生在一个丑陋的和额外的不可达5个字节,就像这样:

  L_00ca:leave.s L_00e5 
L_00cc:离开L_00d1

这两个选项是不能接受的给我。有什么办法要么防止自动生成的离开的说明,否则任何其他方式指定保护的区域,而不是使用这些方法(这是非常恼人的和实际无证) ?



编辑
注:C#编译器本身做到这一点,所以它不是因为如果有一个很好的理由,迫使它我们。例如,如果您有.NET 4.5 Beta版,拆解下面的代码,并检查其执行情况:(异常块内部添加)

 公共静态异步任务<布尔> TestAsync(INT MS)
{
VAR本地=毫秒/ 1000;
Console.WriteLine(在异步调用,前等待+ local.ToString()+ - 第二延迟。);
等待System.Threading.Tasks.Task.Delay(毫秒);
Console.WriteLine(在异步调用,之后等待+ local.ToString()+ - 第二延迟。);

Console.WriteLine();
Console.WriteLine(按任意键继续。);
Console.ReadKey(假);
返回真;
}


解决方案

据我可以告诉,你不能这样做在.NET 4.0中。创建一个方法体的没有唯一的办法的使用的ILGenerator 是使用 MethodBuilder.CreateMethodBody ,但是,这并不让你设置异常处理信息。和的ILGenerator 强制离开你问指令。



然而,如果.NET 4.5是一个选择(好像是),看一看的 MethodBuilder.SetMethodBody 。这使您可以创建自己的IL,但仍通过异常处理信息。您可以在自定义包装这个的ILGenerator 式的阶级自己,用的Emit 方法服用操作码参数,以及阅读 OpCode.Size OpCode.Value 来获得相应的字节。



当然,总是有 Mono.Cecil能做到 ,不过这可能需要更广泛的更改代码,你已经写



修改您似乎已经想通了这一点你自己,但是你离开了这个问题打开。您可以张贴解答自己的问题,并接受他们,如果你想通了你自己。这将有让我知道我不应该浪费时间寻找,并且将有让其他人同样的问题,知道该怎么做。


First, some background info:

I am making a compiler for a school project. It is already working, and I'm expending a lot of effort to bug fix and/or optimize it. I've recently run into a problem with is that I discovered that the ILGenerator object generates an extra leave instruction when you call any of the following member methods:

BeginCatchBlock()
BeginExceptFilterBlock()
BeginFaultBlock()
BeginFinallyBlock()
EndExceptionBlock()

So, you start a try statement with a call to BeginExceptionBlock(), add a couple of catch clauses with BeginCatchBlock(), possibly add a finally clause with BeginFinallyBlock(), and then end the protected code region with EndExceptionBlock().

The methods I listed automatically generate a leave instruction branching to the first instruction after the try statement. I don't want these, for two reasons. One, because it always generates an unoptimized leave instruction, rather than a leave.s instruction, even when it's branching just two bytes away. And two, because you can't control where the leave instruction goes.

So, if you wanted to branch to some other location in your code, you have to add a compiler-generated local variable, set it depending on where you want to go inside of the try statement, let EndExceptionBlock() auto-generate the leave instruction, and then generate a switch statement below the try block. OR, you could just emit a leave or leave.s instruction yourself, before calling one of the previous methods, resulting in an ugly and unreachable extra 5 bytes, like so:

L_00ca: leave.s L_00e5
L_00cc: leave L_00d1

Both of these options are unacceptable to me. Is there any way to either prevent the automatic generation of leave instructions, or else any other way to specify protected regions rather than using these methods (which are extremely annoying and practically undocumented)?

EDIT Note: the C# compiler itself does this, so it's not as if there is a good reason to force it on us. For example, if you have .NET 4.5 beta, disassemble the following code and check their implementation: (exception block added internally)

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;
}

解决方案

As far as I can tell, you cannot do this in .NET 4.0. The only way to create a method body without using ILGenerator is by using MethodBuilder.CreateMethodBody, but that does not allow you to set exception handling info. And ILGenerator forces the leave instruction you're asking about.

However, if .NET 4.5 is an option for you (it seems to be), take a look at MethodBuilder.SetMethodBody. This allows you to create the IL yourself, but still pass through exception handling information. You can wrap this in a custom ILGenerator-like class of your own, with Emit methods taking an OpCode argument, and reading OpCode.Size and OpCode.Value to get the corresponding bytes.

And of course there's always Mono.Cecil, but that probably requires more extensive changes to code you've already written.

Edit: you appear to have already figured this out yourself, but you left this question open. You can post answers to your own questions and accept them, if you've figured it out on your own. This would have let me know I shouldn't have wasted time searching, and which would have let other people with the same question know what to do.

这篇关于Reflection.Emit.ILGenerator异常处理&QUOT;假&QUOT;指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆