[MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path) internal static unsafe T[] AllocateUninitializedArray <T>(int length) { if (RuntimeHelpers.IsReferenceOrContainsReferences <T>()) { return(new T[length]); } // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs #if !DEBUG // small arrays are allocated using `new[]` as that is generally faster. if (length < 2048 / Unsafe.SizeOf <T>()) { return(new T[length]); } #endif // kept outside of the small arrays hot path to have inlining without big size growth return(AllocateNewUninitializedArray(length)); T[] AllocateNewUninitializedArray(int length) { if (length < 0) { throw new OverflowException(); } T[] array = null; RuntimeImports.RhAllocateUninitializedArray(EETypePtr.EETypePtrOf <T[]>().RawValue, (uint)length, Unsafe.AsPointer(ref array)); if (array == null) { throw new OutOfMemoryException(); } return(array); } }
public static unsafe T CreateInstance <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>() { // Grab the pointer to the default constructor of the type. If T doesn't have a default // constructor, the intrinsic returns a marker pointer that we check for. IntPtr defaultConstructor = DefaultConstructorOf <T>(); // Check if we got the marker back. // // TODO: might want to disambiguate the different cases for abstract class, interface, etc. if (defaultConstructor == (IntPtr)(delegate * < Guid >) & MissingConstructorMethod) { throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, typeof(T))); } T t; try { // Call the default constructor on the allocated instance. if (RuntimeHelpers.IsReference <T>()) { // Grab a pointer to the optimized allocator for the type and call it. IntPtr allocator = AllocatorOf <T>(); t = RawCalliHelper.Call <T>(allocator, EETypePtr.EETypePtrOf <T>().RawValue); RawCalliHelper.Call(defaultConstructor, t); // Debugger goo so that stepping in works. Only affects debug info generation. // The call gets optimized away. DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } else { t = default !;
internal static string FastAllocateString(int length) { // We allocate one extra char as an interop convenience so that our strings are null- // terminated, however, we don't pass the extra +1 to the string allocation because the base // size of this object includes the _firstChar field. string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf <string>(), length); Debug.Assert(newStr._stringLength == length); return(newStr); }
public static T CreateInstance <T>() { T t = default(T); bool missingDefaultConstructor = false; EETypePtr eetype = EETypePtr.EETypePtrOf <T>(); // ProjectN:936613 - Early exit for variable sized types (strings, arrays, etc.) as we cannot call // CreateInstanceIntrinsic on them since the intrinsic will attempt to allocate an instance of these types // and that is verboten (it results in silent heap corruption!). if (eetype.ComponentSize != 0) { // ComponentSize > 0 indicates an array-like type (e.g. string, array, etc). missingDefaultConstructor = true; } else if (eetype.IsInterface) { // Do not attempt to allocate interface types either missingDefaultConstructor = true; } else { bool oldValueOfMissingDefaultCtorMarkerBool = s_createInstanceMissingDefaultConstructor; try { t = CreateInstanceIntrinsic <T>(); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } catch (Exception e) { throw new TargetInvocationException(e); } if (s_createInstanceMissingDefaultConstructor != oldValueOfMissingDefaultCtorMarkerBool) { missingDefaultConstructor = true; // We didn't call the real .ctor (because there wasn't one), but we still allocated // an uninitialized object. If it has a finalizer, it would run - prevent that. GC.SuppressFinalize(t); } } if (missingDefaultConstructor) { throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, typeof(T))); } return(t); }
static unsafe string Ctor(char *ptr, int index, int length) { var et = EETypePtr.EETypePtrOf <string>(); var start = ptr + index; var data = StartupCodeHelpers.RhpNewArray(et.Value, length); var s = Unsafe.As <object, string>(ref data); fixed(char *c = &s._firstChar) { Platform.CopyMemory((IntPtr)c, (IntPtr)start, (ulong)length * sizeof(char)); c[length] = '\0'; } return(s); }
private static T CreateInstanceIntrinsic <T>() { // Fallback implementation for codegens that don't support this intrinsic. // This uses the type loader and doesn't have the kind of guarantees about it always working // as the intrinsic expansion has. Also, it's slower. EETypePtr eetype = EETypePtr.EETypePtrOf <T>(); // The default(T) check can be evaluated statically and will result in the body of this method // becoming empty for valuetype Ts. We still need a dynamic IsNullable check to cover Nullables though. // This will obviously need work once we start supporting default valuetype constructors. if (default(T) == null && !eetype.IsNullable) { object o = null; TypeLoaderExports.ActivatorCreateInstanceAny(ref o, eetype.RawValue); return((T)o); } return(default(T)); }
// The following 2 methods and helper class implement the functionality of Activator.CreateInstance<T>() // This method is the public surface area. It wraps the CreateInstance intrinsic with the appropriate try/catch // block so that the correct exceptions are generated. Also, it handles the cases where the T type doesn't have // a default constructor. (Those result in running the ClassWithMissingConstructor's .ctor, which flips the magic // thread static to cause the CreateInstance<T> method to throw the right exception.) // public static T CreateInstance <T>() { T t = default(T); bool missingDefaultConstructor = false; // ProjectN:936613 - Early exit for variable sized types (strings, arrays, etc.) as we cannot call // CreateInstanceIntrinsic on them since the intrinsic will attempt to allocate an instance of these types // and that is verboten (it results in silent heap corruption!). if (EETypePtr.EETypePtrOf <T>().ComponentSize != 0) { // ComponentSize > 0 indicates an array-like type (e.g. string, array, etc). missingDefaultConstructor = true; } else { bool oldValueOfMissingDefaultCtorMarkerBool = s_createInstanceMissingDefaultConstructor; try { t = CreateInstanceIntrinsic <T>(); } catch (Exception e) { throw new TargetInvocationException(e); } if (s_createInstanceMissingDefaultConstructor != oldValueOfMissingDefaultCtorMarkerBool) { missingDefaultConstructor = true; } } if (missingDefaultConstructor) { throw new MissingMemberException(SR.Format(SR.MissingConstructor_Name, typeof(T))); } return(t); }
internal static unsafe T[] AllocateUninitializedArray <T>(int length) { if (RuntimeHelpers.IsReferenceOrContainsReferences <T>()) { return(new T[length]); } if (length < 0) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, 0, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } #if DEBUG // in DEBUG arrays of any length can be created uninitialized #else // otherwise small arrays are allocated using `new[]` as that is generally faster. // // The threshold was derived from various simulations. // As it turned out the threshold depends on overal pattern of all allocations and is typically in 200-300 byte range. // The gradient around the number is shallow (there is no perf cliff) and the exact value of the threshold does not matter a lot. // So it is 256 bytes including array header. if (Unsafe.SizeOf <T>() * length < 256 - 3 * IntPtr.Size) { return(new T[length]); } #endif var pEEType = EETypePtr.EETypePtrOf <T[]>(); T[] array = null; RuntimeImports.RhAllocateUninitializedArray(pEEType.RawValue, (uint)length, Unsafe.AsPointer(ref array)); if (array == null) { throw new OutOfMemoryException(); } return(array); }
public static object ToObject(TypedReference value) { RuntimeTypeHandle typeHandle = value._typeHandle; if (typeHandle.IsNull) { throw new ArgumentNullException(); // For compatibility. } EETypePtr eeType = typeHandle.ToEETypePtr(); if (eeType.IsValueType) { return(RuntimeImports.RhBox(eeType, ref value.Value)); } else if (eeType.IsPointer) { return(RuntimeImports.RhBox(EETypePtr.EETypePtrOf <UIntPtr>(), ref value.Value)); } else { return(Unsafe.As <byte, object>(ref value.Value)); } }
static T[] AllocateNewUninitializedArray(int length, bool pinned) { GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL; if (pinned) { flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP; } if (length < 0) { throw new OverflowException(); } T[] array = null; RuntimeImports.RhAllocateNewArray(EETypePtr.EETypePtrOf <T[]>().RawValue, (uint)length, (uint)flags, Unsafe.AsPointer(ref array)); if (array == null) { throw new OutOfMemoryException(); } return(array); }
internal static object CallDynamicInvokeMethod( object thisPtr, IntPtr methodToCall, object thisPtrDynamicInvokeMethod, IntPtr dynamicInvokeHelperMethod, IntPtr dynamicInvokeHelperGenericDictionary, object targetMethodOrDelegate, object[] parameters, BinderBundle binderBundle, bool wrapInTargetInvocationException, bool invokeMethodHelperIsThisCall = true, bool methodToCallIsThisCall = true) { // This assert is needed because we've double-purposed "targetMethodOrDelegate" (which is actually a MethodBase anytime a custom binder is used) // as a way of obtaining the true parameter type which we need to pass to Binder.ChangeType(). (The type normally passed to DynamicInvokeParamHelperCore // isn't always the exact type (byref stripped off, enums converted to int, etc.) Debug.Assert(!(binderBundle != null && !(targetMethodOrDelegate is MethodBase)), "The only callers that can pass a custom binder are those servicing MethodBase.Invoke() apis."); bool parametersNeedCopyBack = false; ArgSetupState argSetupState = default(ArgSetupState); // Capture state of thread static invoke helper statics object[] parametersOld = s_parameters; object[] nullableCopyBackObjectsOld = s_nullableCopyBackObjects; int curIndexOld = s_curIndex; object targetMethodOrDelegateOld = s_targetMethodOrDelegate; BinderBundle binderBundleOld = s_binderBundle; s_binderBundle = binderBundle; object[] customBinderProvidedParametersOld = s_customBinderProvidedParameters; s_customBinderProvidedParameters = null; try { // If the passed in array is not an actual object[] instance, we need to copy it over to an actual object[] // instance so that the rest of the code can safely create managed object references to individual elements. if (parameters != null && EETypePtr.EETypePtrOf <object[]>() != parameters.EETypePtr) { s_parameters = new object[parameters.Length]; Array.Copy(parameters, s_parameters, parameters.Length); parametersNeedCopyBack = true; } else { s_parameters = parameters; } s_nullableCopyBackObjects = null; s_curIndex = 0; s_targetMethodOrDelegate = targetMethodOrDelegate; try { object result = null; if (invokeMethodHelperIsThisCall) { Debug.Assert(methodToCallIsThisCall == true); result = CalliIntrinsics.Call(dynamicInvokeHelperMethod, thisPtrDynamicInvokeMethod, thisPtr, methodToCall, ref argSetupState); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } else { if (dynamicInvokeHelperGenericDictionary != IntPtr.Zero) { result = CalliIntrinsics.Call(dynamicInvokeHelperMethod, dynamicInvokeHelperGenericDictionary, thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } else { result = CalliIntrinsics.Call(dynamicInvokeHelperMethod, thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } } return(result); } catch (Exception e) when(wrapInTargetInvocationException && argSetupState.fComplete) { throw new TargetInvocationException(e); } finally { if (parametersNeedCopyBack) { Array.Copy(s_parameters, parameters, parameters.Length); } if (argSetupState.fComplete) { // Nullable objects can't take advantage of the ability to update the boxed value on the heap directly, so perform // an update of the parameters array now. if (argSetupState.nullableCopyBackObjects != null) { for (int i = 0; i < argSetupState.nullableCopyBackObjects.Length; i++) { if (argSetupState.nullableCopyBackObjects[i] != null) { parameters[i] = DynamicInvokeBoxIntoNonNullable(argSetupState.nullableCopyBackObjects[i]); } } } } } } finally { // Restore state of thread static helper statics s_parameters = parametersOld; s_nullableCopyBackObjects = nullableCopyBackObjectsOld; s_curIndex = curIndexOld; s_targetMethodOrDelegate = targetMethodOrDelegateOld; s_binderBundle = binderBundleOld; s_customBinderProvidedParameters = customBinderProvidedParametersOld; } }
private static unsafe MethodTable *GetSystemArrayEEType() { return(EETypePtr.EETypePtrOf <Array>().ToPointer()); }
public static unsafe void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count) { nuint uSrcLen; nuint uDstLen; if (src == null) { throw new ArgumentNullException(nameof(src)); } if (dst == null) { throw new ArgumentNullException(nameof(dst)); } // Use optimized path for byte arrays since this is the main scenario for Buffer::BlockCopy // We only need an unreliable comparison since the slow path can handle the byte[] case too. if (src.EETypePtr.FastEqualsUnreliable(EETypePtr.EETypePtrOf <byte[]>())) { uSrcLen = (nuint)src.Length; } else { RuntimeImports.RhCorElementTypeInfo srcCorElementTypeInfo = src.ElementEEType.CorElementTypeInfo; uSrcLen = ((nuint)src.Length) << srcCorElementTypeInfo.Log2OfSize; if (!srcCorElementTypeInfo.IsPrimitive) { throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(src)); } } if (src != dst) { // Use optimized path for byte arrays since this is the main scenario for Buffer::BlockCopy // We only need an unreliable comparison since the slow path can handle the byte[] case too. if (dst.EETypePtr.FastEqualsUnreliable(EETypePtr.EETypePtrOf <byte[]>())) { uDstLen = (nuint)dst.Length; } else { RuntimeImports.RhCorElementTypeInfo dstCorElementTypeInfo = dst.ElementEEType.CorElementTypeInfo; if (!dstCorElementTypeInfo.IsPrimitive) { throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(dst)); } uDstLen = ((nuint)dst.Length) << dstCorElementTypeInfo.Log2OfSize; } } else { uDstLen = uSrcLen; } if (srcOffset < 0) { throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_MustBeNonNegInt32, nameof(srcOffset)); } if (dstOffset < 0) { throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_MustBeNonNegInt32, nameof(dstOffset)); } if (count < 0) { throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_MustBeNonNegInt32, nameof(count)); } nuint uCount = (nuint)count; nuint uSrcOffset = (nuint)srcOffset; nuint uDstOffset = (nuint)dstOffset; if (uSrcLen < uSrcOffset + uCount) { throw new ArgumentException(SR.Argument_InvalidOffLen); } if (uDstLen < uDstOffset + uCount) { throw new ArgumentException(SR.Argument_InvalidOffLen); } if (uCount != 0) { fixed(byte *pSrc = &src.GetRawArrayData(), pDst = &dst.GetRawArrayData()) { Buffer.Memmove(pDst + uDstOffset, pSrc + uSrcOffset, uCount); } } }
public static T CreateInstance <T>() { T t = default(T); bool missingDefaultConstructor = false; EETypePtr eetype = EETypePtr.EETypePtrOf <T>(); if (!RuntimeHelpers.IsReference <T>()) { // Early out for valuetypes since we don't support default constructors anyway. // This lets codegens that expand IsReference<T> optimize away the rest of this code. } else if (eetype.ComponentSize != 0) { // ComponentSize > 0 indicates an array-like type (e.g. string, array, etc). // Allocating this using the normal allocator would result in silent heap corruption. missingDefaultConstructor = true; } else if (eetype.IsInterface) { // Do not attempt to allocate interface types either missingDefaultConstructor = true; } else { bool oldValueOfMissingDefaultCtorMarkerBool = s_createInstanceMissingDefaultConstructor; try { t = (T)(RuntimeImports.RhNewObject(eetype)); // Run the default constructor. If the default constructor was missing, codegen // will expand DefaultConstructorOf to ClassWithMissingConstructor::.ctor // and we detect that later. IntPtr defaultConstructor = DefaultConstructorOf <T>(); RawCalliHelper.Call(defaultConstructor, t); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } catch (Exception e) { throw new TargetInvocationException(e); } if (s_createInstanceMissingDefaultConstructor != oldValueOfMissingDefaultCtorMarkerBool) { missingDefaultConstructor = true; // We didn't call the real .ctor (because there wasn't one), but we still allocated // an uninitialized object. If it has a finalizer, it would run - prevent that. GC.SuppressFinalize(t); } } if (missingDefaultConstructor) { throw new MissingMethodException(SR.Format(SR.MissingConstructor_Name, typeof(T))); } return(t); }
internal static object CallDynamicInvokeMethod( object thisPtr, IntPtr methodToCall, object thisPtrDynamicInvokeMethod, IntPtr dynamicInvokeHelperMethod, IntPtr dynamicInvokeHelperGenericDictionary, object defaultParametersContext, object[] parameters, bool invokeMethodHelperIsThisCall = true, bool methodToCallIsThisCall = true) { bool fDontWrapInTargetInvocationException = false; bool parametersNeedCopyBack = false; ArgSetupState argSetupState = default(ArgSetupState); // Capture state of thread static invoke helper statics object[] parametersOld = s_parameters; object[] nullableCopyBackObjectsOld = s_nullableCopyBackObjects; int curIndexOld = s_curIndex; object defaultParametersContextOld = s_defaultParametersContext; try { // If the passed in array is not an actual object[] instance, we need to copy it over to an actual object[] // instance so that the rest of the code can safely create managed object references to individual elements. if (parameters != null && EETypePtr.EETypePtrOf <object[]>() != parameters.EETypePtr) { s_parameters = new object[parameters.Length]; Array.Copy(parameters, s_parameters, parameters.Length); parametersNeedCopyBack = true; } else { s_parameters = parameters; } s_nullableCopyBackObjects = null; s_curIndex = 0; s_defaultParametersContext = defaultParametersContext; try { object result = null; if (invokeMethodHelperIsThisCall) { Debug.Assert(methodToCallIsThisCall == true); result = CalliIntrinsics.Call(dynamicInvokeHelperMethod, thisPtrDynamicInvokeMethod, thisPtr, methodToCall, ref argSetupState); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } else { if (dynamicInvokeHelperGenericDictionary != IntPtr.Zero) { result = CalliIntrinsics.Call(dynamicInvokeHelperMethod, dynamicInvokeHelperGenericDictionary, thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } else { result = CalliIntrinsics.Call(dynamicInvokeHelperMethod, thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } } return(result); } finally { if (parametersNeedCopyBack) { Array.Copy(s_parameters, parameters, parameters.Length); } if (!argSetupState.fComplete) { fDontWrapInTargetInvocationException = true; } else { // Nullable objects can't take advantage of the ability to update the boxed value on the heap directly, so perform // an update of the parameters array now. if (argSetupState.nullableCopyBackObjects != null) { for (int i = 0; i < argSetupState.nullableCopyBackObjects.Length; i++) { if (argSetupState.nullableCopyBackObjects[i] != null) { parameters[i] = DynamicInvokeBoxIntoNonNullable(argSetupState.nullableCopyBackObjects[i]); } } } } } } catch (Exception e) { if (fDontWrapInTargetInvocationException) { throw; } else { throw new System.Reflection.TargetInvocationException(e); } } finally { // Restore state of thread static helper statics s_parameters = parametersOld; s_nullableCopyBackObjects = nullableCopyBackObjectsOld; s_curIndex = curIndexOld; s_defaultParametersContext = defaultParametersContextOld; } }