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