SMAPI/ModLoader/MonoMod/Utils/DynamicMethodDefinition.Emi...

85 lines
2.9 KiB
C#

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq.Expressions;
using MonoMod.Utils;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Linq;
using OpCodes = System.Reflection.Emit.OpCodes;
namespace MonoMod.Utils {
public sealed partial class DynamicMethodDefinition {
#if !NETSTANDARD
private readonly static MethodInfo m_MethodBase_InvokeSimple = typeof(MethodBase).GetMethod(
"Invoke", BindingFlags.Public | BindingFlags.Instance, null,
new Type[] { typeof(object), typeof(object[]) },
null
);
private MethodBuilder _EmitMethodProxy(MethodBuilder context, DynamicMethod target) {
TypeBuilder tb = (TypeBuilder) context.DeclaringType;
string name = $".dmdproxy<{target.Name.Replace('.', '_')}>?{target.GetHashCode()}";
MethodBuilder mb;
// System.NotSupportedException: The invoked member is not supported before the type is created.
/*
mb = tb.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static) as MethodBuilder;
if (mb != null)
return mb;
*/
Type[] args = target.GetParameters().Select(param => param.ParameterType).ToArray();
mb = tb.DefineMethod(
name,
System.Reflection.MethodAttributes.HideBySig | System.Reflection.MethodAttributes.Private | System.Reflection.MethodAttributes.Static,
CallingConventions.Standard,
target.ReturnType,
args
);
ILGenerator il = mb.GetILGenerator();
// Load the DynamicMethod reference first.
il.EmitReference(target);
// Load any other arguments on top of that.
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldc_I4, args.Length);
il.Emit(OpCodes.Newarr, typeof(object));
for (int i = 0; i < args.Length; i++) {
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg, i);
Type argType = args[i];
bool argIsByRef = argType.IsByRef;
if (argIsByRef)
argType = argType.GetElementType();
bool argIsValueType = argType.GetTypeInfo().IsValueType;
if (argIsValueType) {
il.Emit(OpCodes.Box, argType);
}
il.Emit(OpCodes.Stelem_Ref);
}
// Invoke the delegate and return its result.
il.Emit(OpCodes.Callvirt, m_MethodBase_InvokeSimple);
if (target.ReturnType == typeof(void))
il.Emit(OpCodes.Pop);
else if (target.ReturnType.IsValueType)
il.Emit(OpCodes.Unbox_Any, target.ReturnType);
il.Emit(OpCodes.Ret);
return mb;
}
#endif
}
}