2012-03-09 9 views
10

In primo luogo, alcune informazioni di fondo:Reflection.Emit.ILGenerator Gestione delle eccezioni "Leave" istruzioni

Sto creando un compilatore per un progetto scolastico. Funziona già e sto impiegando un sacco di sforzi per correggere i bug e/o ottimizzarli. Recentemente ho incontrato un problema con è che ho scoperto che l'oggetto ILGenerator genera un leave di istruzioni in più quando si chiama uno dei seguenti metodi membri:

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

Quindi, si avvia un'istruzione try con una chiamata a BeginExceptionBlock(), aggiungere un paio di clausole di catch con BeginCatchBlock(), eventualmente aggiungere una clausola finally con BeginFinallyBlock() e quindi terminare la regione di codice protetta con EndExceptionBlock().

I metodi che ho elencato generano automaticamente un ramo di istruzioni leave alla prima istruzione dopo l'istruzione try. Non voglio questi, per due ragioni. Uno, perché genera sempre un'istruzione leave non ottimizzata, piuttosto che un'istruzione leave.s, anche quando si diramano solo due byte di distanza. E due, perché non puoi controllare dove va l'istruzione di congedo.

Quindi, se si desidera eseguire il diramazione in un'altra posizione nel codice, è necessario aggiungere una variabile locale generata dal compilatore, impostarla in base a dove si desidera andare all'interno dell'istruzione try, lasciare generare automaticamente EndExceptionBlock() l'istruzione leave e quindi generare un'istruzione switch sotto il blocco try. Oppure, si potrebbe semplicemente emettere un leave o leave.s istruzioni da soli, prima di chiamare uno dei metodi precedenti, con conseguente in un brutto e irraggiungibili in più 5 byte, in questo modo:

L_00ca: leave.s L_00e5 
L_00cc: leave L_00d1 

Entrambe queste opzioni sono per me inaccettabile. C'è un modo per evitare che sia la generazione automatica di leave istruzioni, oppure qualsiasi altro modo per specificare le regioni protette piuttosto che utilizzare questi metodi (che sono estremamente fastidioso e praticamente privi di documenti)?

EDIT Nota: il compilatore C# si fa questo, quindi non è come se ci fosse un buon motivo per forzare su di noi. Ad esempio, se si dispone di .NET 4.5 beta, smontare il seguente codice e verificare la loro attuazione: (blocco di eccezione aggiunto 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; 
} 

risposta

7

Per quanto posso dire, non si può fare questo in .NET 4.0. L'unico modo per creare un metodo body senza utilizzando ILGenerator è utilizzando MethodBuilder.CreateMethodBody, ma ciò non consente di impostare le informazioni di gestione delle eccezioni. E ILGenerator impone l'istruzione leave che stai chiedendo.

Tuttavia, se .NET 4.5 è un'opzione per voi (sembra essere), date un'occhiata a MethodBuilder.SetMethodBody. Ciò consente di creare l'IL autonomamente, ma comunque di passare attraverso le informazioni di gestione delle eccezioni. Si può avvolgere questo in un personalizzato ILGenerator classe -come del proprio, con metodi Emit prendere un argomento OpCode, e la lettura OpCode.Size e OpCode.Value per ottenere i byte corrispondenti.

E naturalmente c'è sempre Mono.Cecil, ma che probabilmente richiede più ampie modifiche al codice che hai già scritto.

Modifica: you appear to have already figured this out yourself, ma hai lasciato questa domanda aperta. Puoi pubblicare le risposte alle tue domande e accettarle, se l'hai capito da solo. Questo mi avrebbe fatto sapere che non avrei dovuto perdere tempo a cercare e che avrebbe permesso ad altre persone con la stessa domanda di sapere cosa fare.

+0

Grazie per la risposta. L'ho lasciato aperto perché non ero sicuro che la soluzione che ho trovato (MethodBuilder.SetMethodBody) avrebbe effettivamente funzionato per me - non ha una documentazione molto descrittiva. Grazie ancora! – aboveyou00