SMAPI/ModLoader/MonoMod/RuntimeDetour/Platforms/Runtime/DetourRuntimeNETPlatform.cs

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