The answer is that you must use the DynamicMethod.m_resolver
to resolve tokens for dynamic methods rather than using Module
. This makes sense because DynamicMethod.m_resolver.m_code
is where you should be getting the IL byte array from.
This is difficult because DynamicResolver.ResolveToken
returns IntPtr
outs and turning them back into RuntimeTypeHandle
and RuntimeMethodHandle
etc. requires quite a lot of reflection. This solution is not likely to break in the .NET 4.x runtime but keep an eye out on any major version changes.
There's just no concise way to put this.
Define and use this interface instead of Module
for resolving tokens:
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);
}
For non-dynamic methods:
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);
}
For dynamic methods:
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);
}
}
This is how to detect dynamic methods, and some helper methods:
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;
}
}
RuntimeTypeHandle
andRuntimeMethodHandle
etc fromIntPtr
s. The Reference Source indicates that this is the only real way to solve it. Posting soon. – LanniArray.FindIndex
is a terrible way to locate IL instructions. Since the instructions are variable length, you need to parse each instruction starting from the top of the array. Otherwise,Array.FindIndex
will "find" yourInlineMethod
opcode in the middle of some random integer or floating point literal or member token. – Lanni