2010-11-10 5 views
5

Gracias a Hans Passant responder a mi pregunta aquí: How do I get an IL bytearray from a DynamicMethod?La resolución de las fichas que se encuentran en la IL de un método dinámico

que fue capaz de poner en marcha. Ahora estoy tratando de resolver los tokens de metadatos encontrados en el IL emitido, para ver qué métodos se están llamando o qué no. Puedo resolver que el siguiente token en el cuerpo del método es una llamada. Estoy usando un código del MethodBodyReader de Mono.Reflection.

static byte[] GetILByteArray(Delegate @delegate){ 
    // does stuff mentioned in other thread 
} 
... 
Expression<Action> foo =() => Console.WriteLine(0); 
var compiled = foo.Compile(); 
var bytes = GetILByteArray(compiled); 
int index =Array.FindIndex(bytes,b=>GetOpCode(b).OperandType == OperandType.InlineMethod); 
var token = BitConverter.ToInt32(bytes,index+1); 
compiled.Method.Module.ResolveMember(token); 

Lanza una excepción que indica que el token no se puede resolver en ese dominio. ¿Alguien tiene un truco aquí? ¿Debo intentar pasar los parámetros genéricos de los delegados o son totalmente inútiles?

Actualmente estoy jugando con la idea de escribir un descompilador para delegados en árboles de expresiones y me gustaría poder usar árboles de expresiones que compilo como casos de prueba ya que siempre puedo volver al original y comparar.

+0

¿Un proyecto como este ayuda que: http://www.codeproject.com/KB/cs/sdilreader.aspx?df=100&forumid=303062&exp=0&select=1833733 parece estar trabajando en la misma línea , por lo que su fuente puede contener lo que necesita. – Sorax

+0

¿Lo estás usando? Definitivamente no recuerdo haber obtenido una marca de respuesta. Ayúdame a volver a mi trabajo y te ayudaré, espero que tenga sentido. –

+0

Versión modificada de su respuesta. Es principalmente correcto, te marcaré como la respuesta, pero creo que la versión horneada es la que se necesita. –

Respuesta

7

La respuesta es que debe usar el DynamicMethod.m_resolver para resolver tokens para métodos dinámicos en lugar de usar Module. Esto tiene sentido porque DynamicMethod.m_resolver.m_code es where you should be getting the IL byte array from.

Esto es difícil porque DynamicResolver.ResolveToken vuelve IntPtr outs y convertirlos de nuevo en RuntimeTypeHandle y RuntimeMethodHandle etc. requiere un buen montón de reflexión. No es probable que esta solución se rompa en el tiempo de ejecución de .NET 4.x, pero esté atento a cualquier cambio importante en la versión.

No hay forma concisa de decir esto.

Definir y utilizar esta interfaz en lugar de Module para los tokens de resolución:

public interface ITokenResolver 
{ 
    MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments); 
    Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments); 
    FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments); 
    MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments); 
    byte[] ResolveSignature(int metadataToken); 
    string ResolveString(int metadataToken); 
} 

Para métodos no dinámicos:

public sealed class ModuleTokenResolver : ITokenResolver 
{ 
    private readonly Module module; 

    public ModuleTokenResolver(Module module) 
    { 
     this.module = module; 
    } 

    public MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) => 
     module.ResolveMember(metadataToken, genericTypeArguments, genericMethodArguments); 

    public Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) => 
     module.ResolveType(metadataToken, genericTypeArguments, genericMethodArguments); 

    public FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) => 
     module.ResolveField(metadataToken, genericTypeArguments, genericMethodArguments); 

    public MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) => 
     module.ResolveMethod(metadataToken, genericTypeArguments, genericMethodArguments); 

    public byte[] ResolveSignature(int metadataToken) => 
     module.ResolveSignature(metadataToken); 

    public string ResolveString(int metadataToken) => 
     module.ResolveString(metadataToken); 
} 

Para los métodos dinámicos:

public sealed class DynamicMethodTokenResolver : ITokenResolver 
{ 
    private delegate void TokenResolver(int token, out IntPtr typeHandle, out IntPtr methodHandle, out IntPtr fieldHandle); 
    private delegate string StringResolver(int token); 
    private delegate byte[] SignatureResolver(int token, int fromMethod); 
    private delegate Type GetTypeFromHandleUnsafe(IntPtr handle); 

    private readonly TokenResolver tokenResolver; 
    private readonly StringResolver stringResolver; 
    private readonly SignatureResolver signatureResolver; 
    private readonly GetTypeFromHandleUnsafe getTypeFromHandleUnsafe; 
    private readonly MethodInfo getMethodBase; 
    private readonly ConstructorInfo runtimeMethodHandleInternalCtor; 
    private readonly ConstructorInfo runtimeFieldHandleStubCtor; 
    private readonly MethodInfo getFieldInfo; 

    public DynamicMethodTokenResolver(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."); 

     tokenResolver = (TokenResolver)resolver.GetType().GetMethod("ResolveToken", BindingFlags.Instance | BindingFlags.NonPublic).CreateDelegate(typeof(TokenResolver), resolver); 
     stringResolver = (StringResolver)resolver.GetType().GetMethod("GetStringLiteral", BindingFlags.Instance | BindingFlags.NonPublic).CreateDelegate(typeof(StringResolver), resolver); 
     signatureResolver = (SignatureResolver)resolver.GetType().GetMethod("ResolveSignature", BindingFlags.Instance | BindingFlags.NonPublic).CreateDelegate(typeof(SignatureResolver), resolver); 

     getTypeFromHandleUnsafe = (GetTypeFromHandleUnsafe)typeof(Type).GetMethod("GetTypeFromHandleUnsafe", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(IntPtr) }, null).CreateDelegate(typeof(GetTypeFromHandleUnsafe), null); 
     var runtimeType = typeof(RuntimeTypeHandle).Assembly.GetType("System.RuntimeType"); 

     var runtimeMethodHandleInternal = typeof(RuntimeTypeHandle).Assembly.GetType("System.RuntimeMethodHandleInternal"); 
     getMethodBase = runtimeType.GetMethod("GetMethodBase", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { runtimeType, runtimeMethodHandleInternal }, null); 
     runtimeMethodHandleInternalCtor = runtimeMethodHandleInternal.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(IntPtr) }, null); 

     var runtimeFieldInfoStub = typeof(RuntimeTypeHandle).Assembly.GetType("System.RuntimeFieldInfoStub"); 
     runtimeFieldHandleStubCtor = runtimeFieldInfoStub.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(IntPtr), typeof(object) }, null); 
     getFieldInfo = runtimeType.GetMethod("GetFieldInfo", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { runtimeType, typeof(RuntimeTypeHandle).Assembly.GetType("System.IRuntimeFieldInfo") }, null); 
    } 

    public Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) 
    { 
     IntPtr typeHandle, methodHandle, fieldHandle; 
     tokenResolver.Invoke(metadataToken, out typeHandle, out methodHandle, out fieldHandle); 

     return getTypeFromHandleUnsafe.Invoke(typeHandle); 
    } 

    public MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) 
    { 
     IntPtr typeHandle, methodHandle, fieldHandle; 
     tokenResolver.Invoke(metadataToken, out typeHandle, out methodHandle, out fieldHandle); 

     return (MethodBase)getMethodBase.Invoke(null, new[] 
     { 
      typeHandle == IntPtr.Zero ? null : getTypeFromHandleUnsafe.Invoke(typeHandle), 
      runtimeMethodHandleInternalCtor.Invoke(new object[] { methodHandle }) 
     }); 
    } 

    public FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) 
    { 
     IntPtr typeHandle, methodHandle, fieldHandle; 
     tokenResolver.Invoke(metadataToken, out typeHandle, out methodHandle, out fieldHandle); 

     return (FieldInfo)getFieldInfo.Invoke(null, new[] 
     { 
      typeHandle == IntPtr.Zero ? null : getTypeFromHandleUnsafe.Invoke(typeHandle), 
      runtimeFieldHandleStubCtor.Invoke(new object[] { fieldHandle, null }) 
     }); 
    } 

    public MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) 
    { 
     IntPtr typeHandle, methodHandle, fieldHandle; 
     tokenResolver.Invoke(metadataToken, out typeHandle, out methodHandle, out fieldHandle); 

     if (methodHandle != IntPtr.Zero) 
     { 
      return (MethodBase)getMethodBase.Invoke(null, new[] 
      { 
       typeHandle == IntPtr.Zero ? null : getTypeFromHandleUnsafe.Invoke(typeHandle), 
       runtimeMethodHandleInternalCtor.Invoke(new object[] { methodHandle }) 
      }); 
     } 

     if (fieldHandle != IntPtr.Zero) 
     { 
      return (FieldInfo)getFieldInfo.Invoke(null, new[] 
      { 
       typeHandle == IntPtr.Zero ? null : getTypeFromHandleUnsafe.Invoke(typeHandle), 
       runtimeFieldHandleStubCtor.Invoke(new object[] { fieldHandle, null }) 
      }); 
     } 

     if (typeHandle != IntPtr.Zero) 
     { 
      return getTypeFromHandleUnsafe.Invoke(typeHandle); 
     } 

     throw new NotImplementedException("DynamicMethods are not able to reference members by token other than types, methods and fields."); 
    } 

    public byte[] ResolveSignature(int metadataToken) 
    { 
     return signatureResolver.Invoke(metadataToken, 0); 
    } 

    public string ResolveString(int metadataToken) 
    { 
     return stringResolver.Invoke(metadataToken); 
    } 
} 

Esto es cómo para detectar métodos dinámicos y algunos métodos de ayuda:

public static class ReflectionExtensions 
{ 
    public static bool IsLightweightMethod(this MethodBase method) 
    { 
     return method is DynamicMethod || typeof(DynamicMethod).GetNestedType("RTDynamicMethod", BindingFlags.NonPublic).IsInstanceOfType(method); 
    } 

    public static ITokenResolver GetTokenResolver(this MethodBase method) 
    { 
     var dynamicMethod = TryGetDynamicMethod(method as MethodInfo) ?? method as DynamicMethod; 
     return dynamicMethod != null 
      ? new DynamicMethodTokenResolver(dynamicMethod) 
      : (ITokenResolver)new ModuleTokenResolver(method.Module); 
    } 

    public static byte[] GetILBytes(this MethodBase method) 
    { 
     var dynamicMethod = TryGetDynamicMethod(method as MethodInfo) ?? method as DynamicMethod; 
     return dynamicMethod != null 
      ? GetILBytes(dynamicMethod) 
      : method.GetMethodBody()?.GetILAsByteArray(); 
    } 

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

    public static DynamicMethod TryGetDynamicMethod(MethodInfo rtDynamicMethod) 
    { 
     var typeRTDynamicMethod = typeof(DynamicMethod).GetNestedType("RTDynamicMethod", BindingFlags.NonPublic); 
     return typeRTDynamicMethod.IsInstanceOfType(rtDynamicMethod) 
      ? (DynamicMethod)typeRTDynamicMethod.GetField("m_owner", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(rtDynamicMethod) 
      : null; 
    } 
} 
Cuestiones relacionadas