88 lines
4.8 KiB
C#
88 lines
4.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Reflection.Emit;
|
|
using MonoMod.Utils;
|
|
using System.Linq;
|
|
|
|
namespace MonoMod.RuntimeDetour.Platforms {
|
|
public sealed class DetourRuntimeNETPlatform : DetourRuntimeILPlatform {
|
|
private static readonly object[] _NoArgs = new object[0];
|
|
|
|
private static readonly FieldInfo _DynamicMethod_m_method =
|
|
typeof(DynamicMethod).GetField("m_method", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
private static readonly MethodInfo _DynamicMethod_GetMethodDescriptor =
|
|
typeof(DynamicMethod).GetMethod("GetMethodDescriptor", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
private static readonly FieldInfo _RuntimeMethodHandle_m_value =
|
|
typeof(RuntimeMethodHandle).GetField("m_value", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
|
|
private static readonly MethodInfo _RuntimeHelpers__CompileMethod =
|
|
typeof(RuntimeHelpers).GetMethod("_CompileMethod", BindingFlags.NonPublic | BindingFlags.Static);
|
|
private static readonly bool _RuntimeHelpers__CompileMethod_TakesIntPtr =
|
|
_RuntimeHelpers__CompileMethod != null &&
|
|
_RuntimeHelpers__CompileMethod.GetParameters()[0].ParameterType.FullName == "System.IntPtr";
|
|
private static readonly bool _RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo =
|
|
_RuntimeHelpers__CompileMethod != null &&
|
|
_RuntimeHelpers__CompileMethod.GetParameters()[0].ParameterType.FullName == "System.IRuntimeMethodInfo";
|
|
|
|
#if NETSTANDARD1_X
|
|
private static readonly MethodInfo _MethodBase_get_MethodHandle =
|
|
typeof(MethodBase).GetMethod("get_MethodHandle", BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
private static readonly MethodInfo _IRuntimeMethodInfo_get_Value =
|
|
typeof(RuntimeMethodHandle).GetTypeInfo().Assembly
|
|
.GetType("System.IRuntimeMethodInfo").GetMethod("get_Value", BindingFlags.Public | BindingFlags.Instance);
|
|
private static readonly MethodInfo _RuntimeMethodHandle_GetFunctionPointer =
|
|
typeof(RuntimeMethodHandle).GetMethod("GetFunctionPointer", BindingFlags.NonPublic | BindingFlags.Static);
|
|
|
|
// .NET Core 1.0.0 should have GetFunctionPointer, but it only has got its internal static counterpart.
|
|
protected override IntPtr GetFunctionPointer(RuntimeMethodHandle handle)
|
|
=> (IntPtr) _RuntimeMethodHandle_GetFunctionPointer.Invoke(null, new object[] { _IRuntimeMethodInfo_get_Value.Invoke(_RuntimeMethodHandle_m_value.GetValue(handle), _NoArgs) });
|
|
|
|
// .NET Core 1.0.0 should have PrepareMethod, but it has only got _CompileMethod.
|
|
// Let's hope that it works as well.
|
|
protected override void PrepareMethod(RuntimeMethodHandle handle)
|
|
=> _RuntimeHelpers__CompileMethod.Invoke(null, new object[] { _RuntimeMethodHandle_m_value.GetValue(handle) });
|
|
#endif
|
|
|
|
protected override RuntimeMethodHandle GetMethodHandle(MethodBase method) {
|
|
// Compile the method handle before getting our hands on the final method handle.
|
|
if (method is DynamicMethod dm) {
|
|
#if !NETSTANDARD1_X
|
|
if (_RuntimeHelpers__CompileMethod_TakesIntPtr) {
|
|
// mscorlib 2.0.0.0
|
|
_RuntimeHelpers__CompileMethod.Invoke(null, new object[] { ((RuntimeMethodHandle) _DynamicMethod_GetMethodDescriptor.Invoke(dm, _NoArgs)).Value });
|
|
|
|
} else
|
|
#endif
|
|
if (_RuntimeHelpers__CompileMethod_TakesIRuntimeMethodInfo) {
|
|
// mscorlib 4.0.0.0
|
|
_RuntimeHelpers__CompileMethod.Invoke(null, new object[] { _RuntimeMethodHandle_m_value.GetValue(((RuntimeMethodHandle) _DynamicMethod_GetMethodDescriptor.Invoke(dm, _NoArgs))) });
|
|
|
|
} else {
|
|
// This should work just fine.
|
|
// It abuses the fact that CreateDelegate first compiles the DynamicMethod, before creating the delegate and failing.
|
|
// Only side effect: It introduces a possible deadlock in f.e. tModLoader, which adds a FirstChanceException handler.
|
|
try {
|
|
dm.CreateDelegate(typeof(MulticastDelegate));
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
if (_DynamicMethod_m_method != null)
|
|
return (RuntimeMethodHandle) _DynamicMethod_m_method.GetValue(method);
|
|
if (_DynamicMethod_GetMethodDescriptor != null)
|
|
return (RuntimeMethodHandle) _DynamicMethod_GetMethodDescriptor.Invoke(method, _NoArgs);
|
|
}
|
|
|
|
#if NETSTANDARD1_X
|
|
return (RuntimeMethodHandle) _MethodBase_get_MethodHandle.Invoke(method, _NoArgs);
|
|
#else
|
|
return method.MethodHandle;
|
|
#endif
|
|
}
|
|
}
|
|
}
|