From 626a750a6f45bae53762d4f0f7269951ee47ca51 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Tue, 11 Jul 2023 00:32:52 +0700 Subject: [PATCH 01/23] Create array from array type --- .../CustomAttributeInstantiator.cs | 3 +- .../src/Resources/Strings.resx | 67 ++++++++--------- .../src/System/Array.cs | 68 ++++++++++++++++++ .../src/System/ThrowHelper.cs | 6 ++ .../ReflectionXmlSerializationReader.cs | 3 +- .../System.Runtime/ref/System.Runtime.cs | 3 + src/libraries/System.Runtime/tests/Helpers.cs | 1 + .../System.Runtime/tests/System/ArrayTests.cs | 71 +++++++++++++++++++ 8 files changed, 186 insertions(+), 36 deletions(-) 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 1d29f98859935..50f1ad06201ba 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 @@ -144,8 +144,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/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 60ef1fbf28cad..291506468333e 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - @@ -376,7 +376,7 @@ One of the identified items was in an invalid format. - + Generic arguments after array spec or pointer type. @@ -1080,7 +1080,7 @@ Field '{0}' does not belong to the same class as the constructor. - + Field '{0}' does not have a valid type. @@ -1496,7 +1496,7 @@ Path cannot be the empty string or all whitespace. - Value of argument {0} does not match parameter type: {1} -> {2}. + Value of argument {0} does not match parameter type: {1} -> {2}. Parameter {0} does not have a valid type. @@ -3762,7 +3762,7 @@ "Property '{0}' does not have a setter. - "Value of property '{0}' does not match property type: '{1}' -> '{2}'. + "Value of property '{0}' does not match property type: '{1}' -> '{2}'. Cannot load hostpolicy library. AssemblyDependencyResolver is currently only supported if the runtime is hosted through hostpolicy library. @@ -4235,4 +4235,7 @@ String length exceeded supported range. - + + Type must be a array type. + + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 313b064fc9e67..719d105a5b2a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -207,6 +207,74 @@ public static Array CreateInstance(Type elementType, params long[] lengths) return CreateInstance(elementType, intLengths); } + public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int length) + { + ArgumentNullException.ThrowIfNull(arrayType); + ArgumentOutOfRangeException.ThrowIfNegative(length); + + if (!arrayType.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); + + RuntimeType t = (arrayType.GetElementType()!.UnderlyingSystemType as RuntimeType)!; + Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); + + return InternalCreate(t, 1, &length, null); + } + + public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params int[] lengths) + { + ArgumentNullException.ThrowIfNull(arrayType); + ArgumentNullException.ThrowIfNull(lengths); + if (lengths.Length == 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); + + if (!arrayType.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); + + RuntimeType t = (arrayType.GetElementType()!.UnderlyingSystemType as RuntimeType)!; + Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); + + // 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 InternalCreate(t, lengths.Length, pLengths, null); + } + + 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); + if (lengths.Length == 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); + + if (!arrayType.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); + + RuntimeType t = (arrayType.GetElementType()!.UnderlyingSystemType as RuntimeType)!; + Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); + + // 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 InternalCreate(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 71e2904b8dfbb..79a4f599c4fee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -969,6 +969,8 @@ private static string GetArgumentName(ExceptionArgument argument) return "overlapped"; case ExceptionArgument.minimumBytes: return "minimumBytes"; + case ExceptionArgument.arrayType: + return "arrayType"; default: Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum."); return ""; @@ -1147,6 +1149,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Format_UnclosedFormatItem; case ExceptionResource.Format_ExpectedAsciiDigit: return SR.Format_ExpectedAsciiDigit; + case ExceptionResource.Arg_MustBeArrayType: + return SR.Arg_MustBeArrayType; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1258,6 +1262,7 @@ internal enum ExceptionArgument anyOf, overlapped, minimumBytes, + arrayType, } // @@ -1343,5 +1348,6 @@ internal enum ExceptionResource Format_UnexpectedClosingBrace, Format_UnclosedFormatItem, Format_ExpectedAsciiDigit, + Arg_MustBeArrayType, } } 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 8355612e9f9ed..afd551ac3fe5d 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 dbd41d31f590b..66dcb028a89f8 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/Helpers.cs b/src/libraries/System.Runtime/tests/Helpers.cs index 5d44dc649976b..9da1e7e8fed1e 100644 --- a/src/libraries/System.Runtime/tests/Helpers.cs +++ b/src/libraries/System.Runtime/tests/Helpers.cs @@ -45,6 +45,7 @@ private static Type RefEmitType() private sealed class NonRuntimeType : MockType { public sealed override Type UnderlyingSystemType => this; + protected sealed override bool IsArrayImpl() => false; } } } diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index 7ef45b6401ad0..2c90a11682f5b 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -1861,32 +1861,51 @@ 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 CreateInstanceFromArrayType(Type, long[]) + //array5 = Array.CreateInstanceFromArrayType(array5.GetType(), 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] @@ -1898,6 +1917,13 @@ 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 long[1])); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(null, new int[1], new int[1])); } public static IEnumerable CreateInstance_NotSupportedType_TestData() @@ -1923,6 +1949,13 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele Assert.Throws(() => Array.CreateInstance(elementType, new int[1])); Assert.Throws(() => Array.CreateInstance(elementType, new long[1])); Assert.Throws(() => Array.CreateInstance(elementType, new int[1], new int[1])); + + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 0)); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 0, 0)); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 0, 0, 0)); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new int[1])); + //Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new long[1])); + Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new int[1], new int[1])); } [Fact] @@ -1937,6 +1970,13 @@ 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 long[1])); + AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new int[1], new int[1])); } } @@ -1949,6 +1989,13 @@ 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 long[] { -1 })); + AssertExtensions.Throws("lengths[0]", () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[] { -1 }, new int[1])); } [Fact] @@ -1956,12 +2003,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] @@ -1970,6 +2022,10 @@ 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[]), (long[])null)); + AssertExtensions.Throws("lengths", () => Array.CreateInstanceFromArrayType(typeof(int[]), null, new int[1])); } [Fact] @@ -1979,12 +2035,19 @@ 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 long[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)); } [Theory] @@ -1993,6 +2056,8 @@ public void CreateInstance_LowerBoundNull_ThrowsArgumentNullException() public void CreateInstance_InvalidLengthInLongLength_ThrowsArgumentOutOfRangeException(long length) { AssertExtensions.Throws("len", () => Array.CreateInstance(typeof(int), new long[] { length })); + + //AssertExtensions.Throws("len", () => Array.CreateInstanceFromArrayType(typeof(int[]), new long[] { length })); } [Theory] @@ -2001,6 +2066,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] @@ -2013,12 +2080,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[]), new int[] { int.MaxValue }, new int[] { 2 })); } [Fact] From 9305288c580e0019ca8475cb39c05af36808846f Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Tue, 11 Jul 2023 15:16:24 +0700 Subject: [PATCH 02/23] Code review --- src/libraries/System.Private.CoreLib/src/System/Array.cs | 9 ++++++--- src/libraries/System.Runtime/ref/System.Runtime.cs | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 719d105a5b2a1..b3e1d289702e9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -207,6 +207,7 @@ public static Array CreateInstance(Type elementType, params long[] lengths) return CreateInstance(elementType, intLengths); } + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int length) { ArgumentNullException.ThrowIfNull(arrayType); @@ -215,12 +216,13 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int lengt if (!arrayType.IsArray) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); - RuntimeType t = (arrayType.GetElementType()!.UnderlyingSystemType as RuntimeType)!; + RuntimeType t = (arrayType.GetElementType() as RuntimeType)!; Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); return InternalCreate(t, 1, &length, null); } + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params int[] lengths) { ArgumentNullException.ThrowIfNull(arrayType); @@ -231,7 +233,7 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in if (!arrayType.IsArray) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); - RuntimeType t = (arrayType.GetElementType()!.UnderlyingSystemType as RuntimeType)!; + RuntimeType t = (arrayType.GetElementType() as RuntimeType)!; Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); // Check to make sure the lengths are all non-negative. Note that we check this here to give @@ -246,6 +248,7 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in return InternalCreate(t, lengths.Length, pLengths, null); } + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] lengths, int[] lowerBounds) { ArgumentNullException.ThrowIfNull(arrayType); @@ -259,7 +262,7 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len if (!arrayType.IsArray) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); - RuntimeType t = (arrayType.GetElementType()!.UnderlyingSystemType as RuntimeType)!; + RuntimeType t = (arrayType.GetElementType() as RuntimeType)!; Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); // Check to make sure the lengths are all non-negative. Note that we check this here to give diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 66dcb028a89f8..17b6bddd3e205 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -384,8 +384,11 @@ 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; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static System.Array CreateInstanceFromArrayType(System.Type arrayType, int length) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static System.Array CreateInstanceFromArrayType(System.Type arrayType, params int[] lengths) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] 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; } From f0b24248f804a287d0c80e04cccc0faab0e515e0 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Wed, 12 Jul 2023 02:22:52 +0700 Subject: [PATCH 03/23] InternalCreateFromArrayType --- .../src/System/Array.CoreCLR.cs | 23 ++++++++++++++++ .../src/System/Array.NativeAot.cs | 26 +++++++++++++++++++ .../src/System/Array.cs | 24 +++++++---------- .../System.Runtime/ref/System.Runtime.cs | 3 --- .../System.Runtime/tests/System/ArrayTests.cs | 12 ++++----- .../src/System/Array.Mono.cs | 5 ++++ 6 files changed, 69 insertions(+), 24 deletions(-) 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 699f99c441fdf..0183520f08a0a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using static System.GC; namespace System { @@ -18,6 +19,28 @@ public abstract partial class Array : ICloneable, IList, IStructuralComparable, [MethodImpl(MethodImplOptions.InternalCall)] private static extern unsafe Array InternalCreate(RuntimeType elementType, int rank, int* pLengths, int* pLowerBounds); + private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank, int* pLengths, int* pLowerBounds) + { + if (rank == 1 && !ContainsLowerBounds(rank, pLowerBounds)) + { + return AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS); + } + + return InternalCreate((arrayType.GetElementType() as RuntimeType)!, rank, pLengths, pLowerBounds); + + static bool ContainsLowerBounds(int rank, int* pLowerBounds) + { + if (pLowerBounds != null) + { + for (int i = 0; i < rank; i++) + { + if (pLowerBounds[i] != 0) + return true; + } + } + return false; + } + } private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { 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 1cb5604cefbd5..12a4e12ce25ab 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 @@ -85,6 +85,32 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in } } + private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank, int* pLengths, int* pLowerBounds) + { + if (pLowerBounds != null) + { + for (int i = 0; i < rank; i++) + { + if (pLowerBounds[i] != 0) + throw new PlatformNotSupportedException(SR.PlatformNotSupported_NonZeroLowerBound); + } + } + + if (rank == 1) + { + return RuntimeImports.RhNewArray(arrayType.TypeHandle.ToEETypePtr(), pLengths[0]); + } + else + { + // Create a local copy of the lengths that cannot be motified by the caller + int* pImmutableLengths = stackalloc int[rank]; + for (int i = 0; i < rank; i++) + pImmutableLengths[i] = pLengths[i]; + + 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) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index b3e1d289702e9..1774c6c87a314 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -207,7 +207,6 @@ public static Array CreateInstance(Type elementType, params long[] lengths) return CreateInstance(elementType, intLengths); } - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int length) { ArgumentNullException.ThrowIfNull(arrayType); @@ -215,14 +214,12 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int lengt if (!arrayType.IsArray) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); + if (arrayType.GetArrayRank() != 1) + ThrowHelper.ThrowArgumentException_Argument_IncompatibleArrayType(); - RuntimeType t = (arrayType.GetElementType() as RuntimeType)!; - Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); - - return InternalCreate(t, 1, &length, null); + return InternalCreateFromArrayType(arrayType, 1, &length, null); } - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params int[] lengths) { ArgumentNullException.ThrowIfNull(arrayType); @@ -232,9 +229,8 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in if (!arrayType.IsArray) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); - - RuntimeType t = (arrayType.GetElementType() as RuntimeType)!; - Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); + if (arrayType.GetArrayRank() != lengths.Length) + ThrowHelper.ThrowArgumentException_Argument_IncompatibleArrayType(); // 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 @@ -245,10 +241,9 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, i, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); fixed (int* pLengths = &lengths[0]) - return InternalCreate(t, lengths.Length, pLengths, null); + return InternalCreateFromArrayType(arrayType, lengths.Length, pLengths, null); } - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] lengths, int[] lowerBounds) { ArgumentNullException.ThrowIfNull(arrayType); @@ -261,9 +256,8 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len if (!arrayType.IsArray) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); - - RuntimeType t = (arrayType.GetElementType() as RuntimeType)!; - Debug.Assert(t is not null, $"CreateInstanceFromArrayType can not get underlying system type for \"{arrayType}\""); + if (arrayType.GetArrayRank() != lengths.Length) + ThrowHelper.ThrowArgumentException_Argument_IncompatibleArrayType(); // 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 @@ -275,7 +269,7 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len fixed (int* pLengths = &lengths[0]) fixed (int* pLowerBounds = &lowerBounds[0]) - return InternalCreate(t, lengths.Length, pLengths, pLowerBounds); + return InternalCreateFromArrayType(arrayType, lengths.Length, pLengths, pLowerBounds); } public static void Copy(Array sourceArray, Array destinationArray, long length) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 17b6bddd3e205..66dcb028a89f8 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -384,11 +384,8 @@ 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; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static System.Array CreateInstanceFromArrayType(System.Type arrayType, int length) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] public static System.Array CreateInstanceFromArrayType(System.Type arrayType, params int[] lengths) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("The code for an array of the specified type might not be available.")] 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; } diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index 2c90a11682f5b..645b6e9c408b2 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -1991,8 +1991,8 @@ public void CreateInstance_NegativeLength_ThrowsArgumentOutOfRangeException() 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[,]), -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 long[] { -1 })); AssertExtensions.Throws("lengths[0]", () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[] { -1 }, new int[1])); @@ -2004,8 +2004,8 @@ 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)); + AssertExtensions.Throws("lengths[1]", () => Array.CreateInstanceFromArrayType(typeof(int[,]), 0, -1)); + AssertExtensions.Throws("lengths[1]", () => Array.CreateInstanceFromArrayType(typeof(int[,,]), 0, -1, 0)); } [Fact] @@ -2013,7 +2013,7 @@ 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)); + AssertExtensions.Throws("lengths[2]", () => Array.CreateInstanceFromArrayType(typeof(int[,,]), 0, 0, -1)); } [Fact] @@ -2081,7 +2081,7 @@ public void CreateInstance_RankMoreThanMaxRank_ThrowsTypeLoadException(int lengt var lowerBounds = new int[length]; Assert.Throws(() => Array.CreateInstance(typeof(int), lengths, lowerBounds)); - Assert.Throws(() => Array.CreateInstanceFromArrayType(typeof(int[]), lengths, lowerBounds)); + Assert.Throws(() => Array.CreateInstanceFromArrayType(typeof(int[]), lengths, lowerBounds)); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNonZeroLowerBoundArraySupported))] 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 f6782a42a23be..57b21d78cc501 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 From 58467db59bcdc125a99e044cc97b4bcbeb46b7df Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Wed, 12 Jul 2023 02:41:24 +0700 Subject: [PATCH 04/23] GC --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 0183520f08a0a..69cdb4df70bbf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -4,11 +4,9 @@ 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; -using static System.GC; namespace System { @@ -23,7 +21,7 @@ private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank { if (rank == 1 && !ContainsLowerBounds(rank, pLowerBounds)) { - return AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS); + return GC.AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS); } return InternalCreate((arrayType.GetElementType() as RuntimeType)!, rank, pLengths, pLowerBounds); From f90fda6acfd355e0ab3f0f837da3979d1591cbaa Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Wed, 12 Jul 2023 02:42:49 +0700 Subject: [PATCH 05/23] GC --- src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 69cdb4df70bbf..bdf2ea1bef737 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -21,7 +21,7 @@ private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank { if (rank == 1 && !ContainsLowerBounds(rank, pLowerBounds)) { - return GC.AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS); + return GC.AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC.GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS); } return InternalCreate((arrayType.GetElementType() as RuntimeType)!, rank, pLengths, pLowerBounds); From 824aabec308018d288ee37b9387ac197667cffe3 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Thu, 13 Jul 2023 03:49:58 +0700 Subject: [PATCH 06/23] code review --- .../src/System/Array.CoreCLR.cs | 22 +++---------------- .../CustomAttributeInstantiator.cs | 2 -- 2 files changed, 3 insertions(+), 21 deletions(-) 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 bdf2ea1bef737..ad73dbb0832ca 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -19,25 +19,9 @@ public abstract partial class Array : ICloneable, IList, IStructuralComparable, private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank, int* pLengths, int* pLowerBounds) { - if (rank == 1 && !ContainsLowerBounds(rank, pLowerBounds)) - { - return GC.AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC.GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS); - } - - return InternalCreate((arrayType.GetElementType() as RuntimeType)!, rank, pLengths, pLowerBounds); - - static bool ContainsLowerBounds(int rank, int* pLowerBounds) - { - if (pLowerBounds != null) - { - for (int i = 0; i < rank; i++) - { - if (pLowerBounds[i] != 0) - return true; - } - } - return false; - } + return rank == 1 && (pLowerBounds == null || pLowerBounds[0] == 0) + ? GC.AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC.GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS) + : InternalCreate((arrayType.GetElementType() as RuntimeType)!, rank, pLengths, pLowerBounds); } private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) 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 50f1ad06201ba..b3730ae08ceaa 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; From 8296fe679a2a42ed3db77f4e95c090ff8b0d0ea1 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Thu, 13 Jul 2023 04:37:39 +0700 Subject: [PATCH 07/23] code review Exceptions --- .../src/Resources/Strings.resx | 5 +---- .../System.Private.CoreLib/src/System/Array.cs | 12 ++++++------ .../System.Private.CoreLib/src/System/ThrowHelper.cs | 6 +++--- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 291506468333e..a4a8c23350256 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4235,7 +4235,4 @@ String length exceeded supported range. - - Type must be a array type. - - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 1774c6c87a314..f6d375745fee5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -213,9 +213,9 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int lengt ArgumentOutOfRangeException.ThrowIfNegative(length); if (!arrayType.IsArray) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); if (arrayType.GetArrayRank() != 1) - ThrowHelper.ThrowArgumentException_Argument_IncompatibleArrayType(); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported, ExceptionArgument.arrayType); return InternalCreateFromArrayType(arrayType, 1, &length, null); } @@ -228,9 +228,9 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); if (!arrayType.IsArray) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); if (arrayType.GetArrayRank() != lengths.Length) - ThrowHelper.ThrowArgumentException_Argument_IncompatibleArrayType(); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); // 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 @@ -255,9 +255,9 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); if (!arrayType.IsArray) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); if (arrayType.GetArrayRank() != lengths.Length) - ThrowHelper.ThrowArgumentException_Argument_IncompatibleArrayType(); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); // 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 diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 79a4f599c4fee..e692019683331 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -1149,8 +1149,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Format_UnclosedFormatItem; case ExceptionResource.Format_ExpectedAsciiDigit: return SR.Format_ExpectedAsciiDigit; - case ExceptionResource.Arg_MustBeArrayType: - return SR.Arg_MustBeArrayType; + case ExceptionResource.Argument_HasToBeArrayClass: + return SR.Argument_HasToBeArrayClass; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1348,6 +1348,6 @@ internal enum ExceptionResource Format_UnexpectedClosingBrace, Format_UnclosedFormatItem, Format_ExpectedAsciiDigit, - Arg_MustBeArrayType, + Argument_HasToBeArrayClass, } } From f32e6e5c720b099d2302500d33443ce03197523e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 25 Oct 2023 14:18:16 +0200 Subject: [PATCH 08/23] code review: - add missing comments for new public APIs - use the new API in DataTypeImplementation (and solve a TODO) - fix typo - remove code that was commented out - improve test name --- .../src/System/Array.NativeAot.cs | 2 +- .../src/System/Array.cs | 58 +++++++++++++++++++ .../Xml/Schema/DataTypeImplementation.cs | 9 +-- .../System.Runtime/tests/System/ArrayTests.cs | 31 ++++------ 4 files changed, 76 insertions(+), 24 deletions(-) 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 201dcd314242c..28b8dcbf7cf24 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 @@ -76,7 +76,7 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in } else { - // Create a local copy of the lengths that cannot be motified by the caller + // 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]; diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 092279c2f4758..5496d8f0b926d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -207,6 +207,20 @@ 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, using zero-based indexing. + /// is null. + /// is negative. + /// is not an array type. + /// -or- + /// is not one-dimensional array. + /// + /// When possible, this method should be preferred over , as it allows + /// to avoid a call to method (improved performance and AOT-friendliness). public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int length) { ArgumentNullException.ThrowIfNull(arrayType); @@ -220,6 +234,25 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int lengt return InternalCreateFromArrayType(arrayType, 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 possible, this method should be preferred over , as it allows + /// to avoid a call to method (improved performance and AOT-friendliness). public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params int[] lengths) { ArgumentNullException.ThrowIfNull(arrayType); @@ -244,6 +277,31 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in return InternalCreateFromArrayType(arrayType, 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. + /// AOT: any value in is different than zero. + /// When possible, this method should be preferred over , as it allows + /// to avoid a call to method (improved performance and AOT-friendliness). public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] lengths, int[] lowerBounds) { ArgumentNullException.ThrowIfNull(arrayType); 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 d24b86f64e303..21e2bd2564f9e 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 @@ -1143,11 +1143,12 @@ 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()!); + { + Array result = Array.CreateInstanceFromArrayType(arrayType, values.Count); + values.CopyTo(result); + return result; + } } } diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index ab8271c165c4e..d9fa41e44606f 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/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 { @@ -1896,10 +1894,6 @@ public static void CreateInstance_Advanced(Type elementType, int[] lengths, int[ // Use CreateInstance(Type, long[]) Array array5 = Array.CreateInstance(elementType, lengths.Select(length => (long)length).ToArray()); VerifyArray(array5, elementType, lengths, lowerBounds, repeatedValue); - - // Use CreateInstanceFromArrayType(Type, long[]) - //array5 = Array.CreateInstanceFromArrayType(array5.GetType(), lengths.Select(length => (long)length).ToArray()); - //VerifyArray(array5, elementType, lengths, lowerBounds, repeatedValue); } // Use CreateInstance(Type, int[], int[]) Array array6 = Array.CreateInstance(elementType, lengths, lowerBounds); @@ -1923,7 +1917,6 @@ public static void CreateInstance_NullElementType_ThrowsArgumentNullException() 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 long[1])); AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(null, new int[1], new int[1])); } @@ -1950,13 +1943,19 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele Assert.Throws(() => Array.CreateInstance(elementType, new int[1])); Assert.Throws(() => Array.CreateInstance(elementType, new long[1])); Assert.Throws(() => Array.CreateInstance(elementType, new int[1], new int[1])); + } - Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 0)); - Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 0, 0)); - Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 0, 0, 0)); - Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new int[1])); - //Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new long[1])); - Assert.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, 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] @@ -1976,7 +1975,6 @@ public void CreateInstance_TypeNotRuntimeType_ThrowsArgumentException() 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 long[1])); AssertExtensions.Throws("arrayType", () => Array.CreateInstanceFromArrayType(elementType, new int[1], new int[1])); } } @@ -1995,7 +1993,6 @@ public void CreateInstance_NegativeLength_ThrowsArgumentOutOfRangeException() 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 long[] { -1 })); AssertExtensions.Throws("lengths[0]", () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[] { -1 }, new int[1])); } @@ -2025,7 +2022,6 @@ public void CreateInstance_LengthsNull_ThrowsArgumentNullException() 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[]), (long[])null)); AssertExtensions.Throws("lengths", () => Array.CreateInstanceFromArrayType(typeof(int[]), null, new int[1])); } @@ -2038,7 +2034,6 @@ public void CreateInstance_LengthsEmpty_ThrowsArgumentException() 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 long[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])); } @@ -2057,8 +2052,6 @@ public void CreateInstance_LowerBoundNull_ThrowsArgumentNullException() public void CreateInstance_InvalidLengthInLongLength_ThrowsArgumentOutOfRangeException(long length) { AssertExtensions.Throws("len", () => Array.CreateInstance(typeof(int), new long[] { length })); - - //AssertExtensions.Throws("len", () => Array.CreateInstanceFromArrayType(typeof(int[]), new long[] { length })); } [Theory] From 8a9e0158fe2dcc9cf72b8642ffa45092df37e6e6 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 25 Oct 2023 15:55:45 +0200 Subject: [PATCH 09/23] address code review feedback: simplify the code --- .../System/Xml/Schema/DataTypeImplementation.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) 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 21e2bd2564f9e..f06d23a613486 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,8 +1126,12 @@ internal override RestrictionFlags ValidRestrictionFlags values.Add(typedValue); } Debug.Assert(_itemType.ListValueType.GetElementType() == _itemType.ValueType); - array = ToArray(values, _itemType.ListValueType); + arrayType = _itemType.ListValueType; } + + Array array = Array.CreateInstanceFromArrayType(arrayType, values.Count); + values.CopyTo(array); + if (values.Count < _minListSize) { return new XmlSchemaException(SR.Sch_EmptyAttributeValue, string.Empty); @@ -1142,13 +1146,6 @@ internal override RestrictionFlags ValidRestrictionFlags Error: return exception; - - static Array ToArray(ArrayList values, Type arrayType) - { - Array result = Array.CreateInstanceFromArrayType(arrayType, values.Count); - values.CopyTo(result); - return result; - } } } From 31e43a9260084f2acfbf224ca7de6c050d90a0e8 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 27 Oct 2023 11:00:45 +0200 Subject: [PATCH 10/23] address code review feedback --- .../src/System/Array.NativeAot.cs | 16 -------- .../src/System/Array.cs | 39 +++++++++++++++---- .../src/System/ThrowHelper.cs | 9 +++++ .../Xml/Schema/DataTypeImplementation.cs | 6 +-- .../System.Runtime/tests/System/ArrayTests.cs | 16 ++++++++ 5 files changed, 59 insertions(+), 27 deletions(-) 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 28b8dcbf7cf24..fdf95572c9eed 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 @@ -111,22 +111,6 @@ private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank } } -#pragma warning disable CA1859 // https://github.com/dotnet/roslyn-analyzers/issues/6451 - private static void ValidateElementType(Type elementType) - { - while (elementType.IsArray) - { - elementType = elementType.GetElementType()!; - } - 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() { EETypePtr pElementEEType = ElementEEType; diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 5496d8f0b926d..e64eb43169186 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -226,8 +226,8 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int lengt ArgumentNullException.ThrowIfNull(arrayType); ArgumentOutOfRangeException.ThrowIfNegative(length); - if (!arrayType.IsArray) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); + ValidateArrayType(arrayType); + if (arrayType.GetArrayRank() != 1) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported, ExceptionArgument.arrayType); @@ -257,11 +257,11 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in { ArgumentNullException.ThrowIfNull(arrayType); ArgumentNullException.ThrowIfNull(lengths); + + ValidateArrayType(arrayType); + if (lengths.Length == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); - - if (!arrayType.IsArray) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); if (arrayType.GetArrayRank() != lengths.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); @@ -307,13 +307,13 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len ArgumentNullException.ThrowIfNull(arrayType); ArgumentNullException.ThrowIfNull(lengths); ArgumentNullException.ThrowIfNull(lowerBounds); + + ValidateArrayType(arrayType); + if (lengths.Length != lowerBounds.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RanksAndBounds); if (lengths.Length == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); - - if (!arrayType.IsArray) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); if (arrayType.GetArrayRank() != lengths.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); @@ -330,6 +330,29 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len return InternalCreateFromArrayType(arrayType, lengths.Length, pLengths, pLowerBounds); } + private static void ValidateArrayType(Type arrayType) + { + if (!arrayType.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); + + ValidateElementType(arrayType.GetElementType()!); + } + + private static void ValidateElementType(Type elementType) + { + while (elementType.IsArray) + { + elementType = elementType.GetElementType()!; + } + + if (elementType.IsByRef || elementType.IsByRefLike) + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ByRefLikeArray); + if (elementType == typeof(void)) + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_VoidArray); + if (elementType.ContainsGenericParameters) + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_OpenType); + } + 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 8771d9b25fe86..812b6a872aa39 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -1055,6 +1055,12 @@ private static string GetResourceString(ExceptionResource resource) return SR.NotSupported_KeyCollectionSet; case ExceptionResource.NotSupported_ValueCollectionSet: return SR.NotSupported_ValueCollectionSet; + case ExceptionResource.NotSupported_ByRefLikeArray: + return SR.NotSupported_ByRefLikeArray; + case ExceptionResource.NotSupported_VoidArray: + return SR.NotSupported_VoidArray; + case ExceptionResource.NotSupported_OpenType: + return SR.NotSupported_OpenType; case ExceptionResource.InvalidOperation_NullArray: return SR.InvalidOperation_NullArray; case ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted: @@ -1311,6 +1317,9 @@ internal enum ExceptionResource Serialization_NullKey, NotSupported_KeyCollectionSet, NotSupported_ValueCollectionSet, + NotSupported_ByRefLikeArray, + NotSupported_VoidArray, + NotSupported_OpenType, InvalidOperation_NullArray, TaskT_TransitionToFinal_AlreadyCompleted, TaskCompletionSourceT_TrySetException_NullException, 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 f06d23a613486..38242bd650da8 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 @@ -1129,14 +1129,14 @@ internal override RestrictionFlags ValidRestrictionFlags arrayType = _itemType.ListValueType; } - Array array = Array.CreateInstanceFromArrayType(arrayType, values.Count); - values.CopyTo(array); - 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; diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index d9fa41e44606f..1102b3190c099 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -1945,6 +1945,22 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele Assert.Throws(() => Array.CreateInstance(elementType, new int[1], new int[1])); } + [Theory] + [InlineData(typeof(void))] + [InlineData(typeof(GenericClass<>))] + // not using any by-ref type here as MakeArrayType throws for them, same goes for Type.GetType("SomeByRef[]") + public void CreateInstanceFromArrayType_NotSupportedArrayType_ThrowsNotSupportedException(Type elementType) + { + foreach (Type type in new Type[] { elementType, elementType.MakeArrayType() /* array of arrays */ }) + { + Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(), 0)); + Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(rank: 2), 0, 0)); + Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(rank: 3), 0, 0, 0)); + Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(), new int[1])); + Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(), new int[1], new int[1])); + } + } + [Theory] [InlineData(typeof(int))] [InlineData(typeof(GenericClass<>))] From 387d0c5841e571e4a837af14d6e3589e7746625c Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 27 Oct 2023 15:19:47 +0200 Subject: [PATCH 11/23] supress new test on Mono, which seems to affect other tests as well, see #94086 for details --- src/libraries/System.Runtime/tests/System/ArrayTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index 1102b3190c099..10cad6814c47b 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -1949,6 +1949,7 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele [InlineData(typeof(void))] [InlineData(typeof(GenericClass<>))] // not using any by-ref type here as MakeArrayType throws for them, same goes for Type.GetType("SomeByRef[]") + [ActiveIssue("https://github.com/dotnet/runtime/issues/94086", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime))] public void CreateInstanceFromArrayType_NotSupportedArrayType_ThrowsNotSupportedException(Type elementType) { foreach (Type type in new Type[] { elementType, elementType.MakeArrayType() /* array of arrays */ }) From 4816e63b39f51cee3485094a488c9122939d2df2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 13 Nov 2023 19:12:59 -0800 Subject: [PATCH 12/23] Fix perf and corner-case behaviors --- .../src/System/Array.CoreCLR.cs | 15 +- .../classlibnative/bcltype/arraynative.cpp | 94 ++++++------ .../classlibnative/bcltype/arraynative.h | 6 +- .../src/System/Array.NativeAot.cs | 37 ++++- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 + .../src/System/Array.cs | 135 +++++++----------- .../src/System/ThrowHelper.cs | 12 -- .../System.Runtime/tests/System/ArrayTests.cs | 15 ++ 9 files changed, 164 insertions(+), 152 deletions(-) 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 ad73dbb0832ca..c8c893aa201e7 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -14,16 +14,19 @@ 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, ObjectHandleOnStack retArray); - private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank, int* pLengths, int* pLowerBounds) + private static unsafe Array InternalCreate(RuntimeType type, int rank, int* pLengths, int* pLowerBounds) { - return rank == 1 && (pLowerBounds == null || pLowerBounds[0] == 0) - ? GC.AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC.GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS) - : InternalCreate((arrayType.GetElementType() as RuntimeType)!, rank, pLengths, pLowerBounds); + Array? retArray = null; + InternalCreate(new QCallTypeHandle(ref type), rank, pLengths, pLowerBounds, ObjectHandleOnStack.Create(ref retArray)); + return retArray!; } + private static unsafe Array InternalCreateFromArrayType(RuntimeType type, int rank, int* pLengths, int* pLowerBounds) + => InternalCreate(type, -rank, pLengths, pLowerBounds); + private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { if (sourceArray == null) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 6faf613c3cf06..6796cf611ec47 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -720,7 +720,7 @@ 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()) @@ -753,62 +753,70 @@ 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, QCall::ObjectHandleOnStack retArray) { CONTRACTL { - FCALL_CHECK; - PRECONDITION(rank > 0); + 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 (rank < 0) + { + _ASSERTE((INT32)typeHnd.GetRank() == -rank); + _ASSERTE(typeHnd.IsArray()); - CorElementType kind = ELEMENT_TYPE_ARRAY; + CheckElementType(typeHnd.GetArrayElementTypeHandle()); - // 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() && (pLowerBounds == NULL || pLowerBounds[0] == 0)) { - pRet = AllocatePrimitiveArray(CorType,pLengths[0]); + GCX_COOP(); + retArray.Set(AllocateSzArray(typeHnd, pLengths[0])); goto Done; } - else - if (CorTypeInfo::IsObjRef(CorType)) + + rank = -rank; + } + else + { + CheckElementType(typeHnd); + + CorElementType CorType = typeHnd.GetSignatureCorElementType(); + + CorElementType kind = ELEMENT_TYPE_ARRAY; + + // Is it ELEMENT_TYPE_SZARRAY array? + if (rank == 1 && (pLowerBounds == NULL || pLowerBounds[0] == 0)) { - pRet = AllocateObjectArray(pLengths[0],elementType); - goto Done; + // Shortcut for common cases + if (CorTypeInfo::IsPrimitiveType(CorType)) + { + GCX_COOP(); + retArray.Set(AllocatePrimitiveArray(CorType,pLengths[0])); + goto Done; + } + else + if (CorTypeInfo::IsObjRef(CorType)) + { + GCX_COOP(); + retArray.Set(AllocateObjectArray(pLengths[0],typeHnd)); + goto Done; + } + + kind = ELEMENT_TYPE_SZARRAY; + pLowerBounds = NULL; } - kind = ELEMENT_TYPE_SZARRAY; - pLowerBounds = NULL; + // Find the Array class... + typeHnd = ClassLoader::LoadArrayTypeThrowing(typeHnd, kind, 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 +842,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 84218d698571f..a4ae831dc5709 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, 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/System/Array.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs index fdf95572c9eed..1d47698f810bc 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 @@ -55,6 +55,21 @@ internal unsafe bool IsSzArray return MethodTable.Of(); } + private static void ValidateElementType(Type elementType) + { + while (elementType.IsArray) + { + elementType = elementType.GetElementType()!; + } + + if (elementType.IsByRef || elementType.IsByRefLike) + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ByRefLikeArray); + if (elementType == typeof(void)) + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_VoidArray); + if (elementType.ContainsGenericParameters) + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_OpenType); + } + [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) { @@ -72,21 +87,27 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in if (rank == 1) { return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToEETypePtr(), pLengths[0]); - } else { + 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); } } - private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank, int* pLengths, int* pLowerBounds) + private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, int rank, int* pLengths, int* pLowerBounds) { + Debug.Assert(arrayType.IsArray); + Debug.Assert(arrayType.GetArrayRank() == rank); + + ValidateElementType(arrayType.GetElementType()); + if (pLowerBounds != null) { for (int i = 0; i < rank; i++) @@ -96,9 +117,15 @@ private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank } } + EETypePtr eeType = arrayType.TypeHandle.ToEETypePtr(); if (rank == 1) { - return RuntimeImports.RhNewArray(arrayType.TypeHandle.ToEETypePtr(), pLengths[0]); + // 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 { @@ -107,7 +134,7 @@ private static unsafe Array InternalCreateFromArrayType(Type arrayType, int rank for (int i = 0; i < rank; i++) pImmutableLengths[i] = pLengths[i]; - return NewMultiDimArray(arrayType.TypeHandle.ToEETypePtr(), pImmutableLengths, rank); + return NewMultiDimArray(eeType, pImmutableLengths, rank); } } diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 2f1f27bdf3eee..c1f031085054b 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 3682a4cef1691..b660637d6a4ac 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 e64eb43169186..6f540f4074519 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]; @@ -212,26 +195,31 @@ public static Array CreateInstance(Type elementType, params long[] lengths) /// /// 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, using zero-based indexing. + /// 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 possible, this method should be preferred over , as it allows - /// to avoid a call to method (improved performance and AOT-friendliness). + /// 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); - ValidateArrayType(arrayType); + 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 (arrayType.GetArrayRank() != 1) + if (t.GetArrayRank() != 1) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported, ExceptionArgument.arrayType); - return InternalCreateFromArrayType(arrayType, 1, &length, null); + return InternalCreateFromArrayType(t, 1, &length, null); } /// @@ -251,18 +239,21 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int lengt /// -or- /// rank does not match length. /// - /// When possible, this method should be preferred over , as it allows - /// to avoid a call to method (improved performance and AOT-friendliness). + /// 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); - ValidateArrayType(arrayType); + RuntimeType? t = arrayType as RuntimeType; + if (t == null) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.arrayType); - if (lengths.Length == 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); - if (arrayType.GetArrayRank() != lengths.Length) + if (!t.IsArray) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); + + if (t.GetArrayRank() != lengths.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); // Check to make sure the lengths are all non-negative. Note that we check this here to give @@ -274,7 +265,7 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, i, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); fixed (int* pLengths = &lengths[0]) - return InternalCreateFromArrayType(arrayType, lengths.Length, pLengths, null); + return InternalCreateFromArrayType(t, lengths.Length, pLengths, null); } /// @@ -299,24 +290,31 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in /// rank does not match length. /// /// Any value in is less than zero. - /// AOT: any value in is different than zero. - /// When possible, this method should be preferred over , as it allows - /// to avoid a call to method (improved performance and AOT-friendliness). + /// 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); - ValidateArrayType(arrayType); - if (lengths.Length != lowerBounds.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RanksAndBounds); - if (lengths.Length == 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank); - if (arrayType.GetArrayRank() != lengths.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() != lengths.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); + if (lengths.Length == 1 && 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 @@ -327,30 +325,7 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len fixed (int* pLengths = &lengths[0]) fixed (int* pLowerBounds = &lowerBounds[0]) - return InternalCreateFromArrayType(arrayType, lengths.Length, pLengths, pLowerBounds); - } - - private static void ValidateArrayType(Type arrayType) - { - if (!arrayType.IsArray) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); - - ValidateElementType(arrayType.GetElementType()!); - } - - private static void ValidateElementType(Type elementType) - { - while (elementType.IsArray) - { - elementType = elementType.GetElementType()!; - } - - if (elementType.IsByRef || elementType.IsByRefLike) - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ByRefLikeArray); - if (elementType == typeof(void)) - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_VoidArray); - if (elementType.ContainsGenericParameters) - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_OpenType); + return InternalCreateFromArrayType(t, lengths.Length, pLengths, pLowerBounds); } public static void Copy(Array sourceArray, Array destinationArray, long length) diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index f0e164b04e426..373f17c838135 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -970,8 +970,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: @@ -986,12 +984,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: @@ -1300,7 +1292,6 @@ internal enum ExceptionArgument handle, other, newSize, - lowerBounds, lengths, len, keys, @@ -1308,9 +1299,6 @@ internal enum ExceptionArgument index1, index2, index3, - length1, - length2, - length3, endIndex, elementType, arrayIndex, diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index 10cad6814c47b..8c6d6160177d8 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -2063,6 +2063,21 @@ public void CreateInstance_LowerBoundNull_ThrowsArgumentNullException() AssertExtensions.Throws("lowerBounds", () => Array.CreateInstanceFromArrayType(typeof(int[]), new int[] { 1 }, null)); } + [Fact] + public void CreateInstanceFromTemplate_Rank1() + { + Type variableBoundArrayType = typeof(object).MakeArrayType(1); + Assert.NotEqual(variableBoundArrayType, typeof(object[])); + + Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, 12).GetType(), typeof(object[])); + Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [22]).GetType(), typeof(object[])); + Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [0]).GetType(), typeof(object[])); + + Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType(), variableBoundArrayType); + + Assert.Throws(Array.CreateInstanceFromArrayType(typeof(object[]), [7], [8])); + } + [Theory] [InlineData((long)int.MaxValue + 1)] [InlineData((long)int.MinValue - 1)] From 5845090a91cc6556c64e52d54a2318e4c6ebf0b2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 14 Nov 2023 20:43:12 -0800 Subject: [PATCH 13/23] Cleanup --- .../classlibnative/bcltype/arraynative.cpp | 32 +++++++++---------- .../src/System/Array.NativeAot.cs | 4 ++- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 6796cf611ec47..0335e21257115 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -768,52 +768,50 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3 if (rank < 0) { - _ASSERTE((INT32)typeHnd.GetRank() == -rank); + rank = -rank; + + _ASSERTE(typeHnd.GetRank() == rank); _ASSERTE(typeHnd.IsArray()); CheckElementType(typeHnd.GetArrayElementTypeHandle()); - if (!typeHnd.AsMethodTable()->IsMultiDimArray() && (pLowerBounds == NULL || pLowerBounds[0] == 0)) + if (!typeHnd.AsMethodTable()->IsMultiDimArray()) { + _ASSERTE(pLowerBounds == NULL || pLowerBounds[0] == 0); + GCX_COOP(); retArray.Set(AllocateSzArray(typeHnd, pLengths[0])); goto Done; } - - rank = -rank; } else { CheckElementType(typeHnd); - CorElementType CorType = typeHnd.GetSignatureCorElementType(); - - CorElementType kind = ELEMENT_TYPE_ARRAY; - // Is it ELEMENT_TYPE_SZARRAY array? if (rank == 1 && (pLowerBounds == NULL || pLowerBounds[0] == 0)) { + CorElementType corType = typeHnd.GetSignatureCorElementType(); + // Shortcut for common cases - if (CorTypeInfo::IsPrimitiveType(CorType)) + if (CorTypeInfo::IsPrimitiveType(corType)) { GCX_COOP(); - retArray.Set(AllocatePrimitiveArray(CorType,pLengths[0])); + retArray.Set(AllocatePrimitiveArray(corType, pLengths[0])); goto Done; } - else - if (CorTypeInfo::IsObjRef(CorType)) + + typeHnd = ClassLoader::LoadArrayTypeThrowing(typeHnd); + { GCX_COOP(); - retArray.Set(AllocateObjectArray(pLengths[0],typeHnd)); + retArray.Set(AllocateSzArray(typeHnd, pLengths[0])); goto Done; } - - kind = ELEMENT_TYPE_SZARRAY; - pLowerBounds = NULL; } // Find the Array class... - typeHnd = ClassLoader::LoadArrayTypeThrowing(typeHnd, kind, rank); + typeHnd = ClassLoader::LoadArrayTypeThrowing(typeHnd, ELEMENT_TYPE_ARRAY, rank); } { 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 1d47698f810bc..62f6c228ad519 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 @@ -101,6 +101,8 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in } } + [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) { Debug.Assert(arrayType.IsArray); @@ -129,7 +131,7 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i } else { - // Create a local copy of the lengths that cannot be motified by the caller + // 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]; From 87d756dbbd5f0538e50675d0e7b1988bd364b3aa Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 14 Nov 2023 22:33:57 -0800 Subject: [PATCH 14/23] Delete superfluous validation --- src/coreclr/classlibnative/bcltype/arraynative.cpp | 7 +------ .../src/System/Array.NativeAot.cs | 11 +++-------- .../System.Private.CoreLib/src/System/ThrowHelper.cs | 9 --------- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 0335e21257115..669aa6740741e 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -722,12 +722,6 @@ FCIMPLEND // Check we're allowed to create an array with the given element type. 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()) { @@ -766,6 +760,7 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3 TypeHandle typeHnd = pTypeHnd.AsTypeHandle(); + // Negative rank means that the type handle is the array type if (rank < 0) { rank = -rank; 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 62f6c228ad519..f75caa3bde02f 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 @@ -57,17 +57,12 @@ internal unsafe bool IsSzArray private static void ValidateElementType(Type elementType) { - while (elementType.IsArray) - { - elementType = elementType.GetElementType()!; - } - if (elementType.IsByRef || elementType.IsByRefLike) - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ByRefLikeArray); + throw new NotSupportedException(SR.NotSupported_ByRefLikeArray); if (elementType == typeof(void)) - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_VoidArray); + throw new NotSupportedException(SR.NotSupported_VoidArray); if (elementType.ContainsGenericParameters) - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_OpenType); + throw new NotSupportedException(SR.NotSupported_OpenType); } [RequiresDynamicCode("The code for an array of the specified type might not be available.")] diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index b27727d4a2fca..100222ef29deb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -1094,12 +1094,6 @@ private static string GetResourceString(ExceptionResource resource) return SR.NotSupported_KeyCollectionSet; case ExceptionResource.NotSupported_ValueCollectionSet: return SR.NotSupported_ValueCollectionSet; - case ExceptionResource.NotSupported_ByRefLikeArray: - return SR.NotSupported_ByRefLikeArray; - case ExceptionResource.NotSupported_VoidArray: - return SR.NotSupported_VoidArray; - case ExceptionResource.NotSupported_OpenType: - return SR.NotSupported_OpenType; case ExceptionResource.InvalidOperation_NullArray: return SR.InvalidOperation_NullArray; case ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted: @@ -1354,9 +1348,6 @@ internal enum ExceptionResource Serialization_NullKey, NotSupported_KeyCollectionSet, NotSupported_ValueCollectionSet, - NotSupported_ByRefLikeArray, - NotSupported_VoidArray, - NotSupported_OpenType, InvalidOperation_NullArray, TaskT_TransitionToFinal_AlreadyCompleted, TaskCompletionSourceT_TrySetException_NullException, From 1f67190a5c2274aeef8d29fbedb8dc12d360bd8c Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 14 Nov 2023 22:36:43 -0800 Subject: [PATCH 15/23] Fix build break --- src/coreclr/classlibnative/bcltype/arraynative.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 669aa6740741e..9b7d874ec6a90 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -765,7 +765,7 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3 { rank = -rank; - _ASSERTE(typeHnd.GetRank() == rank); + _ASSERTE((INT32)typeHnd.GetRank() == rank); _ASSERTE(typeHnd.IsArray()); CheckElementType(typeHnd.GetArrayElementTypeHandle()); From 39536c3a37f0b44e256f6a5914a0ecea07abdc9a Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 14 Nov 2023 22:38:47 -0800 Subject: [PATCH 16/23] Smaller diff --- .../src/System/Array.NativeAot.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 f75caa3bde02f..cf138900f4a1b 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 @@ -55,16 +55,6 @@ internal unsafe bool IsSzArray return MethodTable.Of(); } - private static void ValidateElementType(Type 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); - } - [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) { @@ -135,6 +125,16 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i } } + private static void ValidateElementType(Type 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); + } + public unsafe void Initialize() { EETypePtr pElementEEType = ElementEEType; From 2429e23d94a94f83037cf01f43f9b024d51e68b9 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 14 Nov 2023 23:16:15 -0800 Subject: [PATCH 17/23] Fix build break --- .../tests/System.Runtime.Tests/System/ArrayTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8c6d6160177d8..d12015cead44d 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 @@ -2075,7 +2075,7 @@ public void CreateInstanceFromTemplate_Rank1() Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType(), variableBoundArrayType); - Assert.Throws(Array.CreateInstanceFromArrayType(typeof(object[]), [7], [8])); + Assert.Throws(() => Array.CreateInstanceFromArrayType(typeof(object[]), [7], [8])); } [Theory] From 1a6de0fdebe8bfaf68110e1e2dd3c35157981956 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 15 Nov 2023 09:25:56 +0100 Subject: [PATCH 18/23] build fix --- .../tests/System.Runtime.Tests/System/ArrayTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 d12015cead44d..8b9b151950a54 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 @@ -2067,11 +2067,11 @@ public void CreateInstance_LowerBoundNull_ThrowsArgumentNullException() public void CreateInstanceFromTemplate_Rank1() { Type variableBoundArrayType = typeof(object).MakeArrayType(1); - Assert.NotEqual(variableBoundArrayType, typeof(object[])); + Assert.NotEqual(typeof(object[]), variableBoundArrayType); - Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, 12).GetType(), typeof(object[])); - Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [22]).GetType(), typeof(object[])); - Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [0]).GetType(), typeof(object[])); + 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()); Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType(), variableBoundArrayType); From 8ce5332bf811f54ac13aa35b7ec22991a6ff0810 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 15 Nov 2023 14:03:11 -0800 Subject: [PATCH 19/23] FB, fix tests --- .../src/System/Array.CoreCLR.cs | 17 ++++++++++---- .../classlibnative/bcltype/arraynative.cpp | 9 +++----- .../classlibnative/bcltype/arraynative.h | 2 +- src/coreclr/vm/comsynchronizable.h | 1 - .../src/System/Array.cs | 6 ++--- .../System.Runtime.Tests/System/ArrayTests.cs | 23 +++++++++---------- 6 files changed, 30 insertions(+), 28 deletions(-) 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 c8c893aa201e7..ed56ad4b4b686 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -15,17 +15,24 @@ namespace System public abstract partial class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable { [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CreateInstance")] - private static unsafe partial void InternalCreate(QCallTypeHandle type, int rank, int* pLengths, int* pLowerBounds, ObjectHandleOnStack retArray); + 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 type, int rank, int* pLengths, int* pLowerBounds) + private static unsafe Array InternalCreate(RuntimeType elementType, int rank, int* pLengths, int* pLowerBounds) { Array? retArray = null; - InternalCreate(new QCallTypeHandle(ref type), rank, pLengths, pLowerBounds, ObjectHandleOnStack.Create(ref retArray)); + InternalCreate(new QCallTypeHandle(ref elementType), rank, pLengths, pLowerBounds, + fromArrayType: false, ObjectHandleOnStack.Create(ref retArray)); return retArray!; } - private static unsafe Array InternalCreateFromArrayType(RuntimeType type, int rank, int* pLengths, int* pLowerBounds) - => InternalCreate(type, -rank, pLengths, pLowerBounds); + 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 9b7d874ec6a90..4d12f892987b9 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -747,11 +747,11 @@ static void CheckElementType(TypeHandle elementType) } } -void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pLowerBounds, QCall::ObjectHandleOnStack retArray) +void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pLowerBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray) { CONTRACTL { QCALL_CHECK; - PRECONDITION(rank != 0); + PRECONDITION(rank > 0); PRECONDITION(CheckPointer(pLengths)); PRECONDITION(CheckPointer(pLowerBounds, NULL_OK)); } CONTRACTL_END; @@ -760,11 +760,8 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3 TypeHandle typeHnd = pTypeHnd.AsTypeHandle(); - // Negative rank means that the type handle is the array type - if (rank < 0) + if (createFromArrayType) { - rank = -rank; - _ASSERTE((INT32)typeHnd.GetRank() == rank); _ASSERTE(typeHnd.IsArray()); diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index a4ae831dc5709..fae3038469368 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -61,7 +61,7 @@ class ArrayNative }; -extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, QCall::ObjectHandleOnStack retArray); +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/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index a174e2cedf13d..bb50914918464 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/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 6f540f4074519..44e3e6ee35e7a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -254,7 +254,7 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, params in ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); if (t.GetArrayRank() != lengths.Length) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); + 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 @@ -310,9 +310,9 @@ public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int[] len ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_HasToBeArrayClass, ExceptionArgument.arrayType); if (t.GetArrayRank() != lengths.Length) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices, ExceptionArgument.lengths); + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices); - if (lengths.Length == 1 && lowerBounds[0] != 0 && !t.IsSZArray) + 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 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 8b9b151950a54..bdf68eada7b3a 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 @@ -1826,6 +1826,10 @@ public static unsafe IEnumerable CreateInstance_TestData() new object[] { typeof(GenericInterface), default(GenericInterface) }, new object[] { typeof(AbstractClass), default(AbstractClass) }, new object[] { typeof(StaticClass), default(StaticClass) }, + + // Arrays of void arrays + new object[] { typeof(void).MakeArrayType(), null }, + new object[] { typeof(void).MakeArrayType(1), null }, }; } @@ -1948,18 +1952,13 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele [Theory] [InlineData(typeof(void))] [InlineData(typeof(GenericClass<>))] - // not using any by-ref type here as MakeArrayType throws for them, same goes for Type.GetType("SomeByRef[]") - [ActiveIssue("https://github.com/dotnet/runtime/issues/94086", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime))] public void CreateInstanceFromArrayType_NotSupportedArrayType_ThrowsNotSupportedException(Type elementType) { - foreach (Type type in new Type[] { elementType, elementType.MakeArrayType() /* array of arrays */ }) - { - Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(), 0)); - Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(rank: 2), 0, 0)); - Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(rank: 3), 0, 0, 0)); - Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(), new int[1])); - Assert.Throws(() => Array.CreateInstanceFromArrayType(type.MakeArrayType(), new int[1], new int[1])); - } + 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] @@ -2073,7 +2072,7 @@ public void CreateInstanceFromTemplate_Rank1() Assert.Equal(typeof(object[]), Array.CreateInstanceFromArrayType(variableBoundArrayType, [22]).GetType()); Assert.Equal(typeof(object[]), Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [0]).GetType()); - Assert.Equal(Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType(), variableBoundArrayType); + Assert.Equal(variableBoundArrayType, Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType()); Assert.Throws(() => Array.CreateInstanceFromArrayType(typeof(object[]), [7], [8])); } @@ -2115,7 +2114,7 @@ public void CreateInstance_Type_LengthsPlusLowerBoundOverflows_ThrowsArgumentOut { AssertExtensions.Throws(null, () => Array.CreateInstance(typeof(int), new int[] { int.MaxValue }, new int[] { 2 })); - AssertExtensions.Throws(null, () => Array.CreateInstanceFromArrayType(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] From 19271efec9201f6ab92ad7a62248060576a4f7de Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 15 Nov 2023 17:24:46 -0800 Subject: [PATCH 20/23] Add back workaround for Mono --- .../tests/System.Runtime.Tests/System/ArrayTests.cs | 2 ++ 1 file changed, 2 insertions(+) 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 bdf68eada7b3a..1546fad4f40c1 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 @@ -1952,6 +1952,8 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele [Theory] [InlineData(typeof(void))] [InlineData(typeof(GenericClass<>))] + // not using any by-ref type here as MakeArrayType throws for them, same goes for Type.GetType("SomeByRef[]") + [ActiveIssue("https://github.com/dotnet/runtime/issues/94086", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime))] public void CreateInstanceFromArrayType_NotSupportedArrayType_ThrowsNotSupportedException(Type elementType) { Assert.Throws(() => Array.CreateInstanceFromArrayType(elementType.MakeArrayType(), 0)); From 85a44ff1d4c5e66ff649e2a8566746a93ec6aca0 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 15 Nov 2023 23:57:40 -0800 Subject: [PATCH 21/23] Skip handling of void arrays for this PR --- .../classlibnative/bcltype/arraynative.cpp | 5 +++-- .../src/System/Array.NativeAot.cs | 20 ++++++++----------- .../System.Runtime.Tests/System/ArrayTests.cs | 7 ------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 4d12f892987b9..0c1afdcbfd8b7 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -732,7 +732,7 @@ static void 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. @@ -765,7 +765,8 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3 _ASSERTE((INT32)typeHnd.GetRank() == rank); _ASSERTE(typeHnd.IsArray()); - CheckElementType(typeHnd.GetArrayElementTypeHandle()); + if (typeHnd.GetArrayElementTypeHandle().ContainsGenericVariables()) + COMPlusThrow(kNotSupportedException, W("NotSupported_OpenType")); if (!typeHnd.AsMethodTable()->IsMultiDimArray()) { 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 cf138900f4a1b..fa2db3712e060 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) { @@ -93,7 +98,8 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i Debug.Assert(arrayType.IsArray); Debug.Assert(arrayType.GetArrayRank() == rank); - ValidateElementType(arrayType.GetElementType()); + if (arrayType.ContainsGenericParameters) + throw new NotSupportedException(SR.NotSupported_OpenType); if (pLowerBounds != null) { @@ -125,16 +131,6 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i } } - private static void ValidateElementType(Type 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); - } - public unsafe void Initialize() { EETypePtr pElementEEType = ElementEEType; 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 1546fad4f40c1..e2ba16c2e8069 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 @@ -1826,10 +1826,6 @@ public static unsafe IEnumerable CreateInstance_TestData() new object[] { typeof(GenericInterface), default(GenericInterface) }, new object[] { typeof(AbstractClass), default(AbstractClass) }, new object[] { typeof(StaticClass), default(StaticClass) }, - - // Arrays of void arrays - new object[] { typeof(void).MakeArrayType(), null }, - new object[] { typeof(void).MakeArrayType(1), null }, }; } @@ -1950,10 +1946,7 @@ public void CreateInstance_NotSupportedType_ThrowsNotSupportedException(Type ele } [Theory] - [InlineData(typeof(void))] [InlineData(typeof(GenericClass<>))] - // not using any by-ref type here as MakeArrayType throws for them, same goes for Type.GetType("SomeByRef[]") - [ActiveIssue("https://github.com/dotnet/runtime/issues/94086", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime))] public void CreateInstanceFromArrayType_NotSupportedArrayType_ThrowsNotSupportedException(Type elementType) { Assert.Throws(() => Array.CreateInstanceFromArrayType(elementType.MakeArrayType(), 0)); From 10660c1e3bbee33f5c7267d6817e4e9aec7d8c1e Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 16 Nov 2023 05:41:09 -0800 Subject: [PATCH 22/23] Fix tests for native AOT --- .../tests/System.Runtime.Tests/System/ArrayTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 e2ba16c2e8069..2850347d59d6a 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 @@ -2067,7 +2067,10 @@ public void CreateInstanceFromTemplate_Rank1() Assert.Equal(typeof(object[]), Array.CreateInstanceFromArrayType(variableBoundArrayType, [22]).GetType()); Assert.Equal(typeof(object[]), Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [0]).GetType()); - Assert.Equal(variableBoundArrayType, Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType()); + if (PlatformDetection.IsNonZeroLowerBoundArraySupported) + { + Assert.Equal(variableBoundArrayType, Array.CreateInstanceFromArrayType(variableBoundArrayType, [33], [22]).GetType()); + } Assert.Throws(() => Array.CreateInstanceFromArrayType(typeof(object[]), [7], [8])); } From ad50dd52e5760726f70b5c4c186781c4f77bb377 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 16 Nov 2023 05:53:19 -0800 Subject: [PATCH 23/23] Revert unnecessary change --- .../System.Runtime/tests/System.Runtime.Tests/Helpers.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/Helpers.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/Helpers.cs index 9da1e7e8fed1e..5d44dc649976b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/Helpers.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/Helpers.cs @@ -45,7 +45,6 @@ private static Type RefEmitType() private sealed class NonRuntimeType : MockType { public sealed override Type UnderlyingSystemType => this; - protected sealed override bool IsArrayImpl() => false; } } }