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;
6 using System.Text;
7 
8 using Internal.TypeSystem;
9 
10 using Debug = System.Diagnostics.Debug;
11 using Interlocked = System.Threading.Interlocked;
12 
13 namespace Internal.IL.Stubs
14 {
15     /// <summary>
16     /// Thunk to dynamically invoke a method using reflection. The method accepts an object[] of parameters
17     /// to target method, lays them out on the stack, and calls the target method. This thunk has heavy
18     /// dependencies on the general dynamic invocation infrastructure in System.InvokeUtils and gets called from there
19     /// at runtime. See comments in System.InvokeUtils for a more thorough explanation.
20     /// </summary>
21     internal partial class DynamicInvokeMethodThunk : ILStubMethod
22     {
23         private TypeDesc _owningType;
24         private DynamicInvokeMethodSignature _targetSignature;
25 
26         private TypeDesc[] _instantiation;
27         private MethodSignature _signature;
28 
DynamicInvokeMethodThunk(TypeDesc owningType, DynamicInvokeMethodSignature signature)29         public DynamicInvokeMethodThunk(TypeDesc owningType, DynamicInvokeMethodSignature signature)
30         {
31             _owningType = owningType;
32             _targetSignature = signature;
33         }
34 
SupportsThunks(TypeSystemContext context)35         public static bool SupportsThunks(TypeSystemContext context)
36         {
37             return context.SystemModule.GetType("System", "InvokeUtils", false) != null;
38         }
39 
40         public override TypeSystemContext Context
41         {
42             get
43             {
44                 return _owningType.Context;
45             }
46         }
47 
48         public override TypeDesc OwningType
49         {
50             get
51             {
52                 return _owningType;
53             }
54         }
55 
56         private MetadataType InvokeUtilsType
57         {
58             get
59             {
60                 return Context.SystemModule.GetKnownType("System", "InvokeUtils");
61             }
62         }
63 
64         private MetadataType ArgSetupStateType
65         {
66             get
67             {
68                 return InvokeUtilsType.GetNestedType("ArgSetupState");
69             }
70         }
71 
72         public override MethodSignature Signature
73         {
74             get
75             {
76                 if (_signature == null)
77                 {
78                     _signature = new MethodSignature(
79                         MethodSignatureFlags.Static,
80                         Instantiation.Length,
81                         Context.GetWellKnownType(WellKnownType.Object),
82                         new TypeDesc[]
83                         {
84                             Context.GetWellKnownType(WellKnownType.Object),  // thisPtr
85                             Context.GetWellKnownType(WellKnownType.IntPtr),  // methodToCall
86                             ArgSetupStateType.MakeByRefType(),               // argSetupState
87                             Context.GetWellKnownType(WellKnownType.Boolean), // targetIsThisCall
88                         });
89                 }
90 
91                 return _signature;
92             }
93         }
94 
95         public override Instantiation Instantiation
96         {
97             get
98             {
99                 if (_instantiation == null)
100                 {
101                     TypeDesc[] instantiation =
102                         new TypeDesc[_targetSignature.HasReturnValue ? _targetSignature.Length + 1 : _targetSignature.Length];
103 
104                     for (int i = 0; i < _targetSignature.Length; i++)
105                         instantiation[i] = new DynamicInvokeThunkGenericParameter(this, i);
106 
107                     if (_targetSignature.HasReturnValue)
108                         instantiation[_targetSignature.Length] =
109                             new DynamicInvokeThunkGenericParameter(this, _targetSignature.Length);
110 
111                     Interlocked.CompareExchange(ref _instantiation, instantiation, null);
112                 }
113 
114                 return new Instantiation(_instantiation);
115             }
116         }
117 
118         public override string Name
119         {
120             get
121             {
122                 StringBuilder sb = new StringBuilder("InvokeRet");
123 
124                 if (_targetSignature.HasReturnValue)
125                     sb.Append('O');
126                 else
127                     sb.Append('V');
128 
129                 for (int i = 0; i < _targetSignature.Length; i++)
130                     sb.Append(_targetSignature[i] == DynamicInvokeMethodParameterKind.Value ? 'I' : 'R');
131 
132                 return sb.ToString();
133             }
134         }
135 
EmitIL()136         public override MethodIL EmitIL()
137         {
138             ILEmitter emitter = new ILEmitter();
139             ILCodeStream argSetupStream = emitter.NewCodeStream();
140             ILCodeStream thisCallSiteSetupStream = emitter.NewCodeStream();
141             ILCodeStream staticCallSiteSetupStream = emitter.NewCodeStream();
142 
143             // This function will look like
144             //
145             // !For each parameter to the method
146             //    !if (parameter is In Parameter)
147             //       localX is TypeOfParameterX&
148             //       ldtoken TypeOfParameterX
149             //       call DynamicInvokeParamHelperIn(RuntimeTypeHandle)
150             //       stloc localX
151             //    !else
152             //       localX is TypeOfParameter
153             //       ldtoken TypeOfParameterX
154             //       call DynamicInvokeParamHelperRef(RuntimeTypeHandle)
155             //       stloc localX
156 
157             // ldarg.2
158             // call DynamicInvokeArgSetupComplete(ref ArgSetupState)
159 
160             // *** Thiscall instruction stream starts here ***
161 
162             // ldarg.3 // Load targetIsThisCall
163             // brfalse Not_this_call
164 
165             // ldarg.0 // Load this pointer
166             // !For each parameter
167             //    !if (parameter is In Parameter)
168             //       ldloc localX
169             //       ldobj TypeOfParameterX
170             //    !else
171             //       ldloc localX
172             // ldarg.1
173             // calli ReturnType thiscall(TypeOfParameter1, ...)
174             // !if ((ReturnType == void)
175             //    ldnull
176             // !else
177             //    box ReturnType
178             // ret
179 
180             // *** Static call instruction stream starts here ***
181 
182             // Not_this_call:
183             // !For each parameter
184             //    !if (parameter is In Parameter)
185             //       ldloc localX
186             //       ldobj TypeOfParameterX
187             //    !else
188             //       ldloc localX
189             // ldarg.1
190             // calli ReturnType (TypeOfParameter1, ...)
191             // !if ((ReturnType == void)
192             //    ldnull
193             // !else
194             //    box ReturnType
195             // ret
196 
197             ILCodeLabel lStaticCall = emitter.NewCodeLabel();
198             thisCallSiteSetupStream.EmitLdArg(3); // targetIsThisCall
199             thisCallSiteSetupStream.Emit(ILOpcode.brfalse, lStaticCall);
200             staticCallSiteSetupStream.EmitLabel(lStaticCall);
201 
202             thisCallSiteSetupStream.EmitLdArg(0); // thisPtr
203 
204             ILToken tokDynamicInvokeParamHelperRef =
205                 emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeParamHelperRef", null));
206             ILToken tokDynamicInvokeParamHelperIn =
207                 emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeParamHelperIn", null));
208 
209             TypeDesc[] targetMethodSignature = new TypeDesc[_targetSignature.Length];
210 
211             for (int paramIndex = 0; paramIndex < _targetSignature.Length; paramIndex++)
212             {
213                 TypeDesc paramType = Context.GetSignatureVariable(paramIndex, true);
214                 ILToken tokParamType = emitter.NewToken(paramType);
215                 ILLocalVariable local = emitter.NewLocal(paramType.MakeByRefType());
216 
217                 thisCallSiteSetupStream.EmitLdLoc(local);
218                 staticCallSiteSetupStream.EmitLdLoc(local);
219 
220                 argSetupStream.Emit(ILOpcode.ldtoken, tokParamType);
221 
222                 if (_targetSignature[paramIndex] == DynamicInvokeMethodParameterKind.Reference)
223                 {
224                     argSetupStream.Emit(ILOpcode.call, tokDynamicInvokeParamHelperRef);
225 
226                     targetMethodSignature[paramIndex] = paramType.MakeByRefType();
227                 }
228                 else
229                 {
230                     argSetupStream.Emit(ILOpcode.call, tokDynamicInvokeParamHelperIn);
231 
232                     thisCallSiteSetupStream.Emit(ILOpcode.ldobj, tokParamType);
233                     staticCallSiteSetupStream.Emit(ILOpcode.ldobj, tokParamType);
234 
235                     targetMethodSignature[paramIndex] = paramType;
236                 }
237                 argSetupStream.EmitStLoc(local);
238             }
239 
240             argSetupStream.EmitLdArg(2); // argSetupState
241             argSetupStream.Emit(ILOpcode.call, emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeArgSetupComplete", null)));
242 
243             thisCallSiteSetupStream.EmitLdArg(1); // methodToCall
244             staticCallSiteSetupStream.EmitLdArg(1); // methodToCall
245 
246             TypeDesc returnType = _targetSignature.HasReturnValue ?
247                 Context.GetSignatureVariable(_targetSignature.Length, true) :
248                 Context.GetWellKnownType(WellKnownType.Void);
249 
250             MethodSignature thisCallMethodSig = new MethodSignature(0, 0, returnType, targetMethodSignature);
251             thisCallSiteSetupStream.Emit(ILOpcode.calli, emitter.NewToken(thisCallMethodSig));
252 
253             MethodSignature staticCallMethodSig = new MethodSignature(MethodSignatureFlags.Static, 0, returnType, targetMethodSignature);
254             staticCallSiteSetupStream.Emit(ILOpcode.calli, emitter.NewToken(staticCallMethodSig));
255 
256             if (_targetSignature.HasReturnValue)
257             {
258                 ILToken tokReturnType = emitter.NewToken(returnType);
259                 thisCallSiteSetupStream.Emit(ILOpcode.box, tokReturnType);
260                 staticCallSiteSetupStream.Emit(ILOpcode.box, tokReturnType);
261             }
262             else
263             {
264                 thisCallSiteSetupStream.Emit(ILOpcode.ldnull);
265                 staticCallSiteSetupStream.Emit(ILOpcode.ldnull);
266             }
267 
268             thisCallSiteSetupStream.Emit(ILOpcode.ret);
269             staticCallSiteSetupStream.Emit(ILOpcode.ret);
270 
271             return emitter.Link(this);
272         }
273 
274         private partial class DynamicInvokeThunkGenericParameter : GenericParameterDesc
275         {
276             private DynamicInvokeMethodThunk _owningMethod;
277 
DynamicInvokeThunkGenericParameter(DynamicInvokeMethodThunk owningMethod, int index)278             public DynamicInvokeThunkGenericParameter(DynamicInvokeMethodThunk owningMethod, int index)
279             {
280                 _owningMethod = owningMethod;
281                 Index = index;
282             }
283 
284             public override TypeSystemContext Context
285             {
286                 get
287                 {
288                     return _owningMethod.Context;
289                 }
290             }
291 
292             public override int Index
293             {
294                 get;
295             }
296 
297             public override GenericParameterKind Kind
298             {
299                 get
300                 {
301                     return GenericParameterKind.Method;
302                 }
303             }
304         }
305     }
306 
307     internal enum DynamicInvokeMethodParameterKind
308     {
309         None,
310         Value,
311         Reference,
312     }
313 
314     /// <summary>
315     /// Wraps a <see cref="MethodSignature"/> to reduce it's fidelity.
316     /// </summary>
317     internal struct DynamicInvokeMethodSignature : IEquatable<DynamicInvokeMethodSignature>
318     {
319         private MethodSignature _signature;
320 
321         public bool HasReturnValue
322         {
323             get
324             {
325                 return !_signature.ReturnType.IsVoid;
326             }
327         }
328 
329         public int Length
330         {
331             get
332             {
333                 return _signature.Length;
334             }
335         }
336 
337         public DynamicInvokeMethodParameterKind this[int index]
338         {
339             get
340             {
341                 return _signature[index].IsByRef ?
342                     DynamicInvokeMethodParameterKind.Reference :
343                     DynamicInvokeMethodParameterKind.Value;
344             }
345         }
346 
DynamicInvokeMethodSignatureInternal.IL.Stubs.DynamicInvokeMethodSignature347         public DynamicInvokeMethodSignature(MethodSignature concreteSignature)
348         {
349             // ByRef returns should have been filtered out elsewhere. We don't handle them
350             // because reflection can't invoke such methods.
351             Debug.Assert(!concreteSignature.ReturnType.IsByRef);
352             _signature = concreteSignature;
353         }
354 
EqualsInternal.IL.Stubs.DynamicInvokeMethodSignature355         public override bool Equals(object obj)
356         {
357             return obj is DynamicInvokeMethodSignature && Equals((DynamicInvokeMethodSignature)obj);
358         }
359 
GetHashCodeInternal.IL.Stubs.DynamicInvokeMethodSignature360         public override int GetHashCode()
361         {
362             int hashCode = HasReturnValue ? 17 : 23;
363 
364             for (int i = 0; i < Length; i++)
365             {
366                 int value = (int)this[i] * 0x5498341 + 0x832424;
367                 hashCode = hashCode * 31 + value;
368             }
369 
370             return hashCode;
371         }
372 
EqualsInternal.IL.Stubs.DynamicInvokeMethodSignature373         public bool Equals(DynamicInvokeMethodSignature other)
374         {
375             if (HasReturnValue != other.HasReturnValue)
376                 return false;
377 
378             if (Length != other.Length)
379                 return false;
380 
381             for (int i = 0; i < Length; i++)
382             {
383                 if (this[i] != other[i])
384                     return false;
385             }
386 
387             return true;
388         }
389     }
390 }
391