Simplify non-generic ArrayEnumerator (#51351)
authorJan Kotas <jkotas@microsoft.com>
Wed, 21 Apr 2021 03:57:02 +0000 (20:57 -0700)
committerGitHub <noreply@github.com>
Wed, 21 Apr 2021 03:57:02 +0000 (20:57 -0700)
* Simplify non-generic ArrayEnumerator

* Implement GetFlattenedIndex for Mono

src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs
src/coreclr/classlibnative/bcltype/arraynative.cpp
src/coreclr/classlibnative/bcltype/arraynative.h
src/coreclr/vm/ecalllist.h
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Array.Enumerators.cs
src/libraries/System.Private.CoreLib/src/System/Array.cs
src/mono/System.Private.CoreLib/src/System/Array.Mono.cs
src/mono/mono/metadata/icall-def-netcore.h
src/mono/mono/metadata/icall.c

index e661ca1..633b154 100644 (file)
@@ -303,119 +303,40 @@ namespace System
             // GC.KeepAlive(array) not required. pMT kept alive via `ptr`
         }
 
-        // The various Get values...
-        public unsafe object? GetValue(params int[] indices)
+        private unsafe nint GetFlattenedIndex(ReadOnlySpan<int> indices)
         {
-            if (indices == null)
-                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
-            if (Rank != indices.Length)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
-
-            TypedReference elemref = default;
-            fixed (int* pIndices = &indices[0])
-                InternalGetReference(&elemref, indices.Length, pIndices);
-            return TypedReference.InternalToObject(&elemref);
-        }
-
-        public unsafe object? GetValue(int index)
-        {
-            if (Rank != 1)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need1DArray);
-
-            TypedReference elemref = default;
-            InternalGetReference(&elemref, 1, &index);
-            return TypedReference.InternalToObject(&elemref);
-        }
-
-        public unsafe object? GetValue(int index1, int index2)
-        {
-            if (Rank != 2)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need2DArray);
-
-            int* pIndices = stackalloc int[2];
-            pIndices[0] = index1;
-            pIndices[1] = index2;
-
-            TypedReference elemref = default;
-            InternalGetReference(&elemref, 2, pIndices);
-            return TypedReference.InternalToObject(&elemref);
-        }
-
-        public unsafe object? GetValue(int index1, int index2, int index3)
-        {
-            if (Rank != 3)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need3DArray);
-
-            int* pIndices = stackalloc int[3];
-            pIndices[0] = index1;
-            pIndices[1] = index2;
-            pIndices[2] = index3;
-
-            TypedReference elemref = default;
-            InternalGetReference(&elemref, 3, pIndices);
-            return TypedReference.InternalToObject(&elemref);
-        }
-
-        public unsafe void SetValue(object? value, int index)
-        {
-            if (Rank != 1)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need1DArray);
-
-            TypedReference elemref = default;
-            InternalGetReference(&elemref, 1, &index);
-            InternalSetValue(&elemref, value);
-        }
+            // Checked by the caller
+            Debug.Assert(indices.Length == Rank);
 
-        public unsafe void SetValue(object? value, int index1, int index2)
-        {
-            if (Rank != 2)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need2DArray);
-
-            int* pIndices = stackalloc int[2];
-            pIndices[0] = index1;
-            pIndices[1] = index2;
-
-            TypedReference elemref = default;
-            InternalGetReference(&elemref, 2, pIndices);
-            InternalSetValue(&elemref, value);
-        }
-
-        public unsafe void SetValue(object? value, int index1, int index2, int index3)
-        {
-            if (Rank != 3)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need3DArray);
-
-            int* pIndices = stackalloc int[3];
-            pIndices[0] = index1;
-            pIndices[1] = index2;
-            pIndices[2] = index3;
-
-            TypedReference elemref = default;
-            InternalGetReference(&elemref, 3, pIndices);
-            InternalSetValue(&elemref, value);
-        }
-
-        public unsafe void SetValue(object? value, params int[] indices)
-        {
-            if (indices == null)
-                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
-            if (Rank != indices.Length)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
-
-            TypedReference elemref = default;
-            fixed (int* pIndices = &indices[0])
-                InternalGetReference(&elemref, indices.Length, pIndices);
-            InternalSetValue(&elemref, value);
+            if (RuntimeHelpers.GetMethodTable(this)->IsMultiDimensionalArray)
+            {
+                ref int bounds = ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this);
+                nint flattenedIndex = 0;
+                for (int i = 0; i < indices.Length; i++)
+                {
+                    int index = indices[i] - Unsafe.Add(ref bounds, indices.Length  + i);
+                    int length = Unsafe.Add(ref bounds, i);
+                    if ((uint)index >= (uint)length)
+                        ThrowHelper.ThrowIndexOutOfRangeException();
+                    flattenedIndex = (length * flattenedIndex) + index;
+                }
+                Debug.Assert((nuint)flattenedIndex < (nuint)LongLength);
+                return flattenedIndex;
+            }
+            else
+            {
+                int index = indices[0];
+                if ((uint)index >= (uint)LongLength)
+                    ThrowHelper.ThrowIndexOutOfRangeException();
+                return index;
+            }
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        // reference to TypedReference is banned, so have to pass result as pointer
-        private extern unsafe void InternalGetReference(void* elemRef, int rank, int* pIndices);
+        internal extern unsafe object? InternalGetValue(nint flattenedIndex);
 
-        // Ideally, we would like to use TypedReference.SetValue instead. Unfortunately, TypedReference.SetValue
-        // always throws not-supported exception
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern unsafe void InternalSetValue(void* target, object? value);
+        private extern void InternalSetValue(object? value, nint flattenedIndex);
 
         public int Length => checked((int)Unsafe.As<RawArrayData>(this).Length);
 
index 50e652b..b0236e9 100644 (file)
@@ -954,54 +954,50 @@ Done: ;
 }
 FCIMPLEND
 
-
-FCIMPL4(void, ArrayNative::GetReference, ArrayBase* refThisUNSAFE, TypedByRef* elemRef, INT32 rank, INT32* pIndices)
+FCIMPL2(Object*, ArrayNative::GetValue, ArrayBase* refThisUNSAFE, INT_PTR flattenedIndex)
 {
     CONTRACTL {
         FCALL_CHECK;
-        PRECONDITION(rank >= 0);
     } CONTRACTL_END;
 
-    // FC_GC_POLL not necessary. We poll for GC in Array.Rank that's always called
-    // right before this function
-    FC_GC_POLL_NOT_NEEDED();
+    BASEARRAYREF refThis(refThisUNSAFE);
 
-    BASEARRAYREF    refThis  = (BASEARRAYREF) refThisUNSAFE;
+    TypeHandle arrayElementType = refThis->GetArrayElementTypeHandle();
 
-    _ASSERTE(rank == (INT32)refThis->GetRank());
+    // Legacy behavior
+    if (arrayElementType.IsTypeDesc())
+    {
+        CorElementType elemtype = arrayElementType.AsTypeDesc()->GetInternalCorElementType();
+        if (elemtype == ELEMENT_TYPE_PTR || elemtype == ELEMENT_TYPE_FNPTR)
+            FCThrowRes(kNotSupportedException, W("NotSupported_Type"));
+    }
 
-    SIZE_T Offset               = 0;
-    const INT32 *pBoundsPtr     = refThis->GetBoundsPtr();
+    _ASSERTE((SIZE_T)flattenedIndex < refThis->GetNumComponents());
+    void* pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize();
+    OBJECTREF Obj;
 
-    if (rank == 1)
+    MethodTable* pElementTypeMT = arrayElementType.GetMethodTable();
+    if (pElementTypeMT->IsValueType())
     {
-        Offset = pIndices[0] - refThis->GetLowerBoundsPtr()[0];
-
-        // Bounds check each index
-        // Casting to unsigned allows us to use one compare for [0..limit-1]
-        if (((UINT32) Offset) >= ((UINT32) pBoundsPtr[0]))
-            FCThrowVoid(kIndexOutOfRangeException);
+        HELPER_METHOD_FRAME_BEGIN_RET_0();
+        Obj = pElementTypeMT->Box(pData);
+        HELPER_METHOD_FRAME_END();
     }
     else
     {
-        // Avoid redundant computation in GetLowerBoundsPtr
-        const INT32 *pLowerBoundsPtr = pBoundsPtr + rank;
-        _ASSERTE(refThis->GetLowerBoundsPtr() == pLowerBoundsPtr);
-
-        SIZE_T Multiplier = 1;
+        Obj = ObjectToOBJECTREF(*((Object**)pData));
+    }
 
-        for (int i = rank; i >= 1; i--) {
-            INT32 curIndex = pIndices[i-1] - pLowerBoundsPtr[i-1];
+    return OBJECTREFToObject(Obj);
+}
+FCIMPLEND
 
-            // Bounds check each index
-            // Casting to unsigned allows us to use one compare for [0..limit-1]
-            if (((UINT32) curIndex) >= ((UINT32) pBoundsPtr[i-1]))
-                FCThrowVoid(kIndexOutOfRangeException);
+FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex)
+{
+    FCALL_CONTRACT;
 
-            Offset += curIndex * Multiplier;
-            Multiplier *= pBoundsPtr[i-1];
-        }
-    }
+    BASEARRAYREF refThis(refThisUNSAFE);
+    OBJECTREF obj(objUNSAFE);
 
     TypeHandle arrayElementType = refThis->GetArrayElementTypeHandle();
 
@@ -1012,72 +1008,60 @@ FCIMPL4(void, ArrayNative::GetReference, ArrayBase* refThisUNSAFE, TypedByRef* e
         if (elemtype == ELEMENT_TYPE_PTR || elemtype == ELEMENT_TYPE_FNPTR)
             FCThrowResVoid(kNotSupportedException, W("NotSupported_Type"));
     }
-#ifdef _DEBUG
-    CorElementType elemtype = arrayElementType.GetInternalCorElementType();
-    _ASSERTE(elemtype != ELEMENT_TYPE_PTR && elemtype != ELEMENT_TYPE_FNPTR);
-#endif
 
-    elemRef->data = refThis->GetDataPtr() + (Offset * refThis->GetComponentSize());
-    elemRef->type = arrayElementType;
-}
-FCIMPLEND
+    _ASSERTE((SIZE_T)flattenedIndex < refThis->GetNumComponents());
 
-FCIMPL2(void, ArrayNative::SetValue, TypedByRef * target, Object* objUNSAFE)
-{
-    FCALL_CONTRACT;
-
-    OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE);
+    MethodTable* pElementTypeMT = arrayElementType.GetMethodTable();
+    PREFIX_ASSUME(NULL != pElementTypeMT);
 
-    TypeHandle thTarget(target->type);
-
-    MethodTable* pTargetMT = thTarget.AsMethodTable();
-    PREFIX_ASSUME(NULL != pTargetMT);
+    void* pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize();
 
     if (obj == NULL)
     {
         // Null is the universal zero...
-        if (pTargetMT->IsValueType())
-            InitValueClass(target->data,pTargetMT);
+        if (pElementTypeMT->IsValueType())
+            InitValueClass(pData,pElementTypeMT);
         else
-            ClearObjectReference((OBJECTREF*)target->data);
+            ClearObjectReference((OBJECTREF*)pData);
     }
     else
-    if (thTarget == TypeHandle(g_pObjectClass))
+    if (arrayElementType == TypeHandle(g_pObjectClass))
     {
         // Everything is compatible with Object
-        SetObjectReference((OBJECTREF*)target->data,(OBJECTREF)obj);
+        SetObjectReference((OBJECTREF*)pData,(OBJECTREF)obj);
     }
     else
-    if (!pTargetMT->IsValueType())
+    if (!pElementTypeMT->IsValueType())
     {
-        if (ObjIsInstanceOfCached(OBJECTREFToObject(obj), thTarget) != TypeHandle::CanCast)
+        if (ObjIsInstanceOfCached(OBJECTREFToObject(obj), arrayElementType) != TypeHandle::CanCast)
         {
-            // target->data is protected by the caller
-            HELPER_METHOD_FRAME_BEGIN_1(obj);
+            HELPER_METHOD_FRAME_BEGIN_2(refThis, obj);
 
-            if (!ObjIsInstanceOf(OBJECTREFToObject(obj), thTarget))
+            if (!ObjIsInstanceOf(OBJECTREFToObject(obj), arrayElementType))
                 COMPlusThrow(kInvalidCastException,W("InvalidCast_StoreArrayElement"));
 
             HELPER_METHOD_FRAME_END();
+
+            // Refresh pData in case GC moved objects around
+            pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize();
         }
 
-        SetObjectReference((OBJECTREF*)target->data,obj);
+        SetObjectReference((OBJECTREF*)pData,obj);
     }
     else
     {
         // value class or primitive type
 
-        if (!pTargetMT->UnBoxInto(target->data, obj))
+        if (!pElementTypeMT->UnBoxInto(pData, obj))
         {
-            // target->data is protected by the caller
-            HELPER_METHOD_FRAME_BEGIN_1(obj);
+            HELPER_METHOD_FRAME_BEGIN_2(refThis, obj);
 
             ARG_SLOT value = 0;
 
             // Allow enum -> primitive conversion, disallow primitive -> enum conversion
             TypeHandle thSrc = obj->GetTypeHandle();
             CorElementType srcType = thSrc.GetVerifierCorElementType();
-            CorElementType targetType = thTarget.GetSignatureCorElementType();
+            CorElementType targetType = arrayElementType.GetSignatureCorElementType();
 
             if (!InvokeUtil::IsPrimitiveType(srcType) || !InvokeUtil::IsPrimitiveType(targetType))
                 COMPlusThrow(kInvalidCastException, W("InvalidCast_StoreArrayElement"));
@@ -1085,8 +1069,11 @@ FCIMPL2(void, ArrayNative::SetValue, TypedByRef * target, Object* objUNSAFE)
             // Get a properly widened type
             InvokeUtil::CreatePrimitiveValue(targetType,srcType,obj,&value);
 
+            // Refresh pData in case GC moved objects around
+            pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize();
+
             UINT cbSize = CorTypeInfo::Size(targetType);
-            memcpyNoGCRefs(target->data, ArgSlotEndianessFixup(&value, cbSize), cbSize);
+            memcpyNoGCRefs(pData, ArgSlotEndianessFixup(&value, cbSize), cbSize);
 
             HELPER_METHOD_FRAME_END();
         }
index c5be840..af926d8 100644 (file)
@@ -36,10 +36,10 @@ public:
     static FCDECL4(Object*, CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pBounds);
 
     // This method will return a TypedReference to the array element
-    static FCDECL4(void, GetReference, ArrayBase* refThisUNSAFE, TypedByRef* elemRef, INT32 rank, INT32* pIndices);
+    static FCDECL2(Object*, GetValue, ArrayBase* refThisUNSAFE, INT_PTR flattenedIndex);
 
     // This set of methods will set a value in an array
-    static FCDECL2(void, SetValue, TypedByRef* target, Object* objUNSAFE);
+    static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex);
 
     // This method will initialize an array from a TypeHandle
     // to a field.
index 432dc7b..8067ee6 100644 (file)
@@ -686,7 +686,7 @@ FCFuncStart(gArrayFuncs)
     FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy)
     FCFuncElement("CopySlow", ArrayNative::CopySlow)
     FCFuncElement("InternalCreate", ArrayNative::CreateInstance)
-    FCFuncElement("InternalGetReference", ArrayNative::GetReference)
+    FCFuncElement("InternalGetValue", ArrayNative::GetValue)
     FCFuncElement("InternalSetValue", ArrayNative::SetValue)
 FCFuncEnd()
 
index deb8c3f..a1623b6 100644 (file)
@@ -76,7 +76,7 @@
     <Compile Include="$(MSBuildThisFileDirectory)System\ArgumentOutOfRangeException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ArithmeticException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Array.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Array.Enumerators.cs" Condition="'$(TargetsCoreRT)' != 'true'" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Array.Enumerators.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ArraySegment.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ArrayTypeMismatchException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\AssemblyLoadEventArgs.cs" />
index 9d88631..da01cb5 100644 (file)
@@ -9,115 +9,13 @@ namespace System
 {
     internal sealed class ArrayEnumerator : IEnumerator, ICloneable
     {
-        private readonly Array array;
-        private int index;
-        private readonly int endIndex;
-        private readonly int startIndex;    // Save for Reset.
-        private readonly int[] _indices;    // The current position in a multidim array
-        private bool _complete;
-
-        internal ArrayEnumerator(Array array, int index, int count)
-        {
-            this.array = array;
-            this.index = index - 1;
-            startIndex = index;
-            endIndex = index + count;
-            _indices = new int[array.Rank];
-            int checkForZero = 1;  // Check for dimensions of size 0.
-            for (int i = 0; i < array.Rank; i++)
-            {
-                _indices[i] = array.GetLowerBound(i);
-                checkForZero *= array.GetLength(i);
-            }
-            // To make MoveNext simpler, decrement least significant index.
-            _indices[^1]--;
-            _complete = (checkForZero == 0);
-        }
-
-        private void IncArray()
-        {
-            // This method advances us to the next valid array index,
-            // handling all the multiple dimension & bounds correctly.
-            // Think of it like an odometer in your car - we start with
-            // the last digit, increment it, and check for rollover.  If
-            // it rolls over, we set all digits to the right and including
-            // the current to the appropriate lower bound.  Do these overflow
-            // checks for each dimension, and if the most significant digit
-            // has rolled over it's upper bound, we're done.
-            //
-            int rank = array.Rank;
-            _indices[rank - 1]++;
-            for (int dim = rank - 1; dim >= 0; dim--)
-            {
-                if (_indices[dim] > array.GetUpperBound(dim))
-                {
-                    if (dim == 0)
-                    {
-                        _complete = true;
-                        break;
-                    }
-                    for (int j = dim; j < rank; j++)
-                        _indices[j] = array.GetLowerBound(j);
-                    _indices[dim - 1]++;
-                }
-            }
-        }
-
-        public object Clone()
-        {
-            return MemberwiseClone();
-        }
-
-        public bool MoveNext()
-        {
-            if (_complete)
-            {
-                index = endIndex;
-                return false;
-            }
-            index++;
-            IncArray();
-            return !_complete;
-        }
-
-        public object? Current
-        {
-            get
-            {
-                if (index < startIndex) ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted();
-                if (_complete) ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
-                return array.GetValue(_indices);
-            }
-        }
-
-        public void Reset()
-        {
-            index = startIndex - 1;
-            int checkForZero = 1;
-            for (int i = 0; i < array.Rank; i++)
-            {
-                _indices[i] = array.GetLowerBound(i);
-                checkForZero *= array.GetLength(i);
-            }
-            _complete = (checkForZero == 0);
-            // To make MoveNext simpler, decrement least significant index.
-            _indices[^1]--;
-        }
-    }
-
-    internal sealed class SZArrayEnumerator : IEnumerator, ICloneable
-    {
         private readonly Array _array;
-        private int _index;
-        private readonly int _endIndex; // Cache Array.Length, since it's a little slow.
+        private nint _index;
 
-        internal SZArrayEnumerator(Array array)
+        internal ArrayEnumerator(Array array)
         {
-            Debug.Assert(array.Rank == 1 && array.GetLowerBound(0) == 0, "SZArrayEnumerator only works on single dimension arrays w/ a lower bound of zero.");
-
             _array = array;
             _index = -1;
-            _endIndex = array.Length;
         }
 
         public object Clone()
@@ -127,23 +25,36 @@ namespace System
 
         public bool MoveNext()
         {
-            if (_index < _endIndex)
+            nint index = _index + 1;
+            if ((nuint)index >= (nuint)_array.LongLength)
             {
-                _index++;
-                return _index < _endIndex;
+                _index = (nint)_array.LongLength;
+                return false;
             }
-            return false;
+            _index = index;
+            return true;
         }
 
         public object? Current
         {
             get
             {
-                if (_index < 0)
-                    ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted();
-                if (_index >= _endIndex)
-                    ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
-                return _array.GetValue(_index);
+                nint index = _index;
+                Array array = _array;
+
+                if ((nuint)index >= (nuint)array.LongLength)
+                {
+                    if (index < 0)
+                    {
+                        ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted();
+                    }
+                    else
+                    {
+                        ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
+                    }
+                }
+
+                return array.InternalGetValue(index);
             }
         }
 
index ea0c6a7..d1ac28f 100644 (file)
@@ -116,6 +116,75 @@ namespace System
             Copy(sourceArray, isourceIndex, destinationArray, idestinationIndex, ilength);
         }
 
+        // The various Get values...
+        public object? GetValue(params int[] indices)
+        {
+            if (indices == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
+            if (Rank != indices.Length)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
+
+            return InternalGetValue(GetFlattenedIndex(new ReadOnlySpan<int>(indices)));
+        }
+
+        public unsafe object? GetValue(int index)
+        {
+            if (Rank != 1)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need1DArray);
+
+            return InternalGetValue(GetFlattenedIndex(new ReadOnlySpan<int>(&index, 1)));
+        }
+
+        public object? GetValue(int index1, int index2)
+        {
+            if (Rank != 2)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need2DArray);
+
+            return InternalGetValue(GetFlattenedIndex(stackalloc int[] { index1, index2 }));
+        }
+
+        public object? GetValue(int index1, int index2, int index3)
+        {
+            if (Rank != 3)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need3DArray);
+
+            return InternalGetValue(GetFlattenedIndex(stackalloc int[] { index1, index2, index3 }));
+        }
+
+        public unsafe void SetValue(object? value, int index)
+        {
+            if (Rank != 1)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need1DArray);
+
+            InternalSetValue(value, GetFlattenedIndex(new ReadOnlySpan<int>(&index, 1)));
+        }
+
+        public void SetValue(object? value, int index1, int index2)
+        {
+            if (Rank != 2)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need2DArray);
+
+            InternalSetValue(value, GetFlattenedIndex(stackalloc int[] { index1, index2 }));
+        }
+
+        public void SetValue(object? value, int index1, int index2, int index3)
+        {
+            if (Rank != 3)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need3DArray);
+
+            InternalSetValue(value, GetFlattenedIndex(stackalloc int[] { index1, index2, index3 }));
+        }
+
+        public void SetValue(object? value, params int[] indices)
+        {
+            if (indices == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
+            if (Rank != indices.Length)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
+
+            InternalSetValue(value, GetFlattenedIndex(new ReadOnlySpan<int>(indices)));
+        }
+
         public object? GetValue(long index)
         {
             int iindex = (int)index;
@@ -2307,15 +2376,9 @@ namespace System
         private static Span<T> UnsafeArrayAsSpan<T>(Array array, int adjustedIndex, int length) =>
             new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawArrayData()), array.Length).Slice(adjustedIndex, length);
 
-#if !CORERT
         public IEnumerator GetEnumerator()
         {
-            int lowerBound = GetLowerBound(0);
-            if (Rank == 1 && lowerBound == 0)
-                return new SZArrayEnumerator(this);
-            else
-                return new ArrayEnumerator(this, lowerBound, Length);
+            return new ArrayEnumerator(this);
         }
-#endif // !CORERT
     }
 }
index bb4ba93..c56c972 100644 (file)
@@ -3,6 +3,7 @@
 
 using Internal.Runtime.CompilerServices;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Runtime.CompilerServices;
@@ -376,37 +377,38 @@ namespace System
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern unsafe void InternalCreate([NotNull] ref Array? result, IntPtr elementType, int rank, int* lengths, int* lowerBounds);
 
-        public object GetValue(int index)
+        private unsafe nint GetFlattenedIndex(ReadOnlySpan<int> indices)
         {
-            if (Rank != 1)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need1DArray);
+            // Checked by the caller
+            Debug.Assert(indices.Length == Rank);
 
-            int lb = GetLowerBound(0);
-            if (index < lb || index > GetUpperBound(0))
-                throw new IndexOutOfRangeException(SR.Argument_IndexOutOfArrayBounds);
-
-            if (GetType().GetElementType()!.IsPointer)
-                throw new NotSupportedException(SR.NotSupported_Type);
-
-            return GetValueImpl(index - lb);
+            nint flattenedIndex = 0;
+            for (int i = 0; i < indices.Length; i++)
+            {
+                int index = indices[i] - GetLowerBound(i);
+                int length = GetLength(i);
+                if ((uint)index >= (uint)length)
+                    ThrowHelper.ThrowIndexOutOfRangeException();
+                flattenedIndex = (length * flattenedIndex) + index;
+            }
+            Debug.Assert((nuint)flattenedIndex < (nuint)LongLength);
+            return flattenedIndex;
         }
 
-        public object GetValue(int index1, int index2)
+        internal object? InternalGetValue(nint index)
         {
-            if (Rank != 2)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need2DArray);
+            if (GetType().GetElementType()!.IsPointer)
+                throw new NotSupportedException(SR.NotSupported_Type);
 
-            int[] ind = { index1, index2 };
-            return GetValue(ind);
+            return GetValueImpl((int)index);
         }
 
-        public object GetValue(int index1, int index2, int index3)
+        internal void InternalSetValue(object? value, nint index)
         {
-            if (Rank != 3)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need3DArray);
+            if (GetType().GetElementType()!.IsPointer)
+                throw new NotSupportedException(SR.NotSupported_Type);
 
-            int[] ind = { index1, index2, index3 };
-            return GetValue(ind);
+            SetValueImpl(value, (int)index);
         }
 
         public void Initialize()
@@ -423,39 +425,6 @@ namespace System
             return EqualityComparer<T>.Default.LastIndexOf(array, value, startIndex, count);
         }
 
-        public void SetValue(object? value, int index)
-        {
-            if (Rank != 1)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need1DArray);
-
-            int lb = GetLowerBound(0);
-            if (index < lb || index > GetUpperBound(0))
-                throw new IndexOutOfRangeException(SR.Argument_IndexOutOfArrayBounds);
-
-            if (GetType().GetElementType()!.IsPointer)
-                throw new NotSupportedException(SR.NotSupported_Type);
-
-            SetValueImpl(value, index - lb);
-        }
-
-        public void SetValue(object? value, int index1, int index2)
-        {
-            if (Rank != 2)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need2DArray);
-
-            int[] ind = { index1, index2 };
-            SetValue(value, ind);
-        }
-
-        public void SetValue(object? value, int index1, int index2, int index3)
-        {
-            if (Rank != 3)
-                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need3DArray);
-
-            int[] ind = { index1, index2, index3 };
-            SetValue(value, ind);
-        }
-
         public int GetUpperBound(int dimension)
         {
             return GetLowerBound(dimension) + GetLength(dimension) - 1;
@@ -503,12 +472,6 @@ namespace System
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         public extern int GetLowerBound(int dimension);
 
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public extern object GetValue(params int[] indices);
-
-        [MethodImplAttribute(MethodImplOptions.InternalCall)]
-        public extern void SetValue(object? value, params int[] indices);
-
         // CAUTION! No bounds checking!
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         private static extern void GetGenericValue_icall<T>(ref Array self, int pos, out T value);
index d1b44dc..b7ef6d8 100644 (file)
@@ -40,12 +40,10 @@ HANDLES(ARRAY_4a, "GetCorElementTypeOfElementType", ves_icall_System_Array_GetCo
 NOHANDLES(ICALL(ARRAY_5, "GetGenericValue_icall", ves_icall_System_Array_GetGenericValue_icall))
 HANDLES(ARRAY_6, "GetLength",        ves_icall_System_Array_GetLength, gint32, 2, (MonoArray, gint32))
 HANDLES(ARRAY_7, "GetLowerBound",    ves_icall_System_Array_GetLowerBound, gint32, 2, (MonoArray, gint32))
-HANDLES(ARRAY_9, "GetValue",         ves_icall_System_Array_GetValue, MonoObject, 2, (MonoArray, MonoArray))
 HANDLES(ARRAY_10, "GetValueImpl",    ves_icall_System_Array_GetValueImpl, MonoObject, 2, (MonoArray, guint32))
 NOHANDLES(ICALL(ARRAY_10a, "InternalCreate", ves_icall_System_Array_InternalCreate))
 HANDLES(ARRAY_10b, "IsValueOfElementType", ves_icall_System_Array_IsValueOfElementType, gint32, 2, (MonoArray, MonoObject))
 NOHANDLES(ICALL(ARRAY_11, "SetGenericValue_icall", ves_icall_System_Array_SetGenericValue_icall))
-HANDLES(ARRAY_12, "SetValue",         ves_icall_System_Array_SetValue, void, 3, (MonoArray, MonoObject, MonoArray))
 HANDLES(ARRAY_13, "SetValueImpl",  ves_icall_System_Array_SetValueImpl, void, 3, (MonoArray, MonoObject, guint32))
 HANDLES(ARRAY_14, "SetValueRelaxedImpl",  ves_icall_System_Array_SetValueRelaxedImpl, void, 3, (MonoArray, MonoObject, guint32))
 
index 94f3632..75d4daf 100644 (file)
@@ -205,57 +205,10 @@ ves_icall_System_Array_GetValueImpl (MonoArrayHandle array, guint32 pos, MonoErr
        return result;
 }
 
-MonoObjectHandle
-ves_icall_System_Array_GetValue (MonoArrayHandle arr, MonoArrayHandle indices, MonoError *error)
-{
-       MONO_CHECK_ARG_NULL_HANDLE (indices, NULL_HANDLE);
-
-       MonoClass * const indices_class = mono_handle_class (indices);
-       MonoClass * const array_class = mono_handle_class (arr);
-
-       g_assert (m_class_get_rank (indices_class) == 1);
-
-       if (MONO_HANDLE_GETVAL (indices, bounds) || MONO_HANDLE_GETVAL (indices, max_length) != m_class_get_rank (array_class)) {
-               mono_error_set_argument (error, NULL, NULL);
-               return NULL_HANDLE;
-       }
-
-       gint32 index = 0;
-
-       if (!MONO_HANDLE_GETVAL (arr, bounds)) {
-               MONO_HANDLE_ARRAY_GETVAL (index, indices, gint32, 0);
-               if (index < 0 || index >= MONO_HANDLE_GETVAL (arr, max_length)) {
-                       mono_error_set_index_out_of_range (error);
-                       return NULL_HANDLE;
-               }
-
-               return ves_icall_System_Array_GetValueImpl (arr, index, error);
-       }
-       
-       for (gint32 i = 0; i < m_class_get_rank (array_class); i++) {
-               MONO_HANDLE_ARRAY_GETVAL (index, indices, gint32, i);
-               if ((index < MONO_HANDLE_GETVAL (arr, bounds [i].lower_bound)) ||
-                   (index >= (mono_array_lower_bound_t)MONO_HANDLE_GETVAL (arr, bounds [i].length) + MONO_HANDLE_GETVAL (arr, bounds [i].lower_bound))) {
-                       mono_error_set_index_out_of_range (error);
-                       return NULL_HANDLE;
-               }
-       }
-
-       MONO_HANDLE_ARRAY_GETVAL (index, indices, gint32, 0);
-       gint32 pos = index - MONO_HANDLE_GETVAL (arr, bounds [0].lower_bound);
-       for (gint32 i = 1; i < m_class_get_rank (array_class); i++) {
-               MONO_HANDLE_ARRAY_GETVAL (index, indices, gint32, i);
-               pos = pos * MONO_HANDLE_GETVAL (arr, bounds [i].length) + index -
-                       MONO_HANDLE_GETVAL (arr, bounds [i].lower_bound);
-       }
-
-       return ves_icall_System_Array_GetValueImpl (arr, pos, error);
-}
-
 void
 ves_icall_System_Array_SetValueImpl (MonoArrayHandle arr, MonoObjectHandle value, guint32 pos, MonoError *error)
 {
-       array_set_value_impl (arr, value, pos, FALSE, TRUE, error);
+       array_set_value_impl (arr, value, pos, TRUE, TRUE, error);
 }
 
 static inline void
@@ -727,67 +680,6 @@ leave:
 }
 
 void
-ves_icall_System_Array_SetValue (MonoArrayHandle arr, MonoObjectHandle value,
-                                MonoArrayHandle idxs, MonoError *error)
-{
-       icallarray_print ("%s\n", __func__);
-
-       MonoArrayBounds dim;
-       MonoClass *ac, *ic;
-       gint32 idx;
-       gint32 i, pos;
-
-       error_init (error);
-
-       if (MONO_HANDLE_IS_NULL (idxs)) {
-               mono_error_set_argument_null (error, "indices", "");
-               return;
-       }
-
-       ic = mono_handle_class (idxs);
-       ac = mono_handle_class (arr);
-
-       g_assert (m_class_get_rank (ic) == 1);
-       if (mono_handle_array_has_bounds (idxs) || MONO_HANDLE_GETVAL (idxs, max_length) != m_class_get_rank (ac)) {
-               mono_error_set_argument (error, NULL, "");
-               return;
-       }
-
-       if (!mono_handle_array_has_bounds (arr)) {
-               MONO_HANDLE_ARRAY_GETVAL (idx, idxs, gint32, 0);
-               if (idx < 0 || idx >= MONO_HANDLE_GETVAL (arr, max_length)) {
-                       mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ());
-                       return;
-               }
-
-               array_set_value_impl (arr, value, idx, TRUE, TRUE, error);
-               return;
-       }
-       
-       gint32 ac_rank = m_class_get_rank (ac);
-       for (i = 0; i < ac_rank; i++) {
-               mono_handle_array_get_bounds_dim (arr, i, &dim);
-               MONO_HANDLE_ARRAY_GETVAL (idx, idxs, gint32, i);
-               if ((idx < dim.lower_bound) ||
-                   (idx >= (mono_array_lower_bound_t)dim.length + dim.lower_bound)) {
-                       mono_error_set_exception_instance (error, mono_get_exception_index_out_of_range ());
-                       return;
-               }
-       }
-
-       MONO_HANDLE_ARRAY_GETVAL  (idx, idxs, gint32, 0);
-       mono_handle_array_get_bounds_dim (arr, 0, &dim);
-       pos = idx - dim.lower_bound;
-       for (i = 1; i < ac_rank; i++) {
-               mono_handle_array_get_bounds_dim (arr, i, &dim);
-               MONO_HANDLE_ARRAY_GETVAL (idx, idxs, gint32, i);
-               pos = pos * dim.length + idx - dim.lower_bound;
-       }
-
-       array_set_value_impl (arr, value, pos, TRUE, TRUE, error);
-}
-
-void
 ves_icall_System_Array_InternalCreate (MonoArray *volatile* result, MonoType* type, gint32 rank, gint32* pLengths, gint32* pLowerBounds)
 {
        ERROR_DECL (error);