using System.Reflection;
using System.Reflection.Emit;
namespace PlcSiemens.Core.Common
{
/// A dynamic delegate factory.
///
public class DelegateFactory
{
/// Creates a delegate from the given methodInfo and parameterTypes.
///
///
///
///
///
public static D CreateDelegate(MethodInfo methodInfo, Type[] parameterTypes) where D : class
{
if (methodInfo == null)
{
throw new ArgumentNullException("methodInfo");
}
if (parameterTypes == null)
{
throw new ArgumentNullException("parameterTypes");
}
var parameters = methodInfo.GetParameters();
var dynamicMethod = new DynamicMethod(
methodInfo.Name,
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
methodInfo.ReturnType,
parameterTypes,
typeof(object),
true)
{
InitLocals = false
};
var dynamicEmit = new DynamicEmit(dynamicMethod);
if (!methodInfo.IsStatic)
{
dynamicEmit.LoadArgument(0);
dynamicEmit.CastTo(typeof(object), methodInfo.DeclaringType);
}
for (int index = 0; index < parameters.Length; index++)
{
dynamicEmit.LoadArgument(index + 1);
dynamicEmit.CastTo(parameterTypes[index + 1], parameters[index].ParameterType);
}
dynamicEmit.Call(methodInfo);
dynamicEmit.Return();
return dynamicMethod.CreateDelegate(typeof(D)) as D;
}
private class DynamicEmit
{
private ILGenerator _ilGenerator;
private static readonly Dictionary _converts = new Dictionary();
static DynamicEmit()
{
_converts.Add(typeof(sbyte), OpCodes.Conv_I1);
_converts.Add(typeof(short), OpCodes.Conv_I2);
_converts.Add(typeof(int), OpCodes.Conv_I4);
_converts.Add(typeof(long), OpCodes.Conv_I8);
_converts.Add(typeof(byte), OpCodes.Conv_U1);
_converts.Add(typeof(ushort), OpCodes.Conv_U2);
_converts.Add(typeof(uint), OpCodes.Conv_U4);
_converts.Add(typeof(ulong), OpCodes.Conv_U8);
_converts.Add(typeof(float), OpCodes.Conv_R4);
_converts.Add(typeof(double), OpCodes.Conv_R8);
_converts.Add(typeof(bool), OpCodes.Conv_I1);
_converts.Add(typeof(char), OpCodes.Conv_U2);
}
public DynamicEmit(DynamicMethod dynamicMethod)
{
this._ilGenerator = dynamicMethod.GetILGenerator();
}
public DynamicEmit(ILGenerator ilGen)
{
this._ilGenerator = ilGen;
}
public void LoadArgument(int argumentIndex)
{
switch (argumentIndex)
{
case 0:
this._ilGenerator.Emit(OpCodes.Ldarg_0);
break;
case 1:
this._ilGenerator.Emit(OpCodes.Ldarg_1);
break;
case 2:
this._ilGenerator.Emit(OpCodes.Ldarg_2);
break;
case 3:
this._ilGenerator.Emit(OpCodes.Ldarg_3);
break;
default:
if (argumentIndex < 0x100)
{
this._ilGenerator.Emit(OpCodes.Ldarg_S, (byte)argumentIndex);
}
else
{
this._ilGenerator.Emit(OpCodes.Ldarg, argumentIndex);
}
break;
}
}
public void CastTo(Type fromType, Type toType)
{
if (fromType != toType)
{
if (toType == typeof(void))
{
if (!(fromType == typeof(void)))
{
this.Pop();
}
}
else
{
if (fromType.IsValueType)
{
if (toType.IsValueType)
{
this.Convert(toType);
return;
}
this._ilGenerator.Emit(OpCodes.Box, fromType);
}
this.CastTo(toType);
}
}
}
public void CastTo(Type toType)
{
if (toType.IsValueType)
{
this._ilGenerator.Emit(OpCodes.Unbox_Any, toType);
}
else
{
this._ilGenerator.Emit(OpCodes.Castclass, toType);
}
}
public void Pop()
{
this._ilGenerator.Emit(OpCodes.Pop);
}
public void Convert(Type toType)
{
this._ilGenerator.Emit(_converts[toType]);
}
public void Return()
{
this._ilGenerator.Emit(OpCodes.Ret);
}
public void Call(MethodInfo method)
{
if (method.IsFinal || !method.IsVirtual)
{
this._ilGenerator.EmitCall(OpCodes.Call, method, null);
}
else
{
this._ilGenerator.EmitCall(OpCodes.Callvirt, method, null);
}
}
}
}
}