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