diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 699f99c441fdf4..ed56ad4b4b6868 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -15,9 +14,25 @@ namespace System // IList and IReadOnlyList, where T : U dynamically. See the SZArrayHelper class for details. public abstract partial class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable { - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe Array InternalCreate(RuntimeType elementType, int rank, int* pLengths, int* pLowerBounds); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CreateInstance")] + private static unsafe partial void InternalCreate(QCallTypeHandle type, int rank, int* pLengths, int* pLowerBounds, + [MarshalAs(UnmanagedType.Bool)] bool fromArrayType, ObjectHandleOnStack retArray); + + private static unsafe Array InternalCreate(RuntimeType elementType, int rank, int* pLengths, int* pLowerBounds) + { + Array? retArray = null; + InternalCreate(new QCallTypeHandle(ref elementType), rank, pLengths, pLowerBounds, + fromArrayType: false, ObjectHandleOnStack.Create(ref retArray)); + return retArray!; + } + private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, int rank, int* pLengths, int* pLowerBounds) + { + Array? retArray = null; + InternalCreate(new QCallTypeHandle(ref arrayType), rank, pLengths, pLowerBounds, + fromArrayType: true, ObjectHandleOnStack.Create(ref retArray)); + return retArray!; + } private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 6faf613c3cf063..0c1afdcbfd8b7c 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -720,14 +720,8 @@ FCIMPLEND // Check we're allowed to create an array with the given element type. -void ArrayNative::CheckElementType(TypeHandle elementType) +static void CheckElementType(TypeHandle elementType) { - // Checks apply recursively for arrays of arrays etc. - while (elementType.IsArray()) - { - elementType = elementType.GetArrayElementTypeHandle(); - } - // Check for simple types first. if (!elementType.IsTypeDesc()) { @@ -738,7 +732,7 @@ void ArrayNative::CheckElementType(TypeHandle elementType) COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLikeArray")); // Check for open generic types. - if (pMT->IsGenericTypeDefinition() || pMT->ContainsGenericVariables()) + if (pMT->ContainsGenericVariables()) COMPlusThrow(kNotSupportedException, W("NotSupported_OpenType")); // Check for Void. @@ -753,62 +747,67 @@ void ArrayNative::CheckElementType(TypeHandle elementType) } } -FCIMPL4(Object*, ArrayNative::CreateInstance, ReflectClassBaseObject* pElementTypeUNSAFE, INT32 rank, INT32* pLengths, INT32* pLowerBounds) +void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pLowerBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray) { CONTRACTL { - FCALL_CHECK; + QCALL_CHECK; PRECONDITION(rank > 0); PRECONDITION(CheckPointer(pLengths)); PRECONDITION(CheckPointer(pLowerBounds, NULL_OK)); } CONTRACTL_END; - OBJECTREF pRet = NULL; - - REFLECTCLASSBASEREF pElementType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pElementTypeUNSAFE); - - // pLengths and pLowerBounds are pinned buffers. No need to protect them. - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(pElementType); - - TypeHandle elementType(pElementType->GetType()); + BEGIN_QCALL; - CheckElementType(elementType); + TypeHandle typeHnd = pTypeHnd.AsTypeHandle(); - CorElementType CorType = elementType.GetSignatureCorElementType(); + if (createFromArrayType) + { + _ASSERTE((INT32)typeHnd.GetRank() == rank); + _ASSERTE(typeHnd.IsArray()); - CorElementType kind = ELEMENT_TYPE_ARRAY; + if (typeHnd.GetArrayElementTypeHandle().ContainsGenericVariables()) + COMPlusThrow(kNotSupportedException, W("NotSupported_OpenType")); - // Is it ELEMENT_TYPE_SZARRAY array? - if (rank == 1 && (pLowerBounds == NULL || pLowerBounds[0] == 0) -#ifdef FEATURE_64BIT_ALIGNMENT - // On platforms where 64-bit types require 64-bit alignment and don't obtain it naturally force us - // through the slow path where this will be handled. - && (CorType != ELEMENT_TYPE_I8) - && (CorType != ELEMENT_TYPE_U8) - && (CorType != ELEMENT_TYPE_R8) -#endif - ) - { - // Shortcut for common cases - if (CorTypeInfo::IsPrimitiveType(CorType)) + if (!typeHnd.AsMethodTable()->IsMultiDimArray()) { - pRet = AllocatePrimitiveArray(CorType,pLengths[0]); + _ASSERTE(pLowerBounds == NULL || pLowerBounds[0] == 0); + + GCX_COOP(); + retArray.Set(AllocateSzArray(typeHnd, pLengths[0])); goto Done; } - else - if (CorTypeInfo::IsObjRef(CorType)) + } + else + { + CheckElementType(typeHnd); + + // Is it ELEMENT_TYPE_SZARRAY array? + if (rank == 1 && (pLowerBounds == NULL || pLowerBounds[0] == 0)) { - pRet = AllocateObjectArray(pLengths[0],elementType); - goto Done; + CorElementType corType = typeHnd.GetSignatureCorElementType(); + + // Shortcut for common cases + if (CorTypeInfo::IsPrimitiveType(corType)) + { + GCX_COOP(); + retArray.Set(AllocatePrimitiveArray(corType, pLengths[0])); + goto Done; + } + + typeHnd = ClassLoader::LoadArrayTypeThrowing(typeHnd); + + { + GCX_COOP(); + retArray.Set(AllocateSzArray(typeHnd, pLengths[0])); + goto Done; + } } - kind = ELEMENT_TYPE_SZARRAY; - pLowerBounds = NULL; + // Find the Array class... + typeHnd = ClassLoader::LoadArrayTypeThrowing(typeHnd, ELEMENT_TYPE_ARRAY, rank); } { - // Find the Array class... - TypeHandle typeHnd = ClassLoader::LoadArrayTypeThrowing(elementType, kind, rank); - _ASSERTE(rank <= MAX_RANK); // Ensures that the stack buffer size allocations below won't overflow DWORD boundsSize = 0; @@ -834,15 +833,15 @@ FCIMPL4(Object*, ArrayNative::CreateInstance, ReflectClassBaseObject* pElementTy bounds[i] = pLengths[i]; } - pRet = AllocateArrayEx(typeHnd, bounds, boundsSize); + { + GCX_COOP(); + retArray.Set(AllocateArrayEx(typeHnd, bounds, boundsSize)); + } } Done: ; - HELPER_METHOD_FRAME_END(); - - return OBJECTREFToObject(pRet); + END_QCALL; } -FCIMPLEND FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex) { diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 84218d698571fe..fae30384693688 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -30,8 +30,6 @@ class ArrayNative static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); static FCDECL5(void, CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength); - static FCDECL4(Object*, CreateInstance, ReflectClassBaseObject* pElementTypeUNSAFE, INT32 rank, INT32* pLengths, INT32* pBounds); - // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); @@ -44,9 +42,6 @@ class ArrayNative static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); private: - // Helper for CreateInstance - static void CheckElementType(TypeHandle elementType); - // Return values for CanAssignArrayType enum AssignArrayEnum { @@ -66,6 +61,7 @@ class ArrayNative }; +extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd); #endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs index 8c1ee196f32eac..83db754bfa6f9d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs @@ -126,8 +126,6 @@ public static Attribute Instantiate(this CustomAttributeData cad) // // Convert the argument value reported by Reflection into an actual object. // - [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", - Justification = "The AOT compiler ensures array types required by custom attribute blobs are generated.")] private static object? Convert(this CustomAttributeTypedArgument typedArgument) { Type argumentType = typedArgument.ArgumentType; @@ -144,8 +142,7 @@ public static Attribute Instantiate(this CustomAttributeData cad) IList? typedElements = (IList?)(typedArgument.Value); if (typedElements == null) return null; - Type? elementType = argumentType.GetElementType(); - Array array = Array.CreateInstance(elementType, typedElements.Count); + Array array = Array.CreateInstanceFromArrayType(argumentType, typedElements.Count); for (int i = 0; i < typedElements.Count; i++) { object? elementValue = typedElements[i].Convert(); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs index 8115756c8dca58..fa2db3712e0604 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs @@ -58,7 +58,12 @@ internal unsafe bool IsSzArray [RequiresDynamicCode("The code for an array of the specified type might not be available.")] private static unsafe Array InternalCreate(RuntimeType elementType, int rank, int* pLengths, int* pLowerBounds) { - ValidateElementType(elementType); + if (elementType.IsByRef || elementType.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefLikeArray); + if (elementType == typeof(void)) + throw new NotSupportedException(SR.NotSupported_VoidArray); + if (elementType.ContainsGenericParameters) + throw new NotSupportedException(SR.NotSupported_OpenType); if (pLowerBounds != null) { @@ -72,34 +77,59 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in if (rank == 1) { return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToEETypePtr(), pLengths[0]); - } else { - // Create a local copy of the lengths that cannot be motified by the caller + Type arrayType = elementType.MakeArrayType(rank); + + // Create a local copy of the lengths that cannot be modified by the caller int* pImmutableLengths = stackalloc int[rank]; for (int i = 0; i < rank; i++) pImmutableLengths[i] = pLengths[i]; - return NewMultiDimArray(elementType.MakeArrayType(rank).TypeHandle.ToEETypePtr(), pImmutableLengths, rank); + return NewMultiDimArray(arrayType.TypeHandle.ToEETypePtr(), pImmutableLengths, rank); } } -#pragma warning disable CA1859 // https://github.com/dotnet/roslyn-analyzers/issues/6451 - private static void ValidateElementType(Type elementType) + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "The compiler ensures that if we have a TypeHandle of a Rank-1 MdArray, we also generated the SzArray.")] + private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, int rank, int* pLengths, int* pLowerBounds) { - while (elementType.IsArray) + Debug.Assert(arrayType.IsArray); + Debug.Assert(arrayType.GetArrayRank() == rank); + + if (arrayType.ContainsGenericParameters) + throw new NotSupportedException(SR.NotSupported_OpenType); + + if (pLowerBounds != null) { - elementType = elementType.GetElementType()!; + for (int i = 0; i < rank; i++) + { + if (pLowerBounds[i] != 0) + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NonZeroLowerBound); + } + } + + EETypePtr eeType = arrayType.TypeHandle.ToEETypePtr(); + if (rank == 1) + { + // Multidimensional array of rank 1 with 0 lower bounds gets actually allocated + // as an SzArray. SzArray is castable to MdArray rank 1. + if (!eeType.IsSzArray) + eeType = arrayType.GetElementType().MakeArrayType().TypeHandle.ToEETypePtr(); + + return RuntimeImports.RhNewArray(eeType, pLengths[0]); + } + else + { + // Create a local copy of the lengths that cannot be modified by the caller + int* pImmutableLengths = stackalloc int[rank]; + for (int i = 0; i < rank; i++) + pImmutableLengths[i] = pLengths[i]; + + return NewMultiDimArray(eeType, pImmutableLengths, rank); } - if (elementType.IsByRef || elementType.IsByRefLike) - throw new NotSupportedException(SR.NotSupported_ByRefLikeArray); - if (elementType == typeof(void)) - throw new NotSupportedException(SR.NotSupported_VoidArray); - if (elementType.ContainsGenericParameters) - throw new NotSupportedException(SR.NotSupported_OpenType); } -#pragma warning restore CA1859 public unsafe void Initialize() { diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index a174e2cedf13db..bb509149184647 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -70,7 +70,6 @@ friend class ThreadBaseObject; static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration); - static FCDECL1(void, SpinWait, int iterations); static FCDECL0(Object*, GetCurrentThread); static FCDECL1(void, Finalize, ThreadBaseObject* pThis); #ifdef FEATURE_COMINTEROP diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 2f1f27bdf3eee3..c1f031085054ba 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -422,7 +422,6 @@ FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) FCFuncElement("CopySlow", ArrayNative::CopySlow) - FCFuncElement("InternalCreate", ArrayNative::CreateInstance) FCFuncElement("InternalSetValue", ArrayNative::SetValue) FCFuncEnd() diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 3682a4cef1691d..b660637d6a4ac9 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -151,6 +151,7 @@ static const Entry s_QCall[] = DllImportEntry(TypeBuilder_SetConstantValue) DllImportEntry(TypeBuilder_DefineCustomAttribute) DllImportEntry(MdUtf8String_EqualsCaseInsensitive) + DllImportEntry(Array_CreateInstance) DllImportEntry(Array_GetElementConstructorEntrypoint) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index cfa16efccd263c..44e3e6ee35e7a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -74,10 +74,8 @@ ref MemoryMarshal.GetArrayDataReference(larray), [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstance(Type elementType, int length) { - if (elementType is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.elementType); - if (length < 0) - ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum(); + ArgumentNullException.ThrowIfNull(elementType); + ArgumentOutOfRangeException.ThrowIfNegative(length); RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType; if (t == null) @@ -90,12 +88,9 @@ public static unsafe Array CreateInstance(Type elementType, int length) Justification = "MDArrays of Rank != 1 can be created because they don't implement generic interfaces.")] public static unsafe Array CreateInstance(Type elementType, int length1, int length2) { - if (elementType is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.elementType); - if (length1 < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length1, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); - if (length2 < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length2, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + ArgumentNullException.ThrowIfNull(elementType); + ArgumentOutOfRangeException.ThrowIfNegative(length1); + ArgumentOutOfRangeException.ThrowIfNegative(length2); RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType; if (t == null) @@ -109,30 +104,25 @@ public static unsafe Array CreateInstance(Type elementType, int length1, int len Justification = "MDArrays of Rank != 1 can be created because they don't implement generic interfaces.")] public static unsafe Array CreateInstance(Type elementType, int length1, int length2, int length3) { - if (elementType is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.elementType); - if (length1 < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length1, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); - if (length2 < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length2, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); - if (length3 < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length3, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + ArgumentNullException.ThrowIfNull(elementType); + ArgumentOutOfRangeException.ThrowIfNegative(length1); + ArgumentOutOfRangeException.ThrowIfNegative(length2); + ArgumentOutOfRangeException.ThrowIfNegative(length3); RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType; if (t == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.elementType); - int* pLengths = stackalloc int[3] { length1, length2, length3 }; + int* pLengths = stackalloc int[] { length1, length2, length3 }; return InternalCreate(t, 3, pLengths, null); } [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstance(Type elementType, params int[] lengths) { - if (elementType is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.elementType); - if (lengths == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.lengths); + ArgumentNullException.ThrowIfNull(elementType); + ArgumentNullException.ThrowIfNull(lengths); + if (lengths.Length == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); @@ -155,12 +145,10 @@ public static unsafe Array CreateInstance(Type elementType, params int[] lengths [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstance(Type elementType, int[] lengths, int[] lowerBounds) { - if (elementType == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.elementType); - if (lengths == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.lengths); - if (lowerBounds == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.lowerBounds); + ArgumentNullException.ThrowIfNull(elementType); + ArgumentNullException.ThrowIfNull(lengths); + ArgumentNullException.ThrowIfNull(lowerBounds); + if (lengths.Length != lowerBounds.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RanksAndBounds); if (lengths.Length == 0) @@ -186,12 +174,7 @@ public static unsafe Array CreateInstance(Type elementType, int[] lengths, int[] [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static Array CreateInstance(Type elementType, params long[] lengths) { - if (lengths == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.lengths); - } - if (lengths.Length == 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); + ArgumentNullException.ThrowIfNull(lengths); int[] intLengths = new int[lengths.Length]; @@ -207,6 +190,144 @@ public static Array CreateInstance(Type elementType, params long[] lengths) return CreateInstance(elementType, intLengths); } + /// + /// Creates a one-dimensional of the specified array type and length, with zero-based indexing. + /// + /// The type of the array (not of the array element type). + /// The size of the to create. + /// A new one-dimensional of the specified with the specified length. + /// is null. + /// is negative. + /// is not an array type. + /// -or- + /// is not one-dimensional array. + /// + /// When the array type is readily available, this method should be preferred over , as it has + /// better performance and it is AOT-friendly. + public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int length) + { + ArgumentNullException.ThrowIfNull(arrayType); + ArgumentOutOfRangeException.ThrowIfNegative(length); + + RuntimeType? t = arrayType as RuntimeType; + if (t == null) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.arrayType); + + if (!t.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); + + if (t.GetArrayRank() != 1) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported, ExceptionArgument.arrayType); + + return InternalCreateFromArrayType(t, 1, &length, null); + } + + /// + /// Creates a multidimensional of the specified and dimension lengths, with zero-based indexing. + /// + /// The type of the array (not of the array element type). + /// The dimension lengths, specified in an array of 32-bit integers. + /// A new multidimensional of the specified Type with the specified length for each dimension, using zero-based indexing. + /// is null. + /// -or- + /// is null. + /// + /// Any value in is less than zero. + /// The lengths array is empty. + /// -or- + /// is not an array type. + /// -or- + /// rank does not match length. + /// + /// When the array type is readily available, this method should be preferred over , as it has + /// better performance and it is AOT-friendly. + public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params int[] lengths) + { + ArgumentNullException.ThrowIfNull(arrayType); + ArgumentNullException.ThrowIfNull(lengths); + + RuntimeType? t = arrayType as RuntimeType; + if (t == null) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.arrayType); + + if (!t.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); + + if (t.GetArrayRank() != lengths.Length) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices); + + // Check to make sure the lengths are all non-negative. Note that we check this here to give + // a good exception message if they are not; however we check this again inside the execution + // engine's low level allocation function after having made a copy of the array to prevent a + // malicious caller from mutating the array after this check. + for (int i = 0; i < lengths.Length; i++) + if (lengths[i] < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, i, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + + fixed (int* pLengths = &lengths[0]) + return InternalCreateFromArrayType(t, lengths.Length, pLengths, null); + } + + /// + /// Creates a multidimensional of the specified and dimension lengths, with the specified lower bounds. + /// + /// The type of the array (not of the array element type). + /// The dimension lengths, specified in an array of 32-bit integers. + /// A one-dimensional array that contains the lower bound (starting index) of each dimension of the to create. + /// A new multidimensional of the specified with the specified length and lower bound for each dimension. + /// is null. + /// -or- + /// is null. + /// -or- + /// is null. + /// + /// The and arrays do not contain the same number of elements. + /// -or- + /// The lengths array is empty. + /// -or- + /// is not an array type. + /// -or- + /// rank does not match length. + /// + /// Any value in is less than zero. + /// Native AOT: any value in is different than zero. + /// When the array type is readily available, this method should be preferred over , as it has + /// better performance and it is AOT-friendly. + public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] lengths, int[] lowerBounds) + { + ArgumentNullException.ThrowIfNull(arrayType); + ArgumentNullException.ThrowIfNull(lengths); + ArgumentNullException.ThrowIfNull(lowerBounds); + + if (lengths.Length != lowerBounds.Length) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RanksAndBounds); + + RuntimeType? t = arrayType as RuntimeType; + if (t == null) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.arrayType); + + if (!t.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); + + if (t.GetArrayRank() != lengths.Length) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices); + + if (lowerBounds[0] != 0 && t.IsSZArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + + // Check to make sure the lengths are all non-negative. Note that we check this here to give + // a good exception message if they are not; however we check this again inside the execution + // engine's low level allocation function after having made a copy of the array to prevent a + // malicious caller from mutating the array after this check. + for (int i = 0; i < lengths.Length; i++) + if (lengths[i] < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, i, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + + fixed (int* pLengths = &lengths[0]) + fixed (int* pLowerBounds = &lowerBounds[0]) + return InternalCreateFromArrayType(t, lengths.Length, pLengths, pLowerBounds); + } + public static void Copy(Array sourceArray, Array destinationArray, long length) { int ilength = (int)length; diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 79f12c11feb40b..100222ef29deb0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -976,8 +976,6 @@ private static string GetArgumentName(ExceptionArgument argument) return "other"; case ExceptionArgument.newSize: return "newSize"; - case ExceptionArgument.lowerBounds: - return "lowerBounds"; case ExceptionArgument.lengths: return "lengths"; case ExceptionArgument.len: @@ -992,12 +990,6 @@ private static string GetArgumentName(ExceptionArgument argument) return "index2"; case ExceptionArgument.index3: return "index3"; - case ExceptionArgument.length1: - return "length1"; - case ExceptionArgument.length2: - return "length2"; - case ExceptionArgument.length3: - return "length3"; case ExceptionArgument.endIndex: return "endIndex"; case ExceptionArgument.elementType: @@ -1030,6 +1022,8 @@ private static string GetArgumentName(ExceptionArgument argument) return "overlapped"; case ExceptionArgument.minimumBytes: return "minimumBytes"; + case ExceptionArgument.arrayType: + return "arrayType"; case ExceptionArgument.divisor: return "divisor"; case ExceptionArgument.factor: @@ -1212,6 +1206,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Format_UnclosedFormatItem; case ExceptionResource.Format_ExpectedAsciiDigit: return SR.Format_ExpectedAsciiDigit; + case ExceptionResource.Argument_HasToBeArrayClass: + return SR.Argument_HasToBeArrayClass; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1296,7 +1292,6 @@ internal enum ExceptionArgument handle, other, newSize, - lowerBounds, lengths, len, keys, @@ -1304,9 +1299,6 @@ internal enum ExceptionArgument index1, index2, index3, - length1, - length2, - length3, endIndex, elementType, arrayIndex, @@ -1323,6 +1315,7 @@ internal enum ExceptionArgument anyOf, overlapped, minimumBytes, + arrayType, divisor, factor, } @@ -1410,5 +1403,6 @@ internal enum ExceptionResource Format_UnexpectedClosingBrace, Format_UnclosedFormatItem, Format_ExpectedAsciiDigit, + Argument_HasToBeArrayClass, } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs index d24b86f64e303e..38242bd650da8f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs @@ -1097,7 +1097,7 @@ internal override RestrictionFlags ValidRestrictionFlags if (exception != null) goto Error; ArrayList values = new ArrayList(); - object array; + Type arrayType; if (_itemType.Variety == XmlSchemaDatatypeVariety.Union) { object? unionTypedValue; @@ -1113,7 +1113,7 @@ internal override RestrictionFlags ValidRestrictionFlags XsdSimpleValue simpleValue = (XsdSimpleValue)unionTypedValue; values.Add(new XmlAtomicValue(simpleValue.XmlType, simpleValue.TypedValue, nsmgr)); } - array = ToArray(values, typeof(XmlAtomicValue[])); + arrayType = typeof(XmlAtomicValue[]); } else { //Variety == List or Atomic @@ -1126,13 +1126,17 @@ internal override RestrictionFlags ValidRestrictionFlags values.Add(typedValue); } Debug.Assert(_itemType.ListValueType.GetElementType() == _itemType.ValueType); - array = ToArray(values, _itemType.ListValueType); + arrayType = _itemType.ListValueType; } + if (values.Count < _minListSize) { return new XmlSchemaException(SR.Sch_EmptyAttributeValue, string.Empty); } + Array array = Array.CreateInstanceFromArrayType(arrayType, values.Count); + values.CopyTo(array); + exception = listFacetsChecker.CheckValueFacets(array, this); if (exception != null) goto Error; @@ -1142,12 +1146,6 @@ internal override RestrictionFlags ValidRestrictionFlags Error: return exception; - - // TODO: Replace with https://github.com/dotnet/runtime/issues/76478 once available - [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi", - Justification = "Array type is always present as it is passed in as a parameter.")] - static Array ToArray(ArrayList values, Type arrayType) - => values.ToArray(arrayType.GetElementType()!); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index 56ea3f55674a67..e9e2fdfe39bcaf 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -572,8 +572,7 @@ private static void SetCollectionObjectWithCollectionMember([NotNull] ref object } else { - Type elementType = collectionType.GetElementType()!; - a = Array.CreateInstance(elementType, collectionMember.Count); + a = Array.CreateInstanceFromArrayType(collectionType, collectionMember.Count); } for (int i = 0; i < collectionMember.Count; i++) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 231f2ab0584fb6..f253ada5f48a4b 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -384,6 +384,9 @@ public void CopyTo(System.Array array, long index) { } public static System.Array CreateInstance(System.Type elementType, int[] lengths, int[] lowerBounds) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static System.Array CreateInstance(System.Type elementType, params long[] lengths) { throw null; } + public static System.Array CreateInstanceFromArrayType(System.Type arrayType, int length) { throw null; } + public static System.Array CreateInstanceFromArrayType(System.Type arrayType, params int[] lengths) { throw null; } + public static System.Array CreateInstanceFromArrayType(System.Type arrayType, int[] lengths, int[] lowerBounds) { throw null; } public static T[] Empty() { throw null; } public static bool Exists(T[] array, System.Predicate match) { throw null; } public static void Fill(T[] array, T value) { } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs index 13dc4f9d17f25d..2850347d59d6a3 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs @@ -7,10 +7,8 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Threading.Tasks; using Microsoft.DotNet.XUnitExtensions; using Xunit; -using Xunit.Abstractions; namespace System.Tests { @@ -1862,32 +1860,47 @@ public static void CreateInstance_Advanced(Type elementType, int[] lengths, int[ // Use CreateInstance(Type, int) Array array1 = Array.CreateInstance(elementType, lengths[0]); VerifyArray(array1, elementType, lengths, lowerBounds, repeatedValue); + // Use CreateInstanceFromArrayType(Type, int) + array1 = Array.CreateInstanceFromArrayType(array1.GetType(), lengths[0]); + VerifyArray(array1, elementType, lengths, lowerBounds, repeatedValue); } else if (lengths.Length == 2) { // Use CreateInstance(Type, int, int) Array array2 = Array.CreateInstance(elementType, lengths[0], lengths[1]); VerifyArray(array2, elementType, lengths, lowerBounds, repeatedValue); + // Use CreateInstanceFromArrayType(Type, int, int) + array2 = Array.CreateInstanceFromArrayType(array2.GetType(), lengths[0], lengths[1]); + VerifyArray(array2, elementType, lengths, lowerBounds, repeatedValue); } else if (lengths.Length == 3) { // Use CreateInstance(Type, int, int, int) Array array3 = Array.CreateInstance(elementType, lengths[0], lengths[1], lengths[2]); VerifyArray(array3, elementType, lengths, lowerBounds, repeatedValue); + // Use CreateInstanceFromArrayType(Type, int, int, int) + array3 = Array.CreateInstanceFromArrayType(array3.GetType(), lengths[0], lengths[1], lengths[2]); + VerifyArray(array3, elementType, lengths, lowerBounds, repeatedValue); } // Use CreateInstance(Type, int[]) Array array4 = Array.CreateInstance(elementType, lengths); VerifyArray(array4, elementType, lengths, lowerBounds, repeatedValue); + // Use CreateInstanceFromArrayType(Type, int[]) + array4 = Array.CreateInstanceFromArrayType(array4.GetType(), lengths); + VerifyArray(array4, elementType, lengths, lowerBounds, repeatedValue); + // Use CreateInstance(Type, long[]) Array array5 = Array.CreateInstance(elementType, lengths.Select(length => (long)length).ToArray()); VerifyArray(array5, elementType, lengths, lowerBounds, repeatedValue); - } // Use CreateInstance(Type, int[], int[]) Array array6 = Array.CreateInstance(elementType, lengths, lowerBounds); VerifyArray(array6, elementType, lengths, lowerBounds, repeatedValue); + // Use CreateInstanceFromArrayType(Type, int[], int[]) + array6 = Array.CreateInstanceFromArrayType(array6.GetType(), lengths, lowerBounds); + VerifyArray(array6, elementType, lengths, lowerBounds, repeatedValue); } [Fact] @@ -1899,6 +1912,12 @@ public static void CreateInstance_NullElementType_ThrowsArgumentNullException() AssertExtensions.Throws("elementType", () => Array.CreateInstance(null, new int[1])); AssertExtensions.Throws("elementType", () => Array.CreateInstance(null, new long[1])); AssertExtensions.Throws("elementType", () => Array.CreateInstance(null, new int[1], new int[1])); + + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(null, 0)); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(null, 0, 0)); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(null, 0, 0, 0)); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(null, new int[1])); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(null, new int[1], new int[1])); } public static IEnumerable CreateInstance_NotSupportedType_TestData() @@ -1926,6 +1945,30 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele Assert.Throws(() => Array.CreateInstance(elementType, new int[1], new int[1])); } + [Theory] + [InlineData(typeof(GenericClass<>))] + public void CreateInstanceFromArrayType_NotSupportedArrayType_ThrowsNotSupportedException(Type elementType) + { + Assert.Throws(() => Array.CreateInstanceFromArrayType(elementType.MakeArrayType(), 0)); + Assert.Throws(() => Array.CreateInstanceFromArrayType(elementType.MakeArrayType(rank: 2), 0, 0)); + Assert.Throws(() => Array.CreateInstanceFromArrayType(elementType.MakeArrayType(rank: 3), 0, 0, 0)); + Assert.Throws(() => Array.CreateInstanceFromArrayType(elementType.MakeArrayType(), new int[1])); + Assert.Throws(() => Array.CreateInstanceFromArrayType(elementType.MakeArrayType(), new int[1], new int[1])); + } + + [Theory] + [InlineData(typeof(int))] + [InlineData(typeof(GenericClass<>))] + [InlineData(typeof(Span))] + public void CreateInstanceFromArrayType_NotAnArrayType_ThrowsArgumentException(Type notAnArrayType) + { + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(notAnArrayType, 0)); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(notAnArrayType, 0, 0)); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(notAnArrayType, 0, 0, 0)); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(notAnArrayType, new int[1])); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(notAnArrayType, new int[1], new int[1])); + } + [Fact] public void CreateInstance_TypeNotRuntimeType_ThrowsArgumentException() { @@ -1938,6 +1981,12 @@ public void CreateInstance_TypeNotRuntimeType_ThrowsArgumentException() AssertExtensions.Throws("elementType", () => Array.CreateInstance(elementType, new int[1])); AssertExtensions.Throws("elementType", () => Array.CreateInstance(elementType, new long[1])); AssertExtensions.Throws("elementType", () => Array.CreateInstance(elementType, new int[1], new int[1])); + + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 1)); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 1, 1)); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 1, 1, 1)); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new int[1])); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new int[1], new int[1])); } } @@ -1950,6 +1999,12 @@ public void CreateInstance_NegativeLength_ThrowsArgumentOutOfRangeException() AssertExtensions.Throws("lengths[0]", () => Array.CreateInstance(typeof(int), new int[] { -1 })); AssertExtensions.Throws("lengths[0]", () => Array.CreateInstance(typeof(int), new long[] { -1 })); AssertExtensions.Throws("lengths[0]", () => Array.CreateInstance(typeof(int), new int[] { -1 }, new int[1])); + + AssertExtensions.Throws("length", () => Array.CreateInstanceFromArrayType(typeof(int[]), -1)); + AssertExtensions.Throws("lengths[0]", () => Array.CreateInstanceFromArrayType(typeof(int[,]), -1, 0)); + AssertExtensions.Throws("lengths[0]", () => Array.CreateInstanceFromArrayType(typeof(int[,,]), -1, 0, 0)); + AssertExtensions.Throws("lengths[0]", () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[] { -1 })); + AssertExtensions.Throws("lengths[0]", () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[] { -1 }, new int[1])); } [Fact] @@ -1957,12 +2012,17 @@ public void CreateInstance_NegativeLength2_ThrowsArgumentOutOfRangeException() { AssertExtensions.Throws("length2", () => Array.CreateInstance(typeof(int), 0, -1)); AssertExtensions.Throws("length2", () => Array.CreateInstance(typeof(int), 0, -1, 0)); + + AssertExtensions.Throws("lengths[1]", () => Array.CreateInstanceFromArrayType(typeof(int[,]), 0, -1)); + AssertExtensions.Throws("lengths[1]", () => Array.CreateInstanceFromArrayType(typeof(int[,,]), 0, -1, 0)); } [Fact] public void CreateInstance_NegativeLength3_ThrowsArgumentOutOfRangeException() { AssertExtensions.Throws("length3", () => Array.CreateInstance(typeof(int), 0, 0, -1)); + + AssertExtensions.Throws("lengths[2]", () => Array.CreateInstanceFromArrayType(typeof(int[,,]), 0, 0, -1)); } [Fact] @@ -1971,6 +2031,9 @@ public void CreateInstance_LengthsNull_ThrowsArgumentNullException() AssertExtensions.Throws("lengths", () => Array.CreateInstance(typeof(int), (int[])null)); AssertExtensions.Throws("lengths", () => Array.CreateInstance(typeof(int), (long[])null)); AssertExtensions.Throws("lengths", () => Array.CreateInstance(typeof(int), null, new int[1])); + + AssertExtensions.Throws("lengths", () => Array.CreateInstanceFromArrayType(typeof(int[]), (int[])null)); + AssertExtensions.Throws("lengths", () => Array.CreateInstanceFromArrayType(typeof(int[]), null, new int[1])); } [Fact] @@ -1980,12 +2043,36 @@ public void CreateInstance_LengthsEmpty_ThrowsArgumentException() AssertExtensions.Throws(null, () => Array.CreateInstance(typeof(int), new long[0])); AssertExtensions.Throws(null, () => Array.CreateInstance(typeof(int), new int[0], new int[1])); AssertExtensions.Throws(null, () => Array.CreateInstance(typeof(int), new int[0], new int[0])); + + AssertExtensions.Throws(null, () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[0])); + AssertExtensions.Throws(null, () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[0], new int[1])); + AssertExtensions.Throws(null, () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[0], new int[0])); } [Fact] public void CreateInstance_LowerBoundNull_ThrowsArgumentNullException() { AssertExtensions.Throws("lowerBounds", () => Array.CreateInstance(typeof(int), new int[] { 1 }, null)); + + AssertExtensions.Throws("lowerBounds", () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[] { 1 }, null)); + } + + [Fact] + public void CreateInstanceFromTemplate_Rank1() + { + Type variableBoundArrayType = typeof(object).MakeArrayType(1); + Assert.NotEqual(typeof(object[]), variableBoundArrayType); + + Assert.Equal(typeof(object[]), Array.CreateInstanceFromArrayType(variableBoundArrayType, 12).GetType()); + Assert.Equal(typeof(object[]), Array.CreateInstanceFromArrayType(variableBoundArrayType, [22]).GetType()); + Assert.Equal(typeof(object[]), Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [0]).GetType()); + + if (PlatformDetection.IsNonZeroLowerBoundArraySupported) + { + Assert.Equal(variableBoundArrayType, Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType()); + } + + Assert.Throws(() => Array.CreateInstanceFromArrayType(typeof(object[]), [7], [8])); } [Theory] @@ -2002,6 +2089,8 @@ public void CreateInstance_InvalidLengthInLongLength_ThrowsArgumentOutOfRangeExc public void CreateInstance_LengthsAndLowerBoundsHaveDifferentLengths_ThrowsArgumentException(int length) { AssertExtensions.Throws(null, () => Array.CreateInstance(typeof(int), new int[1], new int[length])); + + AssertExtensions.Throws(null, () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[1], new int[length])); } [Theory] @@ -2014,12 +2103,16 @@ public void CreateInstance_RankMoreThanMaxRank_ThrowsTypeLoadException(int lengt var lengths = new int[length]; var lowerBounds = new int[length]; Assert.Throws(() => Array.CreateInstance(typeof(int), lengths, lowerBounds)); + + Assert.Throws(() => Array.CreateInstanceFromArrayType(typeof(int[]), lengths, lowerBounds)); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNonZeroLowerBoundArraySupported))] public void CreateInstance_Type_LengthsPlusLowerBoundOverflows_ThrowsArgumentOutOfRangeException() { AssertExtensions.Throws(null, () => Array.CreateInstance(typeof(int), new int[] { int.MaxValue }, new int[] { 2 })); + + AssertExtensions.Throws(null, () => Array.CreateInstanceFromArrayType(typeof(int).MakeArrayType(1), new int[] { int.MaxValue }, new int[] { 2 })); } [Fact] diff --git a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs index f6782a42a23be3..57b21d78cc5019 100644 --- a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs @@ -289,6 +289,11 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in [MethodImpl(MethodImplOptions.InternalCall)] private static extern unsafe void InternalCreate(ref Array? result, IntPtr elementType, int rank, int* lengths, int* lowerBounds); + private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank, int* pLengths, int* pLowerBounds) + { + return InternalCreate((arrayType.GetElementType() as RuntimeType)!, rank, pLengths, pLowerBounds); + } + private unsafe nint GetFlattenedIndex(int rawIndex) { // Checked by the caller