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