2012-03-09 14 views
10

En primer lugar, un cierto fondo Info:Reflection.Emit.ILGenerator control de excepciones "Deja" instrucción

estoy haciendo un compilador para un proyecto escolar. Ya está funcionando, y estoy gastando un montón de esfuerzo para corregir errores y/u optimizarlo. Recientemente me he encontrado con un problema es que he descubierto que el objeto ILGenerator genera una leave instrucción adicional cuando se llama a cualquiera de los siguientes métodos: miembros

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

Así, se inicia una instrucción try con una llamada a BeginExceptionBlock(), agregue un par de cláusulas catch con BeginCatchBlock(), posiblemente agregue una cláusula finally con BeginFinallyBlock(), y luego termine la región del código protegido con EndExceptionBlock().

Los métodos enumerados generan automáticamente una instrucción leave que se bifurca a la primera instrucción después de la instrucción try. No quiero esto, por dos razones. Uno, porque siempre genera una instrucción leave no optimizada, en lugar de una instrucción leave.s, incluso cuando se ramifica a solo dos bytes de distancia. Y dos, porque no puedes controlar a dónde va la instrucción de abandono.

Por lo tanto, si desea realizar una bifurcación a otra ubicación en su código, debe agregar una variable local generada por el compilador, configurarla en función de dónde desee ingresar dentro de la instrucción try, deje EndExceptionBlock() autogenerar la instrucción leave, y luego genera una instrucción switch debajo del bloque try. O bien, puede simplemente emite una instrucción de leave o leave.s ti mismo, antes de llamar a uno de los métodos anteriores, lo que resulta en un feo e inalcanzables 5 bytes adicionales, así:

L_00ca: leave.s L_00e5 
L_00cc: leave L_00d1 

Ambas opciones son inaceptables para mí. ¿Hay alguna forma de evitar la generación automática de instrucciones leave, o de otra forma especificar regiones protegidas en lugar de utilizar estos métodos (que son extremadamente molestos y prácticamente indocumentados)?

EDIT Nota: el compilador C# hace esto, por lo que no es como si hubiera una buena razón para forzarlo. Por ejemplo, si usted tiene .NET 4.5 beta, desmontar el siguiente código y comprobar su aplicación: (bloque de excepción añadió internamente)

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

Respuesta

7

Por lo que yo puedo decir, no se puede hacer esto en .NET 4.0. La única forma de crear un cuerpo de método sin usando ILGenerator es usando MethodBuilder.CreateMethodBody, pero eso no le permite establecer la información de manejo de excepciones. Y ILGenerator fuerza la instrucción leave sobre la que está preguntando.

Sin embargo, si .NET 4.5 es una opción para usted (parece ser), eche un vistazo a MethodBuilder.SetMethodBody. Esto le permite crear la IL usted mismo, pero aún pasar a través de la información de manejo de excepciones. Puede envolver esto en una clase propia personalizada ILGenerator, con métodos Emit tomando un argumento OpCode y leyendo OpCode.Size y OpCode.Value para obtener los bytes correspondientes.

Y, por supuesto, siempre hay Mono.Cecil, pero eso probablemente requiera cambios más extensos en el código que ya ha escrito.

Editar: you appear to have already figured this out yourself, pero dejaste esta pregunta abierta. Puede publicar respuestas a sus propias preguntas y aceptarlas, si lo ha descubierto por su cuenta. Esto me hubiera hecho saber que no debería haber perdido el tiempo buscando, y que hubiera permitido que otras personas con la misma pregunta supieran qué hacer.

+0

Gracias por la respuesta. Lo dejé abierto porque no estaba seguro de si la solución que encontré (MethodBuilder.SetMethodBody) realmente me funcionaría, no tiene una documentación muy descriptiva. ¡Gracias de nuevo! – aboveyou00

Cuestiones relacionadas