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 ////////////////////////////////////////////////////////////////////////////////
7 //
8 // RuntimeHelpers
9 //    This class defines a set of static methods that provide support for compilers.
10 //
11 
12 using Internal.Reflection.Augments;
13 using Internal.Reflection.Core.NonPortable;
14 using Internal.Runtime.Augments;
15 using System.Runtime;
16 using System.Runtime.Serialization;
17 using System.Threading;
18 
19 namespace System.Runtime.CompilerServices
20 {
21     public static class RuntimeHelpers
22     {
23         [Intrinsic]
InitializeArray(Array array, RuntimeFieldHandle fldHandle)24         public static void InitializeArray(Array array, RuntimeFieldHandle fldHandle)
25         {
26             // We only support this intrinsic when it occurs within a well-defined IL sequence.
27             // If a call to this method occurs within the recognized sequence, codegen must expand the IL sequence completely.
28             // For any other purpose, the API is currently unsupported.
29             // https://github.com/dotnet/corert/issues/364
30             throw new PlatformNotSupportedException();
31         }
32 
RunClassConstructor(RuntimeTypeHandle type)33         public static void RunClassConstructor(RuntimeTypeHandle type)
34         {
35             if (type.IsNull)
36                 throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized);
37 
38             IntPtr pStaticClassConstructionContext = RuntimeAugments.Callbacks.TryGetStaticClassConstructionContext(type);
39             if (pStaticClassConstructionContext == IntPtr.Zero)
40                 return;
41 
42             unsafe
43             {
44                 ClassConstructorRunner.EnsureClassConstructorRun((StaticClassConstructionContext*)pStaticClassConstructionContext);
45             }
46         }
47 
RunModuleConstructor(ModuleHandle module)48         public static void RunModuleConstructor(ModuleHandle module)
49         {
50             if (module.AssociatedModule == null)
51                 throw new ArgumentException(SR.InvalidOperation_HandleIsNotInitialized);
52 
53             ReflectionAugments.ReflectionCoreCallbacks.RunModuleConstructor(module.AssociatedModule);
54         }
55 
GetObjectValue(Object obj)56         public static Object GetObjectValue(Object obj)
57         {
58             if (obj == null)
59                 return null;
60 
61             EETypePtr eeType = obj.EETypePtr;
62             if ((!eeType.IsValueType) || eeType.IsPrimitive)
63                 return obj;
64 
65             return RuntimeImports.RhMemberwiseClone(obj);
66         }
67 
Equals(Object obj1, Object obj2)68         public new static bool Equals(Object obj1, Object obj2)
69         {
70             if (obj1 == obj2)
71                 return true;
72 
73             if ((obj1 == null) || (obj2 == null))
74                 return false;
75 
76             // If it's not a value class, don't compare by value
77             if (!obj1.EETypePtr.IsValueType)
78                 return false;
79 
80             // Make sure they are the same type.
81             if (obj1.EETypePtr != obj2.EETypePtr)
82                 return false;
83 
84             return RuntimeImports.RhCompareObjectContentsAndPadding(obj1, obj2);
85         }
86 
87 #if !FEATURE_SYNCTABLE
88         private const int HASHCODE_BITS = 26;
89         private const int MASK_HASHCODE = (1 << HASHCODE_BITS) - 1;
90 #endif
91 
92         [ThreadStatic]
93         private static int t_hashSeed;
94 
GetNewHashCode()95         internal static int GetNewHashCode()
96         {
97             int multiplier = Environment.CurrentManagedThreadId * 4 + 5;
98             // Every thread has its own generator for hash codes so that we won't get into a situation
99             // where two threads consistently give out the same hash codes.
100             // Choice of multiplier guarantees period of 2**32 - see Knuth Vol 2 p16 (3.2.1.2 Theorem A).
101             t_hashSeed = t_hashSeed * multiplier + 1;
102             return t_hashSeed;
103         }
104 
GetHashCode(Object o)105         public static unsafe int GetHashCode(Object o)
106         {
107 #if FEATURE_SYNCTABLE
108             return ObjectHeader.GetHashCode(o);
109 #else
110             if (o == null)
111                 return 0;
112 
113             fixed (IntPtr* pEEType = &o.m_pEEType)
114             {
115                 int* pSyncBlockIndex = (int*)((byte*)pEEType - 4); // skipping exactly 4 bytes for the SyncTableEntry (exactly 4 bytes not a pointer size).
116                 int hash = *pSyncBlockIndex & MASK_HASHCODE;
117 
118                 if (hash == 0)
119                     return MakeHashCode(o, pSyncBlockIndex);
120                 else
121                     return hash;
122             }
123 #endif
124         }
125 
126 #if !FEATURE_SYNCTABLE
MakeHashCode(Object o, int* pSyncBlockIndex)127         private static unsafe int MakeHashCode(Object o, int* pSyncBlockIndex)
128         {
129             int hash = GetNewHashCode() & MASK_HASHCODE;
130 
131             if (hash == 0)
132                 hash = 1;
133 
134             while (true)
135             {
136                 int oldIndex = Volatile.Read(ref *pSyncBlockIndex);
137 
138                 int currentHash = oldIndex & MASK_HASHCODE;
139                 if (currentHash != 0)
140                 {
141                     // Someone else set the hash code.
142                     hash = currentHash;
143                     break;
144                 }
145 
146                 int newIndex = oldIndex | hash;
147 
148                 if (Interlocked.CompareExchange(ref *pSyncBlockIndex, newIndex, oldIndex) == oldIndex)
149                     break;
150                 // If we get here someone else modified the header.  They may have set the hash code, or maybe some
151                 // other bits.  Let's try again.
152             }
153 
154             return hash;
155         }
156 #endif
157 
158         public static int OffsetToStringData
159         {
160             // Workaround to allow WebAssembly to define a size here without a special CoreLib build
161             // https://github.com/dotnet/corert/issues/4506 includes removing this.
162             [Intrinsic]
163             get
164             {
165                 // Number of bytes from the address pointed to by a reference to
166                 // a String to the first 16-bit character in the String.
167                 // This property allows C#'s fixed statement to work on Strings.
168                 return String.FIRST_CHAR_OFFSET;
169             }
170         }
171 
172         [ThreadStatic]
173         private static unsafe byte* t_sufficientStackLimit;
174 
EnsureSufficientExecutionStack()175         public static unsafe void EnsureSufficientExecutionStack()
176         {
177             byte* limit = t_sufficientStackLimit;
178             if (limit == null)
179                 limit = GetSufficientStackLimit();
180 
181             byte* currentStackPtr = (byte*)(&limit);
182             if (currentStackPtr < limit)
183                 throw new InsufficientExecutionStackException();
184         }
185 
TryEnsureSufficientExecutionStack()186         public static unsafe bool TryEnsureSufficientExecutionStack()
187         {
188             byte* limit = t_sufficientStackLimit;
189             if (limit == null)
190                 limit = GetSufficientStackLimit();
191 
192             byte* currentStackPtr = (byte*)(&limit);
193             return (currentStackPtr >= limit);
194         }
195 
196         [MethodImpl(MethodImplOptions.NoInlining)] // Only called once per thread, no point in inlining.
GetSufficientStackLimit()197         private static unsafe byte* GetSufficientStackLimit()
198         {
199             IntPtr lower, upper;
200             RuntimeImports.RhGetCurrentThreadStackBounds(out lower, out upper);
201 
202             // Compute the limit used by EnsureSufficientExecutionStack and cache it on the thread. This minimum
203             // stack size should be sufficient to allow a typical non-recursive call chain to execute, including
204             // potential exception handling and garbage collection.
205 
206 #if BIT64
207             const int MinExecutionStackSize = 128 * 1024;
208 #else
209             const int MinExecutionStackSize = 64 * 1024;
210 #endif
211 
212             byte* limit = (((byte*)upper - (byte*)lower > MinExecutionStackSize)) ?
213                 ((byte*)lower + MinExecutionStackSize) : ((byte*)upper);
214 
215             return (t_sufficientStackLimit = limit);
216         }
217 
218         [Intrinsic]
IsReferenceOrContainsReferences()219         public static bool IsReferenceOrContainsReferences<T>()
220         {
221             var pEEType = EETypePtr.EETypePtrOf<T>();
222             return !pEEType.IsValueType || pEEType.HasPointers;
223         }
224 
225         [Intrinsic]
IsReference()226         public static bool IsReference<T>()
227         {
228             var pEEType = EETypePtr.EETypePtrOf<T>();
229             return !pEEType.IsValueType;
230         }
231 
232         // Constrained Execution Regions APIs are NOP's because we do not support CERs in .NET Core at all.
ProbeForSufficientStack()233         public static void ProbeForSufficientStack() { }
PrepareConstrainedRegions()234         public static void PrepareConstrainedRegions() { }
PrepareConstrainedRegionsNoOP()235         public static void PrepareConstrainedRegionsNoOP() { }
PrepareMethod(RuntimeMethodHandle method)236         public static void PrepareMethod(RuntimeMethodHandle method) { }
PrepareMethod(RuntimeMethodHandle method, RuntimeTypeHandle[] instantiation)237         public static void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeHandle[] instantiation) { }
PrepareContractedDelegate(Delegate d)238         public static void PrepareContractedDelegate(Delegate d) { }
PrepareDelegate(Delegate d)239         public static void PrepareDelegate(Delegate d)
240         {
241             if (d == null)
242                 throw new ArgumentNullException(nameof(d));
243         }
244 
ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)245         public static void ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
246         {
247             if (code == null)
248                 throw new ArgumentNullException(nameof(code));
249             if (backoutCode == null)
250                 throw new ArgumentNullException(nameof(backoutCode));
251 
252             bool exceptionThrown = false;
253 
254             try
255             {
256                 code(userData);
257             }
258             catch
259             {
260                 exceptionThrown = true;
261                 throw;
262             }
263             finally
264             {
265                 backoutCode(userData, exceptionThrown);
266             }
267         }
268 
TryCode(Object userData)269         public delegate void TryCode(Object userData);
CleanupCode(Object userData, bool exceptionThrown)270         public delegate void CleanupCode(Object userData, bool exceptionThrown);
271 
GetUninitializedObject(Type type)272         public static object GetUninitializedObject(Type type)
273         {
274             if (type == null)
275             {
276                 throw new ArgumentNullException(nameof(type), SR.ArgumentNull_Type);
277             }
278 
279             if(!type.IsRuntimeImplemented())
280             {
281                 throw new SerializationException(SR.Format(SR.Serialization_InvalidType, type.ToString()));
282             }
283 
284             if (type.HasElementType || type.IsGenericParameter)
285             {
286                 throw new ArgumentException(SR.Argument_InvalidValue);
287             }
288 
289             if (type.ContainsGenericParameters)
290             {
291                 throw new MemberAccessException(SR.Acc_CreateGeneric);
292             }
293 
294             if (type.IsCOMObject)
295             {
296                 throw new NotSupportedException(SR.NotSupported_ManagedActivation);
297             }
298 
299             EETypePtr eeTypePtr = type.TypeHandle.ToEETypePtr();
300 
301             if (eeTypePtr == EETypePtr.EETypePtrOf<string>())
302             {
303                 throw new ArgumentException(SR.Argument_NoUninitializedStrings);
304             }
305 
306             if (eeTypePtr.IsAbstract)
307             {
308                 throw new MemberAccessException(SR.Acc_CreateAbst);
309             }
310 
311             if (eeTypePtr.IsByRefLike)
312             {
313                 throw new NotSupportedException(SR.NotSupported_ByRefLike);
314             }
315 
316             if (eeTypePtr.IsNullable)
317             {
318                 return GetUninitializedObject(RuntimeTypeUnifier.GetRuntimeTypeForEEType(eeTypePtr.NullableType));
319             }
320 
321             // Triggering the .cctor here is slightly different than desktop/CoreCLR, which
322             // decide based on BeforeFieldInit, but we don't want to include BeforeFieldInit
323             // in EEType just for this API to behave slightly differently.
324             RunClassConstructor(type.TypeHandle);
325 
326             return RuntimeImports.RhNewObject(eeTypePtr);
327         }
328     }
329 }
330