b9a5b1353499f284e16f7af765b7cc4731b546d2
[platform/upstream/dotnet/runtime.git] /
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
4 using System;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Runtime.InteropServices;
8 using System.Runtime.CompilerServices;
9 using System.Runtime;
10
11 using Internal.Runtime.Augments;
12
13 namespace Internal.Runtime.CompilerServices
14 {
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.
22     [ReflectionBlocked]
23     public struct OpenMethodResolver : IEquatable<OpenMethodResolver>
24     {
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;
27
28         public const short DispatchResolve = 0;
29         public const short GVMResolve = 1;
30         public const short OpenNonVirtualResolve = 2;
31         public const short OpenNonVirtualResolveLookthruUnboxing = 3;
32
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;
39
40         public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, int slot, GCHandle readerGCHandle, int handle)
41         {
42             _resolveType = DispatchResolve;
43             _declaringType = declaringTypeOfSlot.ToEETypePtr();
44             _methodHandleOrSlotOrCodePointer = new IntPtr(slot);
45             _handle = handle;
46             _readerGCHandle = readerGCHandle;
47             _nonVirtualOpenInvokeCodePointer = IntPtr.Zero;
48         }
49
50         public unsafe OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHandle gvmSlot, GCHandle readerGCHandle, int handle)
51         {
52             _resolveType = GVMResolve;
53             _methodHandleOrSlotOrCodePointer = *(IntPtr*)&gvmSlot;
54             _declaringType = declaringTypeOfSlot.ToEETypePtr();
55             _handle = handle;
56             _readerGCHandle = readerGCHandle;
57             _nonVirtualOpenInvokeCodePointer = IntPtr.Zero;
58
59             if (s_lazyGvmLookupForSlot == null)
60                 s_lazyGvmLookupForSlot = &TypeLoaderExports.GVMLookupForSlot;
61         }
62
63         public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle)
64         {
65             _resolveType = OpenNonVirtualResolve;
66             _nonVirtualOpenInvokeCodePointer = _methodHandleOrSlotOrCodePointer = codePointer;
67             _declaringType = declaringType.ToEETypePtr();
68             _handle = handle;
69             _readerGCHandle = readerGCHandle;
70         }
71
72         public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle, short resolveType)
73         {
74             _resolveType = resolveType;
75             _methodHandleOrSlotOrCodePointer = codePointer;
76             _declaringType = declaringType.ToEETypePtr();
77             _handle = handle;
78             _readerGCHandle = readerGCHandle;
79             if (resolveType == OpenNonVirtualResolve)
80                 _nonVirtualOpenInvokeCodePointer = codePointer;
81             else if (resolveType == OpenNonVirtualResolveLookthruUnboxing)
82                 _nonVirtualOpenInvokeCodePointer = RuntimeAugments.TypeLoaderCallbacks.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(codePointer, declaringType);
83             else
84                 throw new NotSupportedException();
85         }
86
87         public short ResolverType
88         {
89             get
90             {
91                 return _resolveType;
92             }
93         }
94
95         public RuntimeTypeHandle DeclaringType
96         {
97             get
98             {
99                 return new RuntimeTypeHandle(_declaringType);
100             }
101         }
102
103         public unsafe RuntimeMethodHandle GVMMethodHandle
104         {
105             get
106             {
107                 IntPtr localIntPtr = _methodHandleOrSlotOrCodePointer;
108                 IntPtr* pMethodHandle = &localIntPtr;
109                 return *(RuntimeMethodHandle*)pMethodHandle;
110             }
111         }
112
113         public bool IsOpenNonVirtualResolve
114         {
115             get
116             {
117                 switch (_resolveType)
118                 {
119                     case OpenNonVirtualResolve:
120                     case OpenNonVirtualResolveLookthruUnboxing:
121                         return true;
122                     default:
123                         return false;
124                 }
125             }
126         }
127
128         public IntPtr CodePointer
129         {
130             get
131             {
132                 return _methodHandleOrSlotOrCodePointer;
133             }
134         }
135
136         public object Reader
137         {
138             get
139             {
140                 return _readerGCHandle.Target;
141             }
142         }
143
144         public int Handle
145         {
146             get
147             {
148                 return _handle;
149             }
150         }
151
152         private unsafe IntPtr ResolveMethod(object thisObject)
153         {
154             if (_resolveType == DispatchResolve)
155             {
156                 return RuntimeImports.RhResolveDispatch(thisObject, _declaringType, (ushort)_methodHandleOrSlotOrCodePointer);
157             }
158             else if (_resolveType == GVMResolve)
159             {
160                 return s_lazyGvmLookupForSlot(thisObject, GVMMethodHandle);
161             }
162             else
163             {
164                 throw new NotSupportedException(); // Should never happen, in this case, the dispatch should be resolved in the other ResolveMethod function
165             }
166         }
167
168         internal static unsafe IntPtr ResolveMethodWorker(IntPtr resolver, object thisObject)
169         {
170             return ((OpenMethodResolver*)resolver)->ResolveMethod(thisObject);
171         }
172
173         public static unsafe IntPtr ResolveMethod(IntPtr resolver, object thisObject)
174         {
175             IntPtr nonVirtualOpenInvokeCodePointer = ((OpenMethodResolver*)resolver)->_nonVirtualOpenInvokeCodePointer;
176             if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero)
177                 return nonVirtualOpenInvokeCodePointer;
178
179             return TypeLoaderExports.OpenInstanceMethodLookup(resolver, thisObject);
180         }
181
182         public static unsafe IntPtr ResolveMethod(IntPtr resolverPtr, RuntimeTypeHandle thisType)
183         {
184             OpenMethodResolver* resolver = ((OpenMethodResolver*)resolverPtr);
185             IntPtr nonVirtualOpenInvokeCodePointer = resolver->_nonVirtualOpenInvokeCodePointer;
186             if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero)
187                 return nonVirtualOpenInvokeCodePointer;
188
189             return RuntimeImports.RhResolveDispatchOnType(thisType.ToEETypePtr(), resolver->_declaringType, (ushort)resolver->_methodHandleOrSlotOrCodePointer);
190         }
191
192         [MethodImpl(MethodImplOptions.AggressiveInlining)]
193         private static int _rotl(int value, int shift)
194         {
195             return (int)(((uint)value << shift) | ((uint)value >> (32 - shift)));
196         }
197
198         private static int CalcHashCode(int hashCode1, int hashCode2, int hashCode3, int hashCode4)
199         {
200             int length = 4;
201
202             int hash1 = 0x449b3ad6;
203             int hash2 = (length << 3) + 0x55399219;
204
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;
209
210             hash1 += _rotl(hash1, 8);
211             hash2 += _rotl(hash2, 8);
212
213             return hash1 ^ hash2;
214         }
215
216         public override int GetHashCode()
217         {
218             return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType.IsNull ? 0 : _declaringType.GetHashCode());
219         }
220
221         public bool Equals(OpenMethodResolver other)
222         {
223             if (other._resolveType != _resolveType)
224                 return false;
225
226             if (other._handle != _handle)
227                 return false;
228
229             if (other._methodHandleOrSlotOrCodePointer != _methodHandleOrSlotOrCodePointer)
230                 return false;
231
232             return other._declaringType.Equals(_declaringType);
233         }
234
235         public override bool Equals(object? obj)
236         {
237             if (!(obj is OpenMethodResolver))
238             {
239                 return false;
240             }
241
242             return ((OpenMethodResolver)obj).Equals(this);
243         }
244
245         private static LowLevelDictionary<OpenMethodResolver, IntPtr> s_internedResolverHash = new LowLevelDictionary<OpenMethodResolver, IntPtr>();
246
247         public unsafe IntPtr ToIntPtr()
248         {
249             lock (s_internedResolverHash)
250             {
251                 IntPtr returnValue;
252                 if (s_internedResolverHash.TryGetValue(this, out returnValue))
253                     return returnValue;
254                 returnValue = (IntPtr)NativeMemory.Alloc((nuint)sizeof(OpenMethodResolver));
255                 *((OpenMethodResolver*)returnValue) = this;
256                 s_internedResolverHash.Add(this, returnValue);
257                 return returnValue;
258             }
259         }
260     }
261 }