1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Reflection; 6 7 #if !FEATURE_DYNAMIC_DELEGATE 8 using System.Reflection.Emit; 9 #endif 10 11 namespace System.Dynamic.Utils 12 { 13 internal static class DelegateHelpers 14 { CreateObjectArrayDelegate(Type delegateType, Func<object[], object> handler)15 internal static Delegate CreateObjectArrayDelegate(Type delegateType, Func<object[], object> handler) 16 { 17 #if !FEATURE_DYNAMIC_DELEGATE 18 return CreateObjectArrayDelegateRefEmit(delegateType, handler); 19 #else 20 return Internal.Runtime.Augments.DynamicDelegateAugments.CreateObjectArrayDelegate(delegateType, handler); 21 #endif 22 } 23 24 25 #if !FEATURE_DYNAMIC_DELEGATE 26 27 private static readonly MethodInfo s_FuncInvoke = typeof(Func<object[], object>).GetMethod("Invoke"); 28 private static readonly MethodInfo s_ArrayEmpty = typeof(Array).GetMethod(nameof(Array.Empty)).MakeGenericMethod(typeof(object)); 29 30 // We will generate the following code: 31 // 32 // object ret; 33 // object[] args = new object[parameterCount]; 34 // args[0] = param0; 35 // args[1] = param1; 36 // ... 37 // try { 38 // ret = handler.Invoke(args); 39 // } finally { 40 // param0 = (T0)args[0]; // only generated for each byref argument 41 // } 42 // return (TRet)ret; CreateObjectArrayDelegateRefEmit(Type delegateType, Func<object[], object> handler)43 private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, Func<object[], object> handler) 44 { 45 MethodInfo delegateInvokeMethod = delegateType.GetInvokeMethod(); 46 47 Type returnType = delegateInvokeMethod.ReturnType; 48 bool hasReturnValue = returnType != typeof(void); 49 50 ParameterInfo[] parameters = delegateInvokeMethod.GetParametersCached(); 51 Type[] paramTypes = new Type[parameters.Length + 1]; 52 paramTypes[0] = typeof(Func<object[], object>); 53 for (int i = 0; i < parameters.Length; i++) 54 { 55 paramTypes[i + 1] = parameters[i].ParameterType; 56 } 57 58 DynamicMethod thunkMethod = new DynamicMethod("Thunk", returnType, paramTypes); 59 ILGenerator ilgen = thunkMethod.GetILGenerator(); 60 61 LocalBuilder argArray = ilgen.DeclareLocal(typeof(object[])); 62 LocalBuilder retValue = ilgen.DeclareLocal(typeof(object)); 63 64 // create the argument array 65 if (parameters.Length == 0) 66 { 67 ilgen.Emit(OpCodes.Call, s_ArrayEmpty); 68 } 69 else 70 { 71 ilgen.Emit(OpCodes.Ldc_I4, parameters.Length); 72 ilgen.Emit(OpCodes.Newarr, typeof(object)); 73 } 74 ilgen.Emit(OpCodes.Stloc, argArray); 75 76 // populate object array 77 bool hasRefArgs = false; 78 for (int i = 0; i < parameters.Length; i++) 79 { 80 bool paramIsByReference = parameters[i].ParameterType.IsByRef; 81 Type paramType = parameters[i].ParameterType; 82 if (paramIsByReference) 83 paramType = paramType.GetElementType(); 84 85 hasRefArgs = hasRefArgs || paramIsByReference; 86 87 ilgen.Emit(OpCodes.Ldloc, argArray); 88 ilgen.Emit(OpCodes.Ldc_I4, i); 89 ilgen.Emit(OpCodes.Ldarg, i + 1); 90 91 if (paramIsByReference) 92 { 93 ilgen.Emit(OpCodes.Ldobj, paramType); 94 } 95 Type boxType = ConvertToBoxableType(paramType); 96 ilgen.Emit(OpCodes.Box, boxType); 97 ilgen.Emit(OpCodes.Stelem_Ref); 98 } 99 100 if (hasRefArgs) 101 { 102 ilgen.BeginExceptionBlock(); 103 } 104 105 // load delegate 106 ilgen.Emit(OpCodes.Ldarg_0); 107 108 // load array 109 ilgen.Emit(OpCodes.Ldloc, argArray); 110 111 // invoke Invoke 112 ilgen.Emit(OpCodes.Callvirt, s_FuncInvoke); 113 ilgen.Emit(OpCodes.Stloc, retValue); 114 115 if (hasRefArgs) 116 { 117 // copy back ref/out args 118 ilgen.BeginFinallyBlock(); 119 for (int i = 0; i < parameters.Length; i++) 120 { 121 if (parameters[i].ParameterType.IsByRef) 122 { 123 Type byrefToType = parameters[i].ParameterType.GetElementType(); 124 125 // update parameter 126 ilgen.Emit(OpCodes.Ldarg, i + 1); 127 ilgen.Emit(OpCodes.Ldloc, argArray); 128 ilgen.Emit(OpCodes.Ldc_I4, i); 129 ilgen.Emit(OpCodes.Ldelem_Ref); 130 ilgen.Emit(OpCodes.Unbox_Any, byrefToType); 131 ilgen.Emit(OpCodes.Stobj, byrefToType); 132 } 133 } 134 ilgen.EndExceptionBlock(); 135 } 136 137 if (hasReturnValue) 138 { 139 ilgen.Emit(OpCodes.Ldloc, retValue); 140 ilgen.Emit(OpCodes.Unbox_Any, ConvertToBoxableType(returnType)); 141 } 142 143 ilgen.Emit(OpCodes.Ret); 144 145 // TODO: we need to cache these. 146 return thunkMethod.CreateDelegate(delegateType, handler); 147 } 148 ConvertToBoxableType(Type t)149 private static Type ConvertToBoxableType(Type t) 150 { 151 return (t.IsPointer) ? typeof(IntPtr) : t; 152 } 153 154 #endif 155 } 156 } 157