1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Runtime.InteropServices;
8 using System.Runtime.CompilerServices;
11 using Internal.Runtime.Augments;
13 namespace Internal.Runtime.CompilerServices
15 // This structure is used to resolve a instance method given an object instance. To use this type
16 // 1) New up an instance using one of the constructors below.
17 // 2) Use the ToIntPtr() method to get the interned instance of this type. This will permanently allocate
18 // a block of memory that can be used to represent a virtual method resolution. This memory is interned
19 // so that repeated allocation of the same resolver will not leak.
20 // 3) Use the ResolveMethod function to do the virtual lookup. This function takes advantage of
21 // a lockless cache so the resolution is very fast for repeated lookups.
23 public struct OpenMethodResolver : IEquatable<OpenMethodResolver>
25 // Lazy initialized to point to the type loader method when the first `GVMResolve` resolver is created
26 private static unsafe delegate*<object, RuntimeMethodHandle, nint> s_lazyGvmLookupForSlot;
28 public const short DispatchResolve = 0;
29 public const short GVMResolve = 1;
30 public const short OpenNonVirtualResolve = 2;
31 public const short OpenNonVirtualResolveLookthruUnboxing = 3;
33 private readonly short _resolveType;
34 private readonly GCHandle _readerGCHandle;
35 private readonly int _handle;
36 private readonly IntPtr _methodHandleOrSlotOrCodePointer;
37 private readonly IntPtr _nonVirtualOpenInvokeCodePointer;
38 private readonly EETypePtr _declaringType;
40 public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, int slot, GCHandle readerGCHandle, int handle)
42 _resolveType = DispatchResolve;
43 _declaringType = declaringTypeOfSlot.ToEETypePtr();
44 _methodHandleOrSlotOrCodePointer = new IntPtr(slot);
46 _readerGCHandle = readerGCHandle;
47 _nonVirtualOpenInvokeCodePointer = IntPtr.Zero;
50 public unsafe OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHandle gvmSlot, GCHandle readerGCHandle, int handle)
52 _resolveType = GVMResolve;
53 _methodHandleOrSlotOrCodePointer = *(IntPtr*)&gvmSlot;
54 _declaringType = declaringTypeOfSlot.ToEETypePtr();
56 _readerGCHandle = readerGCHandle;
57 _nonVirtualOpenInvokeCodePointer = IntPtr.Zero;
59 if (s_lazyGvmLookupForSlot == null)
60 s_lazyGvmLookupForSlot = &TypeLoaderExports.GVMLookupForSlot;
63 public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle)
65 _resolveType = OpenNonVirtualResolve;
66 _nonVirtualOpenInvokeCodePointer = _methodHandleOrSlotOrCodePointer = codePointer;
67 _declaringType = declaringType.ToEETypePtr();
69 _readerGCHandle = readerGCHandle;
72 public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle, short resolveType)
74 _resolveType = resolveType;
75 _methodHandleOrSlotOrCodePointer = codePointer;
76 _declaringType = declaringType.ToEETypePtr();
78 _readerGCHandle = readerGCHandle;
79 if (resolveType == OpenNonVirtualResolve)
80 _nonVirtualOpenInvokeCodePointer = codePointer;
81 else if (resolveType == OpenNonVirtualResolveLookthruUnboxing)
82 _nonVirtualOpenInvokeCodePointer = RuntimeAugments.TypeLoaderCallbacks.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(codePointer, declaringType);
84 throw new NotSupportedException();
87 public short ResolverType
95 public RuntimeTypeHandle DeclaringType
99 return new RuntimeTypeHandle(_declaringType);
103 public unsafe RuntimeMethodHandle GVMMethodHandle
107 IntPtr localIntPtr = _methodHandleOrSlotOrCodePointer;
108 IntPtr* pMethodHandle = &localIntPtr;
109 return *(RuntimeMethodHandle*)pMethodHandle;
113 public bool IsOpenNonVirtualResolve
117 switch (_resolveType)
119 case OpenNonVirtualResolve:
120 case OpenNonVirtualResolveLookthruUnboxing:
128 public IntPtr CodePointer
132 return _methodHandleOrSlotOrCodePointer;
140 return _readerGCHandle.Target;
152 private unsafe IntPtr ResolveMethod(object thisObject)
154 if (_resolveType == DispatchResolve)
156 return RuntimeImports.RhResolveDispatch(thisObject, _declaringType, (ushort)_methodHandleOrSlotOrCodePointer);
158 else if (_resolveType == GVMResolve)
160 return s_lazyGvmLookupForSlot(thisObject, GVMMethodHandle);
164 throw new NotSupportedException(); // Should never happen, in this case, the dispatch should be resolved in the other ResolveMethod function
168 internal static unsafe IntPtr ResolveMethodWorker(IntPtr resolver, object thisObject)
170 return ((OpenMethodResolver*)resolver)->ResolveMethod(thisObject);
173 public static unsafe IntPtr ResolveMethod(IntPtr resolver, object thisObject)
175 IntPtr nonVirtualOpenInvokeCodePointer = ((OpenMethodResolver*)resolver)->_nonVirtualOpenInvokeCodePointer;
176 if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero)
177 return nonVirtualOpenInvokeCodePointer;
179 return TypeLoaderExports.OpenInstanceMethodLookup(resolver, thisObject);
182 public static unsafe IntPtr ResolveMethod(IntPtr resolverPtr, RuntimeTypeHandle thisType)
184 OpenMethodResolver* resolver = ((OpenMethodResolver*)resolverPtr);
185 IntPtr nonVirtualOpenInvokeCodePointer = resolver->_nonVirtualOpenInvokeCodePointer;
186 if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero)
187 return nonVirtualOpenInvokeCodePointer;
189 return RuntimeImports.RhResolveDispatchOnType(thisType.ToEETypePtr(), resolver->_declaringType, (ushort)resolver->_methodHandleOrSlotOrCodePointer);
192 [MethodImpl(MethodImplOptions.AggressiveInlining)]
193 private static int _rotl(int value, int shift)
195 return (int)(((uint)value << shift) | ((uint)value >> (32 - shift)));
198 private static int CalcHashCode(int hashCode1, int hashCode2, int hashCode3, int hashCode4)
202 int hash1 = 0x449b3ad6;
203 int hash2 = (length << 3) + 0x55399219;
205 hash1 = (hash1 + _rotl(hash1, 5)) ^ hashCode1;
206 hash2 = (hash2 + _rotl(hash2, 5)) ^ hashCode2;
207 hash1 = (hash1 + _rotl(hash1, 5)) ^ hashCode3;
208 hash2 = (hash2 + _rotl(hash2, 5)) ^ hashCode4;
210 hash1 += _rotl(hash1, 8);
211 hash2 += _rotl(hash2, 8);
213 return hash1 ^ hash2;
216 public override int GetHashCode()
218 return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType.IsNull ? 0 : _declaringType.GetHashCode());
221 public bool Equals(OpenMethodResolver other)
223 if (other._resolveType != _resolveType)
226 if (other._handle != _handle)
229 if (other._methodHandleOrSlotOrCodePointer != _methodHandleOrSlotOrCodePointer)
232 return other._declaringType.Equals(_declaringType);
235 public override bool Equals(object? obj)
237 if (!(obj is OpenMethodResolver))
242 return ((OpenMethodResolver)obj).Equals(this);
245 private static LowLevelDictionary<OpenMethodResolver, IntPtr> s_internedResolverHash = new LowLevelDictionary<OpenMethodResolver, IntPtr>();
247 public unsafe IntPtr ToIntPtr()
249 lock (s_internedResolverHash)
252 if (s_internedResolverHash.TryGetValue(this, out returnValue))
254 returnValue = (IntPtr)NativeMemory.Alloc((nuint)sizeof(OpenMethodResolver));
255 *((OpenMethodResolver*)returnValue) = this;
256 s_internedResolverHash.Add(this, returnValue);