Instrucción "Dejar" de manejo de excepciones de Reflection.Emit.ILGenerator

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:


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("Press any key to continue.");
    return true;

preguntado el 09 de marzo de 12 a las 23:03

1 Respuestas

As far as I can tell, you cannot do this in .NET 4.0. The only way to create a method body sin usar ILGenerator es mediante el uso MethodBuilder.CreateMethodBody, but that does not allow you to set exception handling info. And ILGenerator obliga al 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 y OpCode.Value to get the corresponding bytes.

Y por supuesto siempre hay Mono.Cecil, but that probably requires more extensive changes to code you've already written.

Editar: 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.

contestado el 23 de mayo de 17 a las 13:05

Thanks for the answer. I left it open because I wasn't sure if the solution I found (MethodBuilder.SetMethodBody) would actually work for me - it doesn't have a very descriptive documentation. Thanks again! - tejón leviatán

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.