134 lines
4.0 KiB
C#
134 lines
4.0 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));
|
|
}
|
|
}
|