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 6 using System; 7 using System.Runtime; 8 using System.Collections.Generic; 9 using System.Diagnostics; 10 using System.Runtime.InteropServices; 11 using System.Threading; 12 13 using Internal.Runtime; 14 using Internal.Runtime.Augments; 15 using Internal.Runtime.CompilerServices; 16 17 using Internal.NativeFormat; 18 using Internal.TypeSystem; 19 using Internal.TypeSystem.Ecma; 20 using Internal.TypeSystem.NativeFormat; 21 using System.Reflection.Metadata; 22 using System.Reflection.Metadata.Ecma335; 23 using System.Reflection.Runtime.General; 24 25 namespace Internal.Runtime.TypeLoader 26 { 27 // Extensibility api to allow a method execution strategy to be defined by a module that depends 28 // on this module. The GlobalExecutionStrategy static variable is expected to be assigned once per process 29 // with whatever the execution strategy is. 30 public abstract class MethodExecutionStrategy 31 { 32 public static MethodExecutionStrategy GlobalExecutionStrategy; OnEntryPoint(MethodEntrypointPtr entrypointInfo, IntPtr callerArgumentsInfo)33 public abstract IntPtr OnEntryPoint(MethodEntrypointPtr entrypointInfo, IntPtr callerArgumentsInfo); 34 } 35 36 internal struct MethodEntrypointData 37 { MethodEntrypointDataInternal.Runtime.TypeLoader.MethodEntrypointData38 public MethodEntrypointData(RuntimeMethodHandle methodIdentifier, IntPtr methodEntrypointThunk) 39 { 40 MethodIdentifier = methodIdentifier; 41 MethodCode = IntPtr.Zero; 42 MethodEntrypointThunk = methodEntrypointThunk; 43 } 44 45 public readonly RuntimeMethodHandle MethodIdentifier; 46 public IntPtr MethodCode; 47 public readonly IntPtr MethodEntrypointThunk; 48 } 49 50 public unsafe struct MethodEntrypointPtr 51 { 52 private static object s_thunkPoolHeap; SetThunkPoolInternal.Runtime.TypeLoader.MethodEntrypointPtr53 internal static void SetThunkPool(object thunkPoolHeap) { s_thunkPoolHeap = thunkPoolHeap; } 54 MethodEntrypointPtrInternal.Runtime.TypeLoader.MethodEntrypointPtr55 internal MethodEntrypointPtr(MethodEntrypointData *data) 56 { 57 _data = data; 58 } 59 MethodEntrypointPtrInternal.Runtime.TypeLoader.MethodEntrypointPtr60 internal MethodEntrypointPtr(IntPtr ptr) 61 { 62 _data = (MethodEntrypointData*)ptr.ToPointer(); 63 } 64 65 private MethodEntrypointData *_data; 66 public RuntimeMethodHandle MethodIdentifier { get { return _data->MethodIdentifier; } } 67 public IntPtr MethodCode 68 { 69 get 70 { 71 return _data->MethodCode; 72 } 73 set 74 { 75 _data->MethodCode = value; 76 RuntimeAugments.SetThunkData(s_thunkPoolHeap, _data->MethodEntrypointThunk, value, new IntPtr(_data)); 77 } 78 } 79 80 public IntPtr MethodEntrypointThunk { get { return _data->MethodEntrypointThunk; } } 81 ToIntPtrInternal.Runtime.TypeLoader.MethodEntrypointPtr82 public IntPtr ToIntPtr() 83 { 84 return new IntPtr(_data); 85 } 86 } 87 88 public static class MethodEntrypointStubs 89 { RuntimeMethodHandleToIntPtr(RuntimeMethodHandle rmh)90 private static unsafe IntPtr RuntimeMethodHandleToIntPtr(RuntimeMethodHandle rmh) 91 { 92 return *(IntPtr*)&rmh; 93 } IntPtrToRuntimeMethodHandle(IntPtr rmh)94 private static unsafe RuntimeMethodHandle IntPtrToRuntimeMethodHandle(IntPtr rmh) 95 { 96 RuntimeMethodHandle handle = default(RuntimeMethodHandle); 97 RuntimeMethodHandle* pRMH = &handle; 98 *((IntPtr*)pRMH) = rmh; 99 100 return handle; 101 } 102 IntPtrToRuntimeTypeHandle(IntPtr rtth)103 private static unsafe RuntimeTypeHandle IntPtrToRuntimeTypeHandle(IntPtr rtth) 104 { 105 RuntimeTypeHandle handle = default(RuntimeTypeHandle); 106 RuntimeTypeHandle* pRTTH = &handle; 107 *((IntPtr*)pRTTH) = rtth; 108 109 return handle; 110 } 111 112 private struct MethodEntrypointLookup 113 { MethodEntrypointLookupInternal.Runtime.TypeLoader.MethodEntrypointStubs.MethodEntrypointLookup114 public MethodEntrypointLookup(MethodDesc method) 115 { 116 _method = method; 117 118 RuntimeTypeHandle declaringTypeHandle = method.OwningType.GetRuntimeTypeHandle(); 119 RuntimeSignature methodSignature; 120 if (!RuntimeSignatureHelper.TryCreate(method, out methodSignature)) 121 { 122 Environment.FailFast("Unable to create method signature"); 123 } 124 RuntimeTypeHandle[] genericMethodArgs = null; 125 if (method.Instantiation.Length != 0) 126 { 127 genericMethodArgs = new RuntimeTypeHandle[method.Instantiation.Length]; 128 for (int i = 0; i < genericMethodArgs.Length; i++) 129 { 130 genericMethodArgs[i] = method.Instantiation[i].GetRuntimeTypeHandle(); 131 } 132 } 133 134 _rmh = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeHandle, 135 IntPtr.Zero, 136 methodSignature, 137 genericMethodArgs); 138 } 139 140 public MethodDesc Method => _method; 141 public RuntimeMethodHandle MethodHandle => _rmh; 142 143 private RuntimeMethodHandle _rmh; 144 MethodDesc _method; 145 } 146 147 private unsafe class MethodEntrypointHash : LockFreeReaderHashtableOfPointers<MethodEntrypointLookup, MethodEntrypointPtr> 148 { 149 TypeLoaderEnvironment _tle = TypeLoaderEnvironment.Instance; 150 151 /// <summary> 152 /// Given a key, compute a hash code. This function must be thread safe. 153 /// </summary> GetKeyHashCode(MethodEntrypointLookup key)154 protected override int GetKeyHashCode(MethodEntrypointLookup key) 155 { 156 return key.MethodHandle.GetHashCode(); 157 } 158 159 /// <summary> 160 /// Given a value, compute a hash code which would be identical to the hash code 161 /// for a key which should look up this value. This function must be thread safe. 162 /// </summary> GetValueHashCode(MethodEntrypointPtr value)163 protected override unsafe int GetValueHashCode(MethodEntrypointPtr value) 164 { 165 return value.MethodIdentifier.GetHashCode(); 166 } 167 168 /// <summary> 169 /// Compare a key and value. If the key refers to this value, return true. 170 /// This function must be thread safe. 171 /// </summary> CompareKeyToValue(MethodEntrypointLookup key, MethodEntrypointPtr value)172 protected override bool CompareKeyToValue(MethodEntrypointLookup key, MethodEntrypointPtr value) 173 { 174 return value.MethodIdentifier.Equals(key.MethodHandle); 175 } 176 177 /// <summary> 178 /// Compare a value with another value. Return true if values are equal. 179 /// This function must be thread safe. 180 /// </summary> CompareValueToValue(MethodEntrypointPtr value1, MethodEntrypointPtr value2)181 protected override bool CompareValueToValue(MethodEntrypointPtr value1, MethodEntrypointPtr value2) 182 { 183 return value1.MethodIdentifier.Equals(value2.MethodIdentifier); 184 } 185 186 [DllImport("*", ExactSpelling = true, EntryPoint = "MethodEntrypointStubs_SetupPointers")] MethodEntrypointStubs_SetupPointers(IntPtr universalTransitionThunk, IntPtr methodEntrypoint)187 private unsafe extern static IntPtr MethodEntrypointStubs_SetupPointers(IntPtr universalTransitionThunk, IntPtr methodEntrypoint); 188 189 private static object s_thunkPoolHeap; 190 private static IntPtr s_entryPointStub = SetupMethodEntrypoints(); 191 SetupMethodEntrypoints()192 private static IntPtr SetupMethodEntrypoints() 193 { 194 return MethodEntrypointStubs_SetupPointers(RuntimeAugments.GetUniversalTransitionThunk(), 195 Intrinsics.AddrOf<Func<IntPtr, IntPtr, IntPtr>>(EntrypointThunk)); 196 } 197 EntrypointThunk(IntPtr callerTransitionBlockParam, IntPtr entrypointData)198 unsafe private static IntPtr EntrypointThunk(IntPtr callerTransitionBlockParam, IntPtr entrypointData) 199 { 200 MethodEntrypointPtr entryPointPointer = new MethodEntrypointPtr(entrypointData); 201 return MethodExecutionStrategy.GlobalExecutionStrategy.OnEntryPoint(entryPointPointer, callerTransitionBlockParam); 202 } 203 204 /// <summary> 205 /// Create a new value from a key. Must be threadsafe. Value may or may not be added 206 /// to collection. Return value must not be null. 207 /// </summary> CreateValueFromKey(MethodEntrypointLookup key)208 protected override unsafe MethodEntrypointPtr CreateValueFromKey(MethodEntrypointLookup key) 209 { 210 lock (this) 211 { 212 IntPtr thunk = IntPtr.Zero; 213 if (s_thunkPoolHeap == null) 214 { 215 s_thunkPoolHeap = RuntimeAugments.CreateThunksHeap(s_entryPointStub); 216 MethodEntrypointPtr.SetThunkPool(s_thunkPoolHeap); 217 Debug.Assert(s_thunkPoolHeap != null); 218 } 219 220 thunk = RuntimeAugments.AllocateThunk(s_thunkPoolHeap); 221 Debug.Assert(thunk != IntPtr.Zero); 222 MethodEntrypointData *methodEntrypointData = (MethodEntrypointData*)MemoryHelpers.AllocateMemory(sizeof(MethodEntrypointData)); 223 224 *methodEntrypointData = new MethodEntrypointData(key.MethodHandle, thunk); 225 226 RuntimeAugments.SetThunkData(s_thunkPoolHeap, thunk, IntPtr.Zero, new IntPtr(methodEntrypointData)); 227 228 SerializedDebugData.RegisterTailCallThunk(thunk); 229 230 return new MethodEntrypointPtr(methodEntrypointData); 231 } 232 } 233 234 /// <summary> 235 /// Convert a value to an IntPtr for storage into the hashtable 236 /// </summary> ConvertValueToIntPtr(MethodEntrypointPtr value)237 protected override IntPtr ConvertValueToIntPtr(MethodEntrypointPtr value) 238 { 239 return value.ToIntPtr(); 240 } 241 242 /// <summary> 243 /// Convert an IntPtr into a value for comparisions, or for returning. 244 /// </summary> ConvertIntPtrToValue(IntPtr pointer)245 protected override MethodEntrypointPtr ConvertIntPtrToValue(IntPtr pointer) 246 { 247 return new MethodEntrypointPtr(pointer); 248 } 249 } 250 251 private static MethodEntrypointHash s_methodEntrypointHash = new MethodEntrypointHash(); 252 TryGetMethodEntrypoint(MethodDesc methodOnType, out IntPtr entryPoint, out IntPtr unboxingStubAddress, out TypeLoaderEnvironment.MethodAddressType foundAddressType)253 public static bool TryGetMethodEntrypoint(MethodDesc methodOnType, out IntPtr entryPoint, out IntPtr unboxingStubAddress, out TypeLoaderEnvironment.MethodAddressType foundAddressType) 254 { 255 MethodDesc typicalMethod = methodOnType.GetTypicalMethodDefinition(); 256 257 if (!(typicalMethod is EcmaMethod)) 258 { 259 foundAddressType = TypeLoaderEnvironment.MethodAddressType.None; 260 entryPoint = IntPtr.Zero; 261 unboxingStubAddress = IntPtr.Zero; 262 return false; 263 } 264 265 // OK, this is a method entrypoint for an ecma method 266 EcmaMethod ecmaTypicalMethod = (EcmaMethod)typicalMethod; 267 268 // Canonicalize 269 MethodDesc canonMethod = methodOnType.GetCanonMethodTarget(CanonicalFormKind.Specific); 270 if (canonMethod != methodOnType) 271 foundAddressType = TypeLoaderEnvironment.MethodAddressType.Canonical; 272 else 273 foundAddressType = TypeLoaderEnvironment.MethodAddressType.Exact; 274 275 276 // Check to see if we should produce an unboxing stub entrypoint 277 278 unboxingStubAddress = IntPtr.Zero; // Optimistically choose not to 279 if (ecmaTypicalMethod.OwningType.IsValueType) 280 { 281 MethodSignature methodSig = ecmaTypicalMethod.Signature; 282 if (!methodSig.IsStatic) 283 unboxingStubAddress = new IntPtr(5); // TODO Actually implement the unboxing stub logic 284 } 285 286 // Ensure RuntimeTypeHandles for owningType, and for instantiation types 287 // They should be there, as the paths to this function should ensure it, but its a bit sketchy 288 // as we don't have an opportunity to easily compute them now 289 if (!canonMethod.OwningType.RetrieveRuntimeTypeHandleIfPossible()) 290 Environment.FailFast("Did not pre-allocate owning type typehandle"); 291 292 foreach (TypeDesc type in canonMethod.Instantiation) 293 { 294 if (!type.RetrieveRuntimeTypeHandleIfPossible()) 295 Environment.FailFast("Did not pre-allocate instantiation type typehandle"); 296 } 297 298 // We need to create a RuntimeMethodHandle for this method 299 MethodEntrypointPtr entrypoint = s_methodEntrypointHash.GetOrCreateValue(new MethodEntrypointLookup(canonMethod)); 300 if (entrypoint.MethodCode != IntPtr.Zero) 301 entryPoint = entrypoint.MethodCode; 302 else 303 entryPoint = entrypoint.MethodEntrypointThunk; 304 305 return true; 306 } 307 } 308 }