2010-11-10 4 views
8

Come un po 'di novità, sto provando a vedere quanto sia diverso il IL dal codice leggero generato in fase di esecuzione rispetto al codice generato dal compilatore VS, come ho notato che il codice VS tende a funzionare con un profilo di prestazioni diverso per cose come calchi.Come posso ottenere un IL bytearray da un DynamicMethod?

Così ho scritto il seguente codice ::

Func<object,string> vs = x=>(string)x; 
Expression<Func<object,string>> exp = x=>(string)x; 
var compiled = exp.Compile(); 
Array.ForEach(vs.Method.GetMethodBody().GetILAsByteArray(),Console.WriteLine); 
Array.ForEach(compiled.Method.GetMethodBody().GetILAsByteArray(),Console.WriteLine); 

Purtroppo, questo genera un'eccezione come GetMethodBody è apparentemente un'operazione non valida sul codice generato da alberi di espressione. Come posso fare in una libreria (cioè non con uno strumento esterno a meno che lo strumento non abbia un'API) guardare il codice generato dal codice usando il codegen leggero?

Modifica: l'errore si verifica sulla riga 5, compilato.Method.GetMethodBody() genera l'eccezione.

Edit2: Qualcuno sa come recuperare le variabili locali dichiarate nel metodo? O non c'è modo di ottenere le variabili?

+0

Quale riga sta generando l'eccezione? Puoi commentare il primo Array.ForEach e vedere se funziona?Sospetto che la prima chiamata a GetMethodBody() fallisca semplicemente perché quell'espressione non è stata compilata in IL. Non vedo alcun motivo per cui la seconda chiamata dovrebbe fallire. – cdhowie

+0

Domanda interessante. Ricevo una InvalidOperationException ("Operazione non valida a causa dello stato corrente dell'oggetto") nella chiamata GetMethodBody. Non sono sicuro di come l'avvio della vita come CachedAnonymousDelegate vs Expression influenzi il tuo comportamento come Func. Continuerò a lavorare su questo. – Sorax

+0

La risposta selezionata deve essere cambiata perché non copre tutti i casi ed è inutilmente complessa. Si prega di consultare [questa risposta] (http://stackoverflow.com/a/35711507/521757). – jnm2

risposta

16

Sì, non funziona, il metodo è generato da Reflection.Emit. L'IL è memorizzato in ILGenerator di MethodBuilder. Puoi scavare ma devi essere piuttosto disperato. La riflessione è necessaria per arrivare ai membri interni e privati. Questo lavorato su .NET 3.5SP1:

using System.Linq.Expressions; 
using System.Reflection; 
using System.Reflection.Emit; 
... 

     var mtype = compiled.Method.GetType(); 
     var fiOwner = mtype.GetField("m_owner", BindingFlags.Instance | BindingFlags.NonPublic); 
     var dynMethod = fiOwner.GetValue(compiled.Method) as DynamicMethod; 
     var ilgen = dynMethod.GetILGenerator(); 
     var fiBytes = ilgen.GetType().GetField("m_ILStream", BindingFlags.Instance | BindingFlags.NonPublic); 
     var fiLength = ilgen.GetType().GetField("m_length", BindingFlags.Instance | BindingFlags.NonPublic); 
     byte[] il = fiBytes.GetValue(ilgen) as byte[]; 
     int cnt = (int)fiLength.GetValue(ilgen); 
     // Dump <cnt> bytes from <il> 
     //... 

On .NET 4.0 Dovrete usare ilgen.GetType() BaseType.GetField (...) in quanto il generatore IL è stato cambiato, DynamicILGenerator, derivato da. ILGenerator.

+0

Yuck! Grazie mille! –

+0

Oh, dannazione, è brutto. Buon lavoro ... è un peccato che il codice non possa essere più bello. :( – cdhowie

+0

Appena provato, questo non sembra funzionare in .NET 4. mi dice che fiBytes è nullo :( –

0

base al largo il lavoro di Hans Passant ero in grado di scavare un po 'più profonda ci sembra essere un metodo che si dovrebbe chiamare, chiamato BakeByteArray così i seguenti lavori ::

var dynMethod = fiOwner.GetValue(compiled.Method) as DynamicMethod; 
var ilgen =dynamicMethod.GetILGenerator(); 
byte[] il = ilgen.GetType().GetMethod("BakeByteArray", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(ilgen, null) as byte[]; 

Questo certamente aiuta, ma ho ancora non ho ancora modo di risolvere VariableInfo's, cosa che potrebbe essere d'aiuto nel mio lavoro.

+2

Si sta cuocendo due volte, non è sicuro se verrà bruciato, probabilmente no. –

+0

Non sono sicuro, tuttavia questo codice è completamente inutile in quanto non riesco a smontare ciò che realizzo realisticamente con questo approccio in quanto non riesco a risolvere il problema dei metadataToken perché il modulo elencato nella cosa sembra inutile . –

0

Le attuali soluzioni qui non stanno affrontando la situazione attuale in .NET 4 molto bene. È possibile utilizzare DynamicILInfo o ILGenerator per creare il metodo dinamico, ma le soluzioni elencate qui non funzionano con i metodi dinamici DynamicILInfoin tutto.

Se si utilizza il metodo DynamicILInfo per la generazione di IL o il metodo ILGenerator, il bytecode IL finisce in DynamicMethod.m_resolver.m_code. Non è necessario controllare entrambi i metodi ed è una soluzione meno complessa.

questa è la versione si dovrebbe usare:

public static byte[] GetILBytes(DynamicMethod dynamicMethod) 
{ 
    var resolver = typeof(DynamicMethod).GetField("m_resolver", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(dynamicMethod); 
    if (resolver == null) throw new ArgumentException("The dynamic method's IL has not been finalized."); 
    return (byte[])resolver.GetType().GetField("m_code", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(resolver); 
} 

See this answer per ulteriori metodi di supporto e una soluzione per il problema di risoluzione del token DynamicMethod.