问题
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;
}
回答1:
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.
来源:https://stackoverflow.com/questions/9642512/reflection-emit-ilgenerator-exception-handling-leave-instruction