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 }