From 69202c0117da87918d7674eed69603545bc14be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 9 Sep 2024 08:41:53 +0200 Subject: [PATCH] Fix reflection-calling `Set` method on arrays The test added in #106787 found an issue in the implementation of reflection calls to array `Set` methods. We used to throw the wrong exception type. There were probably other corner case bugs (like what exception is thrown when both element type is wrong and index is out of range and when/how value coercion should happen). This should fix that. --- .../Runtime/Augments/RuntimeAugments.cs | 26 +++++++++++++++++++ .../src/System/Array.NativeAot.cs | 2 +- .../Runtime/TypeInfos/RuntimeArrayTypeInfo.cs | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 7db11a8204177..4b0967e372bbc 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -120,6 +120,32 @@ public static unsafe Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArray return Array.NewMultiDimArray(typeHandleForArrayType.ToMethodTable(), pImmutableLengths, lengths.Length); } + public static unsafe void SetArrayValue(Array array, int[] indices, object value) + { + MethodTable* elementMT = array.ElementMethodTable; + + if (elementMT->IsPointer || elementMT->IsFunctionPointer) + { + Debug.Assert(value.GetMethodTable()->ValueTypeSize == IntPtr.Size); + elementMT = value.GetMethodTable(); + } + + if (elementMT->IsValueType) + { + Debug.Assert(value.GetMethodTable()->IsValueType && elementMT->ValueTypeSize == value.GetMethodTable()->ValueTypeSize); + nint flattenedIndex = array.GetFlattenedIndex(indices); + ref byte element = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(array), (nuint)flattenedIndex * array.ElementSize); + RuntimeImports.RhUnbox(value, ref element, elementMT); + } + else + { + RuntimeImports.RhCheckArrayStore(array, value); + nint flattenedIndex = array.GetFlattenedIndex(indices); + ref object element = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(array)), flattenedIndex); + element = value; + } + } + public static IntPtr GetAllocateObjectHelperForType(RuntimeTypeHandle type) { return RuntimeImports.RhGetRuntimeHelperForType(type.ToMethodTable(), RuntimeHelperKind.AllocateObject); 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 58378b92a9c5f..faa4d51e93532 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 @@ -727,7 +727,7 @@ private unsafe nint GetFlattenedIndex(int rawIndex) return rawIndex; } - private unsafe nint GetFlattenedIndex(ReadOnlySpan indices) + internal unsafe nint GetFlattenedIndex(ReadOnlySpan indices) { // Checked by the caller Debug.Assert(indices.Length == Rank); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs index ab9fd61a3587e..1f2f374b2436e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs @@ -213,7 +213,7 @@ internal sealed override IEnumerable SyntheticMethods for (int i = 0; i < rank; i++) indices[i] = (int)(args[i]); object value = args[rank]; - array.SetValue(value, indices); + RuntimeAugments.SetArrayValue(array, indices, value); return null; } );