You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
5.0 KiB
152 lines
5.0 KiB
5 years ago
|
// Copyright (c) Microsoft. All rights reserved.
|
||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||
|
|
||
|
#if !NET_4_6
|
||
|
#if !FEATURE_DYNAMIC_DELEGATE
|
||
|
|
||
|
using System.Reflection;
|
||
|
using System.Reflection.Emit;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
namespace System.Dynamic.Utils
|
||
|
{
|
||
|
internal static class DelegateHelpers
|
||
|
{
|
||
|
private static readonly MethodInfo _funcInvoke = typeof(Func<object[], object>).GetMethod("Invoke");
|
||
|
|
||
|
internal static Delegate CreateObjectArrayDelegate(Type delegateType, Func<object[], object> handler)
|
||
|
{
|
||
|
#if !FEATURE_DYNAMIC_DELEGATE
|
||
|
return CreateObjectArrayDelegateRefEmit(delegateType, handler);
|
||
|
#else
|
||
|
return Internal.Runtime.Augments.DynamicDelegateAugments.CreateObjectArrayDelegate(delegateType, handler);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if !FEATURE_DYNAMIC_DELEGATE
|
||
|
|
||
|
// We will generate the following code:
|
||
|
//
|
||
|
// object ret;
|
||
|
// object[] args = new object[parameterCount];
|
||
|
// args[0] = param0;
|
||
|
// args[1] = param1;
|
||
|
// ...
|
||
|
// try {
|
||
|
// ret = handler.Invoke(args);
|
||
|
// } finally {
|
||
|
// param0 = (T0)args[0]; // only generated for each byref argument
|
||
|
// }
|
||
|
// return (TRet)ret;
|
||
|
private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, Func<object[], object> handler)
|
||
|
{
|
||
|
var delegateInvokeMethod = delegateType.GetMethod("Invoke");
|
||
|
|
||
|
var returnType = delegateInvokeMethod.ReturnType;
|
||
|
var hasReturnValue = returnType != typeof(void);
|
||
|
|
||
|
var parameters = delegateInvokeMethod.GetParameters();
|
||
|
var paramTypes = new Type[parameters.Length + 1];
|
||
|
paramTypes[0] = typeof(Func<object[], object>);
|
||
|
for (var i = 0; i < parameters.Length; i++)
|
||
|
{
|
||
|
paramTypes[i + 1] = parameters[i].ParameterType;
|
||
|
}
|
||
|
|
||
|
var thunkMethod = new DynamicMethod("Thunk", returnType, paramTypes);
|
||
|
var ilgen = thunkMethod.GetILGenerator();
|
||
|
|
||
|
var argArray = ilgen.DeclareLocal(typeof(object[]));
|
||
|
var retValue = ilgen.DeclareLocal(typeof(object));
|
||
|
|
||
|
// create the argument array
|
||
|
ilgen.Emit(OpCodes.Ldc_I4, parameters.Length);
|
||
|
ilgen.Emit(OpCodes.Newarr, typeof(object));
|
||
|
ilgen.Emit(OpCodes.Stloc, argArray);
|
||
|
|
||
|
// populate object array
|
||
|
var hasRefArgs = false;
|
||
|
for (var i = 0; i < parameters.Length; i++)
|
||
|
{
|
||
|
var paramIsByReference = parameters[i].ParameterType.IsByRef;
|
||
|
var paramType = parameters[i].ParameterType;
|
||
|
if (paramIsByReference)
|
||
|
{
|
||
|
paramType = paramType.GetElementType();
|
||
|
}
|
||
|
|
||
|
hasRefArgs = hasRefArgs || paramIsByReference;
|
||
|
|
||
|
ilgen.Emit(OpCodes.Ldloc, argArray);
|
||
|
ilgen.Emit(OpCodes.Ldc_I4, i);
|
||
|
ilgen.Emit(OpCodes.Ldarg, i + 1);
|
||
|
|
||
|
if (paramIsByReference)
|
||
|
{
|
||
|
ilgen.Emit(OpCodes.Ldobj, paramType);
|
||
|
}
|
||
|
var boxType = ConvertToBoxableType(paramType);
|
||
|
ilgen.Emit(OpCodes.Box, boxType);
|
||
|
ilgen.Emit(OpCodes.Stelem_Ref);
|
||
|
}
|
||
|
|
||
|
if (hasRefArgs)
|
||
|
{
|
||
|
ilgen.BeginExceptionBlock();
|
||
|
}
|
||
|
|
||
|
// load delegate
|
||
|
ilgen.Emit(OpCodes.Ldarg_0);
|
||
|
|
||
|
// load array
|
||
|
ilgen.Emit(OpCodes.Ldloc, argArray);
|
||
|
|
||
|
// invoke Invoke
|
||
|
var invoke = _funcInvoke;
|
||
|
ilgen.Emit(OpCodes.Callvirt, invoke);
|
||
|
ilgen.Emit(OpCodes.Stloc, retValue);
|
||
|
|
||
|
if (hasRefArgs)
|
||
|
{
|
||
|
// copy back ref/out args
|
||
|
ilgen.BeginFinallyBlock();
|
||
|
for (var i = 0; i < parameters.Length; i++)
|
||
|
{
|
||
|
if (parameters[i].ParameterType.IsByRef)
|
||
|
{
|
||
|
var byrefToType = parameters[i].ParameterType.GetElementType();
|
||
|
|
||
|
// update parameter
|
||
|
ilgen.Emit(OpCodes.Ldarg, i + 1);
|
||
|
ilgen.Emit(OpCodes.Ldloc, argArray);
|
||
|
ilgen.Emit(OpCodes.Ldc_I4, i);
|
||
|
ilgen.Emit(OpCodes.Ldelem_Ref);
|
||
|
ilgen.Emit(OpCodes.Unbox_Any, byrefToType);
|
||
|
ilgen.Emit(OpCodes.Stobj, byrefToType);
|
||
|
}
|
||
|
}
|
||
|
ilgen.EndExceptionBlock();
|
||
|
}
|
||
|
|
||
|
if (hasReturnValue)
|
||
|
{
|
||
|
ilgen.Emit(OpCodes.Ldloc, retValue);
|
||
|
ilgen.Emit(OpCodes.Unbox_Any, ConvertToBoxableType(returnType));
|
||
|
}
|
||
|
|
||
|
ilgen.Emit(OpCodes.Ret);
|
||
|
|
||
|
// TODO: we need to cache these.
|
||
|
return thunkMethod.CreateDelegate(delegateType, handler);
|
||
|
}
|
||
|
|
||
|
private static Type ConvertToBoxableType(Type t)
|
||
|
{
|
||
|
return (t.IsPointer) ? typeof(IntPtr) : t;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
#endif
|