2010-11-10 7 views
8

Como novedad, trato de ver qué tan diferente se ve la IL del código de peso ligero en el tiempo de ejecución frente al código generado por el compilador VS, ya que noté que el código VS tiende a ejecutarse con un perfil de rendimiento diferente para cosas como moldes.¿Cómo obtengo un bytearray de IL de DynamicMethod?

Así que escribió el siguiente código ::

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

Desafortunadamente, esto arroja una excepción, ya GetMethodBody es aparentemente una operación ilegal en el código generado por los árboles de expresión. ¿Cómo puedo hacerlo de una manera bibliotecaria (es decir, no con una herramienta externa a menos que la herramienta tenga una API) mire el código generado por código usando codegen ligero?

Editar: el error ocurre en la línea 5, compiled.Method.GetMethodBody() arroja la excepción.

Edit2: ¿Alguien sabe cómo recuperar las variables locales declaradas en el método? ¿O no hay forma de GetVariables?

+0

¿Qué línea arroja la excepción? ¿Puedes comentar el primer Array.ForEach y ver si funciona?Sospecho que la primera llamada a GetMethodBody() está fallando simplemente porque esa expresión no se ha compilado en IL. No veo ninguna razón por la cual la segunda llamada debería fallar. – cdhowie

+0

Pregunta interesante. Obtengo una InvalidOperationException ("Operación no válida debido al estado actual del objeto") en la llamada a GetMethodBody. No estoy seguro de cómo comenzar la vida como un CachedAnonymousDelegate vs Expression afectaría su comportamiento como Func. Voy a seguir trabajando en este. – Sorax

+0

La respuesta seleccionada debe cambiarse porque no cubre todos los casos y es innecesariamente compleja. Por favor vea [esta respuesta] (http://stackoverflow.com/a/35711507/521757). – jnm2

Respuesta

16

Sí, no funciona, el método es generado por Reflection.Emit. El IL se almacena en ILGenerator del MethodBuilder. Puedes desenterrarlo pero tienes que estar bastante desesperado. Se necesita reflexión para llegar a los miembros internos y privados. Esta trabajado en .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> 
     //... 

En .NET 4.0 que tendrá que utilizar ilgen.GetType() BaseType.GetField (...) porque el generador de IL fue cambiado, DynamicILGenerator, deriva de. ILGenerator.

+0

¡Yuck! ¡Muchas gracias! –

+0

Oh, maldita sea, eso es feo. Buen trabajo ... es una pena que el código no pueda ser más hermoso. :( – cdhowie

+0

Acabo de intentarlo, esto no parece funcionar en .NET 4. me dice que fiBytes es nulo :( –

0

base del trabajo de Hans Passant pude profundizar un poco más parece que hay un método que debe llamar, llama BakeByteArray por lo que las siguientes obras ::

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[]; 

Esto sin duda ayuda, pero todavía no tengo manera de resolver VariableInfo's todavía que es algo que ayudaría en mi trabajo.

+2

Lo estás horneando dos veces. No estoy seguro si se quema, probablemente no. –

+0

No estoy seguro, sin embargo, este código es completamente inútil, ya que no puedo desmontar lo que realicé usando este enfoque ya que no puedo hacer que metadataToken resuelva nada útil ya que el módulo que aparece en la lista parece inútil . –

0

Las soluciones actuales de aquí no están abordando la situación actual en .NET 4 muy bien. Puede usar DynamicILInfo o ILGenerator para crear el método dinámico, pero las soluciones enumeradas aquí no funcionan con DynamicILInfo métodos dinámicos en total.

Si usa el método DynamicILInfo de generar IL o el método ILGenerator, el bytecode IL termina en DynamicMethod.m_resolver.m_code. No es necesario que compruebe ambos métodos y es una solución menos compleja.

Esta es la versión que debe utilizar:

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 para más métodos de ayuda y una solución para el problema de resolución símbolo DynamicMethod.

Cuestiones relacionadas