网上演练
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

// 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