Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: Create array from array type #88620

Merged
merged 26 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand All @@ -18,6 +17,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))
AlexRadch marked this conversation as resolved.
Show resolved Hide resolved
{
return GC.AllocateNewArray(arrayType.TypeHandle.Value, pLengths[0], GC.GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GC.AllocateNewArray is designed for allocating the special arrays. You may want to measure how slow it is for allocating ordinary small arrays.

We may want to have a new runtime calls for this that will handle the multi-dim case too

Copy link
Contributor Author

@AlexRadch AlexRadch Jul 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got the next benchmark scores: https://github.com/AlexRadch/Temp/tree/GC_AllocateNewArray_Bench

|             Type |                Method |   Count |             Mean |          Error |         StdDev |           Median | Ratio | RatioSD |     Gen0 |     Gen1 |     Gen2 | Allocated | Alloc Ratio |
|----------------- |---------------------- |-------- |-----------------:|---------------:|---------------:|-----------------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|
|    BenchArrayInt |                Create |       0 |         8.531 ns |      0.0618 ns |      0.0548 ns |         8.537 ns |  1.00 |    0.00 |   0.0057 |        - |        - |      24 B |        1.00 |
|    BenchArrayInt |              Allocate |       0 |        45.232 ns |      0.9173 ns |      1.2245 ns |        44.796 ns |  5.36 |    0.18 |   0.0057 |        - |        - |      24 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |       0 |         8.971 ns |      0.0736 ns |      0.0652 ns |         8.985 ns |  1.05 |    0.01 |   0.0057 |        - |        - |      24 B |        1.00 |
| BenchArrayString |                Create |       0 |         8.193 ns |      0.1971 ns |      0.3554 ns |         8.040 ns |  0.96 |    0.04 |   0.0057 |        - |        - |      24 B |        1.00 |
| BenchArrayString |              Allocate |       0 |        46.420 ns |      0.9399 ns |      1.4353 ns |        46.031 ns |  5.51 |    0.19 |   0.0057 |        - |        - |      24 B |        1.00 |
| BenchArrayString | AllocateUninitialized |       0 |         7.978 ns |      0.1903 ns |      0.1869 ns |         7.957 ns |  0.94 |    0.02 |   0.0057 |        - |        - |      24 B |        1.00 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create |       1 |        10.022 ns |      0.0600 ns |      0.0532 ns |        10.007 ns |  1.00 |    0.00 |   0.0076 |        - |        - |      32 B |        1.00 |
|    BenchArrayInt |              Allocate |       1 |        45.933 ns |      0.6901 ns |      0.6117 ns |        45.977 ns |  4.58 |    0.06 |   0.0076 |        - |        - |      32 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |       1 |        10.480 ns |      0.1064 ns |      0.0943 ns |        10.459 ns |  1.05 |    0.01 |   0.0076 |        - |        - |      32 B |        1.00 |
| BenchArrayString |                Create |       1 |         9.512 ns |      0.2020 ns |      0.1791 ns |         9.442 ns |  0.95 |    0.02 |   0.0076 |        - |        - |      32 B |        1.00 |
| BenchArrayString |              Allocate |       1 |        47.115 ns |      0.4613 ns |      0.4089 ns |        47.239 ns |  4.70 |    0.04 |   0.0076 |        - |        - |      32 B |        1.00 |
| BenchArrayString | AllocateUninitialized |       1 |         9.621 ns |      0.1906 ns |      0.3533 ns |         9.533 ns |  0.99 |    0.04 |   0.0076 |        - |        - |      32 B |        1.00 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create |       5 |        13.273 ns |      0.2988 ns |      0.3197 ns |        13.212 ns |  1.00 |    0.00 |   0.0115 |        - |        - |      48 B |        1.00 |
|    BenchArrayInt |              Allocate |       5 |        48.570 ns |      0.7197 ns |      0.6380 ns |        48.606 ns |  3.64 |    0.10 |   0.0114 |        - |        - |      48 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |       5 |        13.704 ns |      0.1898 ns |      0.1776 ns |        13.657 ns |  1.03 |    0.02 |   0.0115 |        - |        - |      48 B |        1.00 |
| BenchArrayString |                Create |       5 |        15.731 ns |      0.1732 ns |      0.1353 ns |        15.740 ns |  1.17 |    0.03 |   0.0153 |        - |        - |      64 B |        1.33 |
| BenchArrayString |              Allocate |       5 |        53.037 ns |      0.3743 ns |      0.3318 ns |        53.119 ns |  3.97 |    0.10 |   0.0153 |        - |        - |      64 B |        1.33 |
| BenchArrayString | AllocateUninitialized |       5 |        15.644 ns |      0.1767 ns |      0.1653 ns |        15.627 ns |  1.17 |    0.03 |   0.0153 |        - |        - |      64 B |        1.33 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create |      10 |        15.196 ns |      0.1711 ns |      0.1429 ns |        15.183 ns |  1.00 |    0.00 |   0.0153 |        - |        - |      64 B |        1.00 |
|    BenchArrayInt |              Allocate |      10 |        51.142 ns |      0.4151 ns |      0.3467 ns |        51.112 ns |  3.37 |    0.04 |   0.0153 |        - |        - |      64 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |      10 |        16.782 ns |      0.2404 ns |      0.2249 ns |        16.719 ns |  1.11 |    0.02 |   0.0153 |        - |        - |      64 B |        1.00 |
| BenchArrayString |                Create |      10 |        23.496 ns |      0.3257 ns |      0.2720 ns |        23.519 ns |  1.55 |    0.02 |   0.0249 |        - |        - |     104 B |        1.62 |
| BenchArrayString |              Allocate |      10 |        63.847 ns |      0.8255 ns |      0.6445 ns |        64.097 ns |  4.20 |    0.04 |   0.0248 |        - |        - |     104 B |        1.62 |
| BenchArrayString | AllocateUninitialized |      10 |        23.543 ns |      0.2681 ns |      0.2377 ns |        23.540 ns |  1.55 |    0.02 |   0.0249 |        - |        - |     104 B |        1.62 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create |     100 |        83.160 ns |      1.6437 ns |      1.7587 ns |        82.706 ns |  1.00 |    0.00 |   0.1013 |        - |        - |     424 B |        1.00 |
|    BenchArrayInt |              Allocate |     100 |       120.106 ns |      1.9915 ns |      1.8629 ns |       119.715 ns |  1.44 |    0.05 |   0.1013 |        - |        - |     424 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |     100 |        84.808 ns |      0.6549 ns |      0.5113 ns |        84.780 ns |  1.01 |    0.02 |   0.1013 |        - |        - |     424 B |        1.00 |
| BenchArrayString |                Create |     100 |       160.460 ns |      1.8637 ns |      1.7433 ns |       160.166 ns |  1.93 |    0.05 |   0.1969 |        - |        - |     824 B |        1.94 |
| BenchArrayString |              Allocate |     100 |       196.155 ns |      2.7282 ns |      2.4185 ns |       195.607 ns |  2.35 |    0.04 |   0.1969 |        - |        - |     824 B |        1.94 |
| BenchArrayString | AllocateUninitialized |     100 |       159.295 ns |      2.0602 ns |      1.7203 ns |       159.233 ns |  1.91 |    0.04 |   0.1969 |        - |        - |     824 B |        1.94 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create |    1000 |       777.217 ns |     12.3406 ns |     10.9396 ns |       775.492 ns |  1.00 |    0.00 |   0.9613 |        - |        - |    4024 B |        1.00 |
|    BenchArrayInt |              Allocate |    1000 |       789.251 ns |     10.2752 ns |      9.6114 ns |       787.657 ns |  1.02 |    0.01 |   0.9613 |        - |        - |    4024 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |    1000 |       187.433 ns |      3.7948 ns |      7.6657 ns |       184.953 ns |  0.25 |    0.01 |   0.9584 |        - |        - |    4024 B |        1.00 |
| BenchArrayString |                Create |    1000 |     1,554.818 ns |     15.1447 ns |     14.1664 ns |     1,559.290 ns |  2.00 |    0.04 |   1.9150 |        - |        - |    8024 B |        1.99 |
| BenchArrayString |              Allocate |    1000 |     1,562.925 ns |     13.4857 ns |     11.9547 ns |     1,563.005 ns |  2.01 |    0.03 |   1.9150 |        - |        - |    8024 B |        1.99 |
| BenchArrayString | AllocateUninitialized |    1000 |     1,562.850 ns |     19.5024 ns |     17.2883 ns |     1,564.989 ns |  2.01 |    0.03 |   1.9150 |        - |        - |    8024 B |        1.99 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create |   10000 |     7,200.916 ns |     67.9913 ns |     63.5991 ns |     7,185.784 ns |  1.00 |    0.00 |   9.5215 |        - |        - |   40024 B |        1.00 |
|    BenchArrayInt |              Allocate |   10000 |     7,149.810 ns |     84.2155 ns |     78.7753 ns |     7,130.345 ns |  0.99 |    0.02 |   9.5215 |        - |        - |   40024 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |   10000 |       651.649 ns |      7.0545 ns |      5.8909 ns |       650.162 ns |  0.09 |    0.00 |   9.5234 |        - |        - |   40024 B |        1.00 |
| BenchArrayString |                Create |   10000 |    14,001.614 ns |    237.3965 ns |    198.2368 ns |    14,009.929 ns |  1.94 |    0.03 |  18.8599 |        - |        - |   80024 B |        2.00 |
| BenchArrayString |              Allocate |   10000 |    14,089.164 ns |    142.5671 ns |    119.0500 ns |    14,099.614 ns |  1.96 |    0.02 |  18.8599 |        - |        - |   80024 B |        2.00 |
| BenchArrayString | AllocateUninitialized |   10000 |    14,088.178 ns |     65.6579 ns |     58.2040 ns |    14,101.808 ns |  1.95 |    0.02 |  18.8599 |        - |        - |   80024 B |        2.00 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create |  100000 |    29,484.404 ns |    311.6528 ns |    382.7376 ns |    29,507.994 ns |  1.00 |    0.00 | 124.9695 | 124.9695 | 124.9695 |  400066 B |        1.00 |
|    BenchArrayInt |              Allocate |  100000 |    29,567.013 ns |    449.1566 ns |    420.1414 ns |    29,604.886 ns |  1.00 |    0.02 | 124.9695 | 124.9695 | 124.9695 |  400066 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized |  100000 |    26,492.538 ns |    292.9388 ns |    259.6827 ns |    26,564.154 ns |  0.90 |    0.02 | 124.9695 | 124.9695 | 124.9695 |  400066 B |        1.00 |
| BenchArrayString |                Create |  100000 |    54,173.601 ns |    672.7485 ns |    596.3741 ns |    54,227.924 ns |  1.84 |    0.04 | 249.9390 | 249.9390 | 249.9390 |  800107 B |        2.00 |
| BenchArrayString |              Allocate |  100000 |    54,339.854 ns |    967.7527 ns |    808.1174 ns |    54,227.414 ns |  1.85 |    0.03 | 249.9390 | 249.9390 | 249.9390 |  800107 B |        2.00 |
| BenchArrayString | AllocateUninitialized |  100000 |    53,836.765 ns |    441.4254 ns |    391.3121 ns |    53,730.786 ns |  1.83 |    0.03 | 249.9390 | 249.9390 | 249.9390 |  800107 B |        2.00 |
|                  |                       |         |                  |                |                |                  |       |         |          |          |          |           |             |
|    BenchArrayInt |                Create | 1000000 |   208,606.318 ns |  3,285.4765 ns |  3,073.2368 ns |   208,015.698 ns |  1.00 |    0.00 | 999.7559 | 999.7559 | 999.7559 | 4000356 B |        1.00 |
|    BenchArrayInt |              Allocate | 1000000 |   207,851.978 ns |  3,900.6592 ns |  3,830.9689 ns |   206,175.659 ns |  1.00 |    0.02 | 999.7559 | 999.7559 | 999.7559 | 4000357 B |        1.00 |
|    BenchArrayInt | AllocateUninitialized | 1000000 |   183,179.046 ns |  1,604.5172 ns |  1,500.8664 ns |   183,096.216 ns |  0.88 |    0.01 | 999.7559 | 999.7559 | 999.7559 | 4000329 B |        1.00 |
| BenchArrayString |                Create | 1000000 | 2,440,452.062 ns | 48,071.0001 ns | 64,173.4044 ns | 2,449,305.859 ns | 11.64 |    0.36 | 140.6250 | 140.6250 | 140.6250 | 8000068 B |        2.00 |
| BenchArrayString |              Allocate | 1000000 | 2,427,959.420 ns | 37,518.6029 ns | 31,329.7327 ns | 2,437,695.117 ns | 11.68 |    0.20 | 132.8125 | 132.8125 | 132.8125 | 8000066 B |        2.00 |
| BenchArrayString | AllocateUninitialized | 1000000 | 2,412,512.165 ns | 33,286.0854 ns | 29,507.2477 ns | 2,419,748.047 ns | 11.60 |    0.18 | 140.6250 | 140.6250 | 140.6250 | 8000068 B |        2.00 |

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, these benchmark shows that GC.Allocate has high overhead for small sizes, but they do not show where the new API is going to land exactly.

I would be interesting to benchmark new int[1] vs. Array.CreateInstanceFromArrayType(typeof(int[]), 1) vs. Array.CreateInstance(typeof(int), 1). We want Array.CreateInstanceFromArrayType performance to be as close as possible to new int[1].

}

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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ public static Attribute Instantiate(this CustomAttributeData cad)
IList<CustomAttributeTypedArgument>? typedElements = (IList<CustomAttributeTypedArgument>?)(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);
AlexRadch marked this conversation as resolved.
Show resolved Hide resolved
for (int i = 0; i < typedElements.Count; i++)
{
object? elementValue = typedElements[i].Convert();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
67 changes: 35 additions & 32 deletions src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

<!--
Microsoft ResX Schema
Version 2.0

The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.

Example:

... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
Expand All @@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>

There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.

Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.

The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:

Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.

mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
Expand Down Expand Up @@ -376,7 +376,7 @@
<data name="Arg_FormatException" xml:space="preserve">
<value>One of the identified items was in an invalid format.</value>
</data>
<data name= "Arg_GenericArgumentsAfterArrayOrPointerType" xml:space="preserve">
<data name="Arg_GenericArgumentsAfterArrayOrPointerType" xml:space="preserve">
<value>Generic arguments after array spec or pointer type.</value>
</data>
<data name="Arg_GenericParameter" xml:space="preserve">
Expand Down Expand Up @@ -1080,7 +1080,7 @@
<data name="Argument_FieldDoesNotBelongToConstructorClass" xml:space="preserve">
<value>Field '{0}' does not belong to the same class as the constructor.</value>
</data>
<data name="Argument_FieldDoesNotHaveAValidType" xml:space="preserve">
<data name="Argument_FieldDoesNotHaveAValidType" xml:space="preserve">
<value>Field '{0}' does not have a valid type.</value>
</data>
<data name="Argument_FieldDoesNotHaveAValidValue" xml:space="preserve">
Expand Down Expand Up @@ -1496,7 +1496,7 @@
<value>Path cannot be the empty string or all whitespace.</value>
</data>
<data name="Argument_ParameterHasUnmatchedArgumentValue" xml:space="preserve">
<value>Value of argument {0} does not match parameter type: {1} -> {2}.</value>
<value>Value of argument {0} does not match parameter type: {1} -&gt; {2}.</value>
</data>
<data name="Argument_ParameterInvalidType" xml:space="preserve">
<value>Parameter {0} does not have a valid type.</value>
Expand Down Expand Up @@ -3762,7 +3762,7 @@
<value>"Property '{0}' does not have a setter.</value>
</data>
<data name="Argument_PropertyUnmatchingPropertyType" xml:space="preserve">
<value>"Value of property '{0}' does not match property type: '{1}' -> '{2}'.</value>
<value>"Value of property '{0}' does not match property type: '{1}' -&gt; '{2}'.</value>
</data>
<data name="AssemblyDependencyResolver_FailedToLoadHostpolicy" xml:space="preserve">
<value>Cannot load hostpolicy library. AssemblyDependencyResolver is currently only supported if the runtime is hosted through hostpolicy library.</value>
Expand Down Expand Up @@ -4235,4 +4235,7 @@
<data name="OutOfMemory_StringTooLong" xml:space="preserve">
<value>String length exceeded supported range.</value>
</data>
</root>
<data name="Arg_MustBeArrayType" xml:space="preserve">
<value>Type must be a array type.</value>
AlexRadch marked this conversation as resolved.
Show resolved Hide resolved
</data>
</root>
65 changes: 65 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,71 @@ public static Array CreateInstance(Type elementType, params long[] lengths)
return CreateInstance(elementType, intLengths);
}

public static unsafe Array CreateInstanceFromArrayType(Type arrayType, int length)
AlexRadch marked this conversation as resolved.
Show resolved Hide resolved
{
ArgumentNullException.ThrowIfNull(arrayType);
ArgumentOutOfRangeException.ThrowIfNegative(length);

if (!arrayType.IsArray)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeArrayType, ExceptionArgument.arrayType);
if (arrayType.GetArrayRank() != 1)
ThrowHelper.ThrowArgumentException_Argument_IncompatibleArrayType();

return InternalCreateFromArrayType(arrayType, 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);
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
// engine's low level allocation function after having made a copy of the array to prevent a
// malicious caller from mutating the array after this check.
for (int i = 0; i < lengths.Length; i++)
if (lengths[i] < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, i, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);

fixed (int* pLengths = &lengths[0])
return InternalCreateFromArrayType(arrayType, 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);
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
// engine's low level allocation function after having made a copy of the array to prevent a
// malicious caller from mutating the array after this check.
for (int i = 0; i < lengths.Length; i++)
if (lengths[i] < 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.lengths, i, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);

fixed (int* pLengths = &lengths[0])
fixed (int* pLowerBounds = &lowerBounds[0])
return InternalCreateFromArrayType(arrayType, lengths.Length, pLengths, pLowerBounds);
}

public static void Copy(Array sourceArray, Array destinationArray, long length)
{
int ilength = (int)length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 "";
Expand Down Expand Up @@ -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 "";
Expand Down Expand Up @@ -1258,6 +1262,7 @@ internal enum ExceptionArgument
anyOf,
overlapped,
minimumBytes,
arrayType,
}

//
Expand Down Expand Up @@ -1343,5 +1348,6 @@ internal enum ExceptionResource
Format_UnexpectedClosingBrace,
Format_UnclosedFormatItem,
Format_ExpectedAsciiDigit,
Arg_MustBeArrayType,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
jkotas marked this conversation as resolved.
Show resolved Hide resolved
}

for (int i = 0; i < collectionMember.Count; i++)
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>() { throw null; }
public static bool Exists<T>(T[] array, System.Predicate<T> match) { throw null; }
public static void Fill<T>(T[] array, T value) { }
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.Runtime/tests/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Loading
Loading