DP-PatMe2Mqtt/PatMe2Mqtt/FastReflectionHelper.cs
2023-05-26 15:52:45 +02:00

136 lines
4.4 KiB
C#

using System;
using System.Reflection;
using System.Reflection.Emit;
namespace MonoMod.Utils
{
public delegate object? FastReflectionDelegate(object? target, params object?[] args);
public static class FastReflectionHelper
{
private static readonly Type[] _dynamicMethodDelegateArgs = { typeof(object), typeof(object?[]) };
public static FastReflectionDelegate CreateFastDelegate(MethodBase method, bool directBoxValueAccess = true)
{
var dm = new DynamicMethod($"FastReflection<{method}>", typeof(object), _dynamicMethodDelegateArgs);
var il = dm.GetILGenerator();
var args = method.GetParameters();
var generateLocalBoxValuePtr = true;
if (!method.IsStatic)
{
il.Emit(OpCodes.Ldarg_0);
if (method.DeclaringType?.IsValueType ?? false)
{
il.Emit(OpCodes.Unbox_Any, method.DeclaringType);
}
}
for (var i = 0; i < args.Length; i++)
{
var argType = args[i].ParameterType;
var argIsByRef = argType.IsByRef;
if (argIsByRef)
{
argType = argType.GetElementType()!;
}
var argIsValueType = argType.IsValueType;
if (argIsByRef && argIsValueType && !directBoxValueAccess)
{
// Used later when storing back the reference to the new box in the array.
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
}
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
if (argIsByRef && !argIsValueType)
{
il.Emit(OpCodes.Ldelema, typeof(object));
}
else
{
il.Emit(OpCodes.Ldelem_Ref);
if (argIsValueType)
{
if (!argIsByRef || !directBoxValueAccess)
{
// if !directBoxValueAccess, create a new box if required
il.Emit(OpCodes.Unbox_Any, argType);
if (argIsByRef)
{
// box back
il.Emit(OpCodes.Box, argType);
// store new box value address to local 0
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Unbox, argType);
if (generateLocalBoxValuePtr)
{
generateLocalBoxValuePtr = false;
il.DeclareLocal(typeof(void*));
}
il.Emit(OpCodes.Stloc_0);
// arr and index set up already
il.Emit(OpCodes.Stelem_Ref);
// load address back to stack
il.Emit(OpCodes.Ldloc_0);
}
}
else
{
// if directBoxValueAccess, emit unbox (get value address)
il.Emit(OpCodes.Unbox, argType);
}
}
}
}
if (method.IsConstructor)
{
il.Emit(OpCodes.Newobj, (ConstructorInfo) method);
}
else if (method.IsFinal || !method.IsVirtual)
{
il.Emit(OpCodes.Call, (MethodInfo) method);
}
else
{
il.Emit(OpCodes.Callvirt, (MethodInfo) method);
}
var returnType = method.IsConstructor ? method.DeclaringType! : ((MethodInfo) method).ReturnType;
if (returnType != typeof(void))
{
if (returnType.IsValueType)
{
il.Emit(OpCodes.Box, returnType);
}
}
else
{
il.Emit(OpCodes.Ldnull);
}
il.Emit(OpCodes.Ret);
return (FastReflectionDelegate) dm.CreateDelegate(typeof(FastReflectionDelegate));
}
}
}