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