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 using System.Reflection.Runtime.General; 13 14 using Internal.Runtime; 15 using Internal.Runtime.Augments; 16 using Internal.Runtime.CompilerServices; 17 18 using Internal.Metadata.NativeFormat; 19 using Internal.NativeFormat; 20 using Internal.TypeSystem; 21 using Internal.TypeSystem.NativeFormat; 22 23 namespace Internal.Runtime.TypeLoader 24 { 25 public sealed partial class TypeLoaderEnvironment 26 { 27 public enum MethodAddressType 28 { 29 None, 30 Exact, 31 Canonical, 32 UniversalCanonical 33 } 34 35 /// <summary> 36 /// Resolve a MethodDesc to a callable method address and unboxing stub address. 37 /// </summary> 38 /// <param name="method">Native metadata method description object</param> 39 /// <param name="methodAddress">Resolved method address</param> 40 /// <param name="unboxingStubAddress">Resolved unboxing stub address</param> 41 /// <returns>true when the resolution succeeded, false when not</returns> TryGetMethodAddressFromMethodDesc( MethodDesc method, out IntPtr methodAddress, out IntPtr unboxingStubAddress, out MethodAddressType foundAddressType)42 internal static bool TryGetMethodAddressFromMethodDesc( 43 MethodDesc method, 44 out IntPtr methodAddress, 45 out IntPtr unboxingStubAddress, 46 out MethodAddressType foundAddressType) 47 { 48 methodAddress = IntPtr.Zero; 49 unboxingStubAddress = IntPtr.Zero; 50 foundAddressType = MethodAddressType.None; 51 52 #if SUPPORTS_R2R_LOADING 53 TryGetCodeTableEntry(method, out methodAddress, out unboxingStubAddress, out foundAddressType); 54 #endif 55 #if SUPPORT_JIT 56 if (foundAddressType == MethodAddressType.None) 57 MethodEntrypointStubs.TryGetMethodEntrypoint(method, out methodAddress, out unboxingStubAddress, out foundAddressType); 58 #endif 59 if (foundAddressType != MethodAddressType.None) 60 return true; 61 62 // Otherwise try to find it via an invoke map 63 return TryGetMethodAddressFromTypeSystemMethodViaInvokeMap(method, out methodAddress, out unboxingStubAddress, out foundAddressType); 64 } 65 66 /// <summary> 67 /// Resolve a MethodDesc to a callable method address and unboxing stub address by searching 68 /// by searching in the InvokeMaps. This function is a wrapper around TryGetMethodInvokeDataFromInvokeMap 69 /// that produces output in the format which matches the code table system. 70 /// </summary> 71 /// <param name="method">Native metadata method description object</param> 72 /// <param name="methodAddress">Resolved method address</param> 73 /// <param name="unboxingStubAddress">Resolved unboxing stub address</param> 74 /// <returns>true when the resolution succeeded, false when not</returns> TryGetMethodAddressFromTypeSystemMethodViaInvokeMap( MethodDesc method, out IntPtr methodAddress, out IntPtr unboxingStubAddress, out MethodAddressType foundAddressType)75 private static bool TryGetMethodAddressFromTypeSystemMethodViaInvokeMap( 76 MethodDesc method, 77 out IntPtr methodAddress, 78 out IntPtr unboxingStubAddress, 79 out MethodAddressType foundAddressType) 80 { 81 methodAddress = IntPtr.Zero; 82 unboxingStubAddress = IntPtr.Zero; 83 foundAddressType = MethodAddressType.None; 84 #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING 85 NativeFormatMethod nativeFormatMethod = method.GetTypicalMethodDefinition() as NativeFormatMethod; 86 if (nativeFormatMethod == null) 87 return false; 88 89 MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer( 90 nativeFormatMethod.MetadataReader, nativeFormatMethod.Handle); 91 92 // Try to find a specific canonical match, or if that fails, a universal match 93 if (TryGetMethodInvokeDataFromInvokeMap( 94 nativeFormatMethod, 95 method, 96 ref methodSignatureComparer, 97 CanonicalFormKind.Specific, 98 out methodAddress, 99 out foundAddressType) || 100 101 TryGetMethodInvokeDataFromInvokeMap( 102 nativeFormatMethod, 103 method, 104 ref methodSignatureComparer, 105 CanonicalFormKind.Universal, 106 out methodAddress, 107 out foundAddressType)) 108 { 109 if (method.OwningType.IsValueType && !method.Signature.IsStatic) 110 { 111 // In this case the invoke map found an unboxing stub, and we should pull the method address out as well 112 unboxingStubAddress = methodAddress; 113 methodAddress = RuntimeAugments.GetCodeTarget(unboxingStubAddress); 114 115 if (!method.HasInstantiation && ((foundAddressType != MethodAddressType.Exact) || method.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))) 116 { 117 IntPtr underlyingTarget; // unboxing and instantiating stub handling 118 if (!TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(methodAddress, out underlyingTarget)) 119 { 120 Environment.FailFast("Expected this to be an unboxing and instantiating stub."); 121 } 122 methodAddress = underlyingTarget; 123 } 124 } 125 126 return true; 127 } 128 129 #endif 130 return false; 131 } 132 133 /// <summary> 134 /// Attempt a virtual dispatch on a given instanceType based on the method found via a metadata token 135 /// </summary> TryDispatchMethodOnTarget_Inner(NativeFormatModuleInfo module, int metadataToken, RuntimeTypeHandle targetInstanceType, out IntPtr methodAddress)136 private static bool TryDispatchMethodOnTarget_Inner(NativeFormatModuleInfo module, int metadataToken, RuntimeTypeHandle targetInstanceType, out IntPtr methodAddress) 137 { 138 #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING 139 TypeSystemContext context = TypeSystemContextFactory.Create(); 140 141 NativeFormatMetadataUnit metadataUnit = context.ResolveMetadataUnit(module); 142 MethodDesc targetMethod = metadataUnit.GetMethod(metadataToken.AsHandle(), null); 143 TypeDesc instanceType = context.ResolveRuntimeTypeHandle(targetInstanceType); 144 145 MethodDesc realTargetMethod = targetMethod; 146 147 // For non-interface methods we support the target method not being the exact target. (This allows 148 // a canonical method to be passed in and work for any generic type instantiation.) 149 if (!targetMethod.OwningType.IsInterface) 150 realTargetMethod = instanceType.FindMethodOnTypeWithMatchingTypicalMethod(targetMethod); 151 152 bool success = LazyVTableResolver.TryDispatchMethodOnTarget(instanceType, realTargetMethod, out methodAddress); 153 154 TypeSystemContextFactory.Recycle(context); 155 return success; 156 #else 157 methodAddress = IntPtr.Zero; 158 return false; 159 #endif 160 } 161 162 #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING 163 #if DEBUG 164 private static int s_ConvertDispatchCellInfoCounter; 165 #endif 166 167 /// <summary> 168 /// Attempt to convert the dispatch cell to a metadata token to a more efficient vtable dispatch or interface/slot dispatch. 169 /// Failure to convert is not a correctness issue. We also support performing a dispatch based on metadata token alone. 170 /// </summary> ConvertDispatchCellInfo_Inner(NativeFormatModuleInfo module, DispatchCellInfo cellInfo)171 private static DispatchCellInfo ConvertDispatchCellInfo_Inner(NativeFormatModuleInfo module, DispatchCellInfo cellInfo) 172 { 173 Debug.Assert(cellInfo.CellType == DispatchCellType.MetadataToken); 174 175 TypeSystemContext context = TypeSystemContextFactory.Create(); 176 177 MethodDesc targetMethod = context.ResolveMetadataUnit(module).GetMethod(cellInfo.MetadataToken.AsHandle(), null); 178 Debug.Assert(!targetMethod.HasInstantiation); // At this time we do not support generic virtuals through the dispatch mechanism 179 Debug.Assert(targetMethod.IsVirtual); 180 if (targetMethod.OwningType.IsInterface) 181 { 182 if (!LazyVTableResolver.TryGetInterfaceSlotNumberFromMethod(targetMethod, out cellInfo.InterfaceSlot)) 183 { 184 // Unable to resolve interface method. Fail, by not mutating cellInfo 185 return cellInfo; 186 } 187 188 if (!targetMethod.OwningType.RetrieveRuntimeTypeHandleIfPossible()) 189 { 190 new TypeBuilder().BuildType(targetMethod.OwningType); 191 } 192 193 cellInfo.CellType = DispatchCellType.InterfaceAndSlot; 194 cellInfo.InterfaceType = targetMethod.OwningType.RuntimeTypeHandle.ToIntPtr(); 195 cellInfo.MetadataToken = 0; 196 } 197 else 198 { 199 // Virtual function case, attempt to resolve to a VTable slot offset. 200 // If the offset is less than 4096 update the cellInfo 201 #if DEBUG 202 // The path of resolving a metadata token at dispatch time is relatively rare in practice. 203 // Force it to occur in debug builds with much more regularity 204 if ((s_ConvertDispatchCellInfoCounter % 16) == 0) 205 { 206 s_ConvertDispatchCellInfoCounter++; 207 TypeSystemContextFactory.Recycle(context); 208 return cellInfo; 209 } 210 s_ConvertDispatchCellInfoCounter++; 211 #endif 212 213 int slotIndexOfMethod = LazyVTableResolver.VirtualMethodToSlotIndex(targetMethod); 214 int vtableOffset = -1; 215 if (slotIndexOfMethod >= 0) 216 vtableOffset = LazyVTableResolver.SlotIndexToEETypeVTableOffset(slotIndexOfMethod); 217 if ((vtableOffset < 4096) && (vtableOffset != -1)) 218 { 219 cellInfo.CellType = DispatchCellType.VTableOffset; 220 cellInfo.VTableOffset = checked((uint)vtableOffset); 221 cellInfo.MetadataToken = 0; 222 } 223 // Otherwise, do nothing, and resolve with a metadata dispatch later 224 } 225 226 TypeSystemContextFactory.Recycle(context); 227 return cellInfo; 228 } 229 #endif 230 231 /// <summary> 232 /// Resolve a dispatch on an interface EEType/slot index pair to a function pointer 233 /// </summary> TryResolveTypeSlotDispatch_Inner(IntPtr targetTypeAsIntPtr, IntPtr interfaceTypeAsIntPtr, ushort slot, out IntPtr methodAddress)234 private bool TryResolveTypeSlotDispatch_Inner(IntPtr targetTypeAsIntPtr, IntPtr interfaceTypeAsIntPtr, ushort slot, out IntPtr methodAddress) 235 { 236 methodAddress = IntPtr.Zero; 237 238 #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING 239 TypeSystemContext context = TypeSystemContextFactory.Create(); 240 241 TypeDesc targetType; 242 TypeDesc interfaceType; 243 244 unsafe 245 { 246 targetType = context.ResolveRuntimeTypeHandle(((EEType*)targetTypeAsIntPtr.ToPointer())->ToRuntimeTypeHandle()); 247 interfaceType = context.ResolveRuntimeTypeHandle(((EEType*)interfaceTypeAsIntPtr.ToPointer())->ToRuntimeTypeHandle()); 248 } 249 250 if (!(interfaceType.GetTypeDefinition() is MetadataType)) 251 { 252 // If the interface open type is not a metadata type, this must be an interface not known in the metadata world. 253 // Use the redhawk resolver for this directly. 254 TypeDesc pregeneratedType = LazyVTableResolver.GetMostDerivedPregeneratedOrTemplateLoadedType(targetType); 255 pregeneratedType.RetrieveRuntimeTypeHandleIfPossible(); 256 interfaceType.RetrieveRuntimeTypeHandleIfPossible(); 257 methodAddress = RuntimeAugments.ResolveDispatchOnType(pregeneratedType.RuntimeTypeHandle, interfaceType.RuntimeTypeHandle, slot); 258 } 259 else 260 { 261 MethodDesc interfaceMethod; 262 263 if (!LazyVTableResolver.TryGetMethodFromInterfaceSlot(interfaceType, slot, out interfaceMethod)) 264 return false; 265 266 if (!LazyVTableResolver.TryDispatchMethodOnTarget(targetType, interfaceMethod, out methodAddress)) 267 return false; 268 } 269 270 TypeSystemContextFactory.Recycle(context); 271 272 return true; 273 #else 274 return false; 275 #endif 276 } 277 } 278 } 279