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