Skip to content

Commit

Permalink
Move Array.CreateInstance methods to shared CoreLib (#66025)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkotas authored Mar 9, 2022
1 parent 6187fdf commit f977881
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 432 deletions.
113 changes: 1 addition & 112 deletions src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,119 +15,8 @@ namespace System
// IList<U> and IReadOnlyList<U>, where T : U dynamically. See the SZArrayHelper class for details.
public abstract partial class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable
{
// Create instance will create an array
[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();

RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType;
if (t == null)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.elementType);
return InternalCreate((void*)t.TypeHandle.Value, 1, &length, null);
}

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);

RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType;
if (t == null)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.elementType);
int* pLengths = stackalloc int[2];
pLengths[0] = length1;
pLengths[1] = length2;
return InternalCreate((void*)t.TypeHandle.Value, 2, pLengths, null);
}

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);

RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType;
if (t == null)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.elementType);
int* pLengths = stackalloc int[3];
pLengths[0] = length1;
pLengths[1] = length2;
pLengths[2] = length3;
return InternalCreate((void*)t.TypeHandle.Value, 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);
if (lengths.Length == 0)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank);

RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType;
if (t == null)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.elementType);

// Check to make sure the lengths are all positive. 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((void*)t.TypeHandle.Value, lengths.Length, pLengths, null);
}

[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);
if (lengths.Length != lowerBounds!.Length)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RanksAndBounds);
if (lengths.Length == 0)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank);

RuntimeType? t = elementType.UnderlyingSystemType as RuntimeType;
if (t == null)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_MustBeType, ExceptionArgument.elementType);

// Check to make sure the lenghts are all positive. 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((void*)t.TypeHandle.Value, lengths.Length, pLengths, pLowerBounds);
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe Array InternalCreate(void* elementType, int rank, int* pLengths, int* pLowerBounds);
private static extern unsafe Array InternalCreate(RuntimeType elementType, int rank, int* pLengths, int* pLowerBounds);

// Copies length elements from sourceArray, starting at index 0, to
// destinationArray, starting at index 0.
Expand Down
60 changes: 21 additions & 39 deletions src/coreclr/classlibnative/bcltype/arraynative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,10 +815,9 @@ FCIMPLEND
void ArrayNative::CheckElementType(TypeHandle elementType)
{
// Checks apply recursively for arrays of arrays etc.
if (elementType.IsArray())
while (elementType.IsArray())
{
CheckElementType(elementType.GetArrayElementTypeHandle());
return;
elementType = elementType.GetArrayElementTypeHandle();
}

// Check for simple types first.
Expand All @@ -837,28 +836,16 @@ void ArrayNative::CheckElementType(TypeHandle elementType)
// Check for Void.
if (elementType.GetSignatureCorElementType() == ELEMENT_TYPE_VOID)
COMPlusThrow(kNotSupportedException, W("NotSupported_VoidArray"));

// That's all the dangerous simple types we know, it must be OK.
return;
}

// ByRefs and generic type variables are never allowed.
if (elementType.IsByRef() || elementType.IsGenericVariable())
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));

// We can create pointers and function pointers, but it requires skip verification permission.
CorElementType etType = elementType.GetSignatureCorElementType();
if (etType == ELEMENT_TYPE_PTR || etType == ELEMENT_TYPE_FNPTR)
else
{
return;
// ByRefs and generic type variables are never allowed.
if (elementType.IsByRef() || elementType.IsGenericVariable())
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
}

// We shouldn't get here (it means we've encountered a new type of typehandle if we do).
_ASSERTE(!"Shouldn't get here, unknown type handle type");
COMPlusThrow(kNotSupportedException);
}

FCIMPL4(Object*, ArrayNative::CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pLowerBounds)
FCIMPL4(Object*, ArrayNative::CreateInstance, ReflectClassBaseObject* pElementTypeUNSAFE, INT32 rank, INT32* pLengths, INT32* pLowerBounds)
{
CONTRACTL {
FCALL_CHECK;
Expand All @@ -868,13 +855,14 @@ FCIMPL4(Object*, ArrayNative::CreateInstance, void* elementTypeHandle, INT32 ran
} CONTRACTL_END;

OBJECTREF pRet = NULL;
TypeHandle elementType = TypeHandle::FromPtr(elementTypeHandle);

_ASSERTE(!elementType.IsNull());
REFLECTCLASSBASEREF pElementType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pElementTypeUNSAFE);

// pLengths and pLowerBounds are pinned buffers. No need to protect them.
HELPER_METHOD_FRAME_BEGIN_RET_0();
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(pElementType);

TypeHandle elementType(pElementType->GetType());

CheckElementType(elementType);

CorElementType CorType = elementType.GetSignatureCorElementType();
Expand Down Expand Up @@ -913,34 +901,28 @@ FCIMPL4(Object*, ArrayNative::CreateInstance, void* elementTypeHandle, INT32 ran
// 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 overlow

DWORD boundsSize = 0;
INT32* bounds;
if (pLowerBounds != NULL) {
if (!ClrSafeInt<DWORD>::multiply(rank, 2, boundsSize))
COMPlusThrowOM();
DWORD dwAllocaSize = 0;
if (!ClrSafeInt<DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize))
COMPlusThrowOM();

bounds = (INT32*) _alloca(dwAllocaSize);
if (pLowerBounds != NULL)
{
boundsSize = 2 * rank;
bounds = (INT32*) _alloca(boundsSize * sizeof(INT32));

for (int i=0;i<rank;i++) {
bounds[2*i] = pLowerBounds[i];
bounds[2*i+1] = pLengths[i];
}
}
else {
else
{
boundsSize = rank;

DWORD dwAllocaSize = 0;
if (!ClrSafeInt<DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize))
COMPlusThrowOM();

bounds = (INT32*) _alloca(dwAllocaSize);
bounds = (INT32*) _alloca(boundsSize * sizeof(INT32));

// We need to create a private copy of pLengths to avoid holes caused
// by caller mutating the array
for (int i=0;i<rank;i++)
for (int i=0; i < rank; i++)
bounds[i] = pLengths[i];
}

Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/classlibnative/bcltype/arraynative.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ 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);

// This method will create a new array of type type, with zero lower
// bounds and rank.
static FCDECL4(Object*, CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pBounds);
static FCDECL4(Object*, CreateInstance, ReflectClassBaseObject* pElementTypeUNSAFE, INT32 rank, INT32* pLengths, INT32* pBounds);

// This method will return a TypedReference to the array element
static FCDECL2(Object*, GetValue, ArrayBase* refThisUNSAFE, INT_PTR flattenedIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,42 +143,16 @@ public static unsafe Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArray
{
// We just checked above that all lower bounds are zero. In that case, we should actually allocate
// a new SzArray instead.
RuntimeTypeHandle elementTypeHandle = new RuntimeTypeHandle(typeHandleForArrayType.ToEETypePtr().ArrayElementType);
int length = lengths[0];
if (length < 0)
throw new OverflowException(); // For compat: we need to throw OverflowException(): Array.CreateInstance throws ArgumentOutOfRangeException()
return Array.CreateInstance(Type.GetTypeFromHandle(elementTypeHandle), length);
Type elementType = Type.GetTypeFromHandle(new RuntimeTypeHandle(typeHandleForArrayType.ToEETypePtr().ArrayElementType));
return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToEETypePtr(), lengths[0]);
}

// Create a local copy of the lenghts that cannot be motified by the caller
int* pLengths = stackalloc int[lengths.Length];
// Create a local copy of the lengths that cannot be modified by the caller
int* pImmutableLengths = stackalloc int[lengths.Length];
for (int i = 0; i < lengths.Length; i++)
pLengths[i] = lengths[i];
pImmutableLengths[i] = lengths[i];

return Array.NewMultiDimArray(typeHandleForArrayType.ToEETypePtr(), pLengths, lengths.Length);
}

//
// Helper to create an array from a newobj instruction
//
public static unsafe Array NewObjArray(RuntimeTypeHandle typeHandleForArrayType, int[] arguments)
{
EETypePtr eeTypePtr = typeHandleForArrayType.ToEETypePtr();
Debug.Assert(eeTypePtr.IsArray);

fixed (int* pArguments = arguments)
{
return ArrayHelpers.NewObjArray((IntPtr)eeTypePtr.ToPointer(), arguments.Length, pArguments);
}
}

public static ref byte GetSzArrayElementAddress(Array array, int index)
{
if ((uint)index >= (uint)array.Length)
throw new IndexOutOfRangeException();

ref byte start = ref Unsafe.As<RawArrayData>(array).Data;
return ref Unsafe.Add(ref start, (IntPtr)(nint)((nuint)index * array.ElementSize));
return Array.NewMultiDimArray(typeHandleForArrayType.ToEETypePtr(), pImmutableLengths, lengths.Length);
}

public static IntPtr GetAllocateObjectHelperForType(RuntimeTypeHandle type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,8 @@ public static unsafe Array NewObjArray(IntPtr pEEType, int nDimensions, int* pDi
{
// Multidimensional array of rank 1 with 0 lower bounds gets actually allocated
// as an SzArray. SzArray is castable to MdArray rank 1.
int length = pDimensions[0];
if (length < 0)
{
// Compat: we need to throw OverflowException. Array.CreateInstance would throw ArgumentOutOfRange
throw new OverflowException();
}

RuntimeTypeHandle elementTypeHandle = new RuntimeTypeHandle(eeType.ArrayElementType);
return Array.CreateInstance(Type.GetTypeFromHandle(elementTypeHandle), length);
Type elementType = Type.GetTypeFromHandle(new RuntimeTypeHandle(eeType.ArrayElementType));
return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToEETypePtr(), pDimensions[0]);
}

return Array.NewMultiDimArray(eeType, pDimensions, rank);
Expand Down
Loading

0 comments on commit f977881

Please sign in to comment.