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 7e4184cee9f1c6..3d33da1beb928f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -381,8 +381,52 @@ private unsafe bool IsValueOfElementType(object value) // if this is an array of value classes and that value class has a default constructor // then this calls this default constructor on every element in the value class array. // otherwise this is a no-op. Generally this method is called automatically by the compiler - [MethodImpl(MethodImplOptions.InternalCall)] - public extern void Initialize(); + public unsafe void Initialize() + { + MethodTable* pArrayMT = RuntimeHelpers.GetMethodTable(this); + TypeHandle thElem = pArrayMT->GetArrayElementTypeHandle(); + if (thElem.IsTypeDesc) + { + return; + } + + MethodTable* pElemMT = thElem.AsMethodTable(); + if (!pElemMT->HasDefaultConstructor || !pElemMT->IsValueType) + { + return; + } + + RuntimeType arrayType = (RuntimeType)GetType(); + + if (arrayType.GenericCache is not ArrayInitializeCache cache) + { + cache = new ArrayInitializeCache(arrayType); + arrayType.GenericCache = cache; + } + + delegate* constructorFtn = cache.ConstructorEntrypoint; + ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(this); + nuint elementSize = pArrayMT->ComponentSize; + + for (int i = 0; i < Length; i++) + { + constructorFtn(ref arrayRef); + arrayRef = ref Unsafe.Add(ref arrayRef, elementSize); + } + } + + private sealed unsafe partial class ArrayInitializeCache + { + internal readonly delegate* ConstructorEntrypoint; + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_GetElementConstructorEntrypoint")] + private static partial delegate* GetElementConstructorEntrypoint(QCallTypeHandle arrayType); + + public ArrayInitializeCache(RuntimeType arrayType) + { + ConstructorEntrypoint = GetElementConstructorEntrypoint(new QCallTypeHandle(ref arrayType)); + } + } } #pragma warning disable CA1822 // Mark members as static diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 31fc4aa08dd400..9fb736b48f9b0b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -489,6 +489,9 @@ internal unsafe struct MethodTable [FieldOffset(InterfaceMapOffset)] public MethodTable** InterfaceMap; + // WFLAGS_LOW_ENUM + private const uint enum_flag_HasDefaultCtor = 0x00000200; + // WFLAGS_HIGH_ENUM private const uint enum_flag_ContainsPointers = 0x01000000; private const uint enum_flag_HasComponentSize = 0x80000000; @@ -560,6 +563,14 @@ public bool HasTypeEquivalence } } + public bool HasDefaultConstructor + { + get + { + return ((HasComponentSize ? 0 : Flags) & enum_flag_HasDefaultCtor) != 0; + } + } + public bool IsMultiDimensionalArray { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index ce0ea564bbb210..80298fd13650c1 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -28,116 +28,24 @@ FCIMPL1(INT32, ArrayNative::GetCorElementTypeOfElementType, ArrayBase* arrayUNSA } FCIMPLEND -// array is GC protected by caller -void ArrayInitializeWorker(ARRAYBASEREF * arrayRef, - MethodTable* pArrayMT, - MethodTable* pElemMT) +extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd) { - STATIC_CONTRACT_MODE_COOPERATIVE; - - // Ensure that the array element type is fully loaded before executing its code - pElemMT->EnsureInstanceActive(); - - //can not use contract here because of SEH - _ASSERTE(IsProtectedByGCFrame (arrayRef)); - - SIZE_T offset = ArrayBase::GetDataPtrOffset(pArrayMT); - SIZE_T size = pArrayMT->GetComponentSize(); - SIZE_T cElements = (*arrayRef)->GetNumComponents(); - - MethodTable * pCanonMT = pElemMT->GetCanonicalMethodTable(); - WORD slot = pCanonMT->GetDefaultConstructorSlot(); - - PCODE ctorFtn = pCanonMT->GetSlot(slot); - -#if defined(TARGET_X86) && !defined(TARGET_UNIX) - BEGIN_CALL_TO_MANAGED(); - - - for (SIZE_T i = 0; i < cElements; i++) - { - // Since GetSlot() is not idempotent and may have returned - // a non-optimal entry-point the first time round. - if (i == 1) - { - ctorFtn = pCanonMT->GetSlot(slot); - } - - BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset); - -#ifdef _DEBUG - __asm { - mov ECX, thisPtr - mov EDX, pElemMT // Instantiation argument if the type is generic - call [ctorFtn] - nop // Mark the fact that we can call managed code - } -#else // _DEBUG - typedef void (__fastcall * CtorFtnType)(BYTE*, BYTE*); - (*(CtorFtnType)ctorFtn)(thisPtr, (BYTE*)pElemMT); -#endif // _DEBUG - - offset += size; - } - - END_CALL_TO_MANAGED(); -#else // TARGET_X86 && !TARGET_UNIX - // - // This is quite a bit slower, but it is portable. - // - - for (SIZE_T i =0; i < cElements; i++) - { - // Since GetSlot() is not idempotent and may have returned - // a non-optimal entry-point the first time round. - if (i == 1) - { - ctorFtn = pCanonMT->GetSlot(slot); - } - - BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset); - - PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(ctorFtn); - DECLARE_ARGHOLDER_ARRAY(args, 2); - args[ARGNUM_0] = PTR_TO_ARGHOLDER(thisPtr); - args[ARGNUM_1] = PTR_TO_ARGHOLDER(pElemMT); // Instantiation argument if the type is generic - CALL_MANAGED_METHOD_NORET(args); - - offset += size; - } -#endif // !TARGET_X86 || TARGET_UNIX -} - - -FCIMPL1(void, ArrayNative::Initialize, ArrayBase* array) -{ - FCALL_CONTRACT; - - if (array == NULL) - { - FCThrowVoid(kNullReferenceException); - } + QCALL_CONTRACT; + PCODE ctorEntrypoint = NULL; - MethodTable* pArrayMT = array->GetMethodTable(); + BEGIN_QCALL; - TypeHandle thElem = pArrayMT->GetArrayElementTypeHandle(); - if (thElem.IsTypeDesc()) - return; + TypeHandle th = pArrayTypeHnd.AsTypeHandle(); + MethodTable* pElemMT = th.GetArrayElementTypeHandle().AsMethodTable(); + ctorEntrypoint = pElemMT->GetDefaultConstructor()->GetMultiCallableAddrOfCode(); - MethodTable * pElemMT = thElem.AsMethodTable(); - if (!pElemMT->HasDefaultConstructor() || !pElemMT->IsValueType()) - return; - - ARRAYBASEREF arrayRef (array); - HELPER_METHOD_FRAME_BEGIN_1(arrayRef); + pElemMT->EnsureInstanceActive(); - ArrayInitializeWorker(&arrayRef, pArrayMT, pElemMT); + END_QCALL; - HELPER_METHOD_FRAME_END(); + return ctorEntrypoint; } -FCIMPLEND - // Returns whether you can directly copy an array of srcType into destType. FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index dcaa2ada39931a..84218d698571fe 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -27,8 +27,6 @@ class ArrayNative public: static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); - static FCDECL1(void, Initialize, ArrayBase* pArray); - static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); static FCDECL5(void, CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength); @@ -68,4 +66,6 @@ class ArrayNative }; +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 28b652a6b1e868..9a6a1c11ac8c59 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 @@ -107,12 +107,26 @@ private static void ValidateElementType(Type elementType) throw new NotSupportedException(SR.NotSupported_OpenType); } - public void Initialize() + public unsafe void Initialize() { - // This api is a nop unless the array element type is a value type with an explicit nullary constructor. - // Such a type could not be expressed in C# up until recently. - // TODO: Implement this - return; + EETypePtr pElementEEType = ElementEEType; + if (!pElementEEType.IsValueType) + return; + + IntPtr constructorEntryPoint = RuntimeAugments.TypeLoaderCallbacks.TryGetDefaultConstructorForType(new RuntimeTypeHandle(pElementEEType)); + if (constructorEntryPoint == IntPtr.Zero) + return; + + var constructorFtn = (delegate*)RuntimeAugments.TypeLoaderCallbacks.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(constructorEntryPoint, new RuntimeTypeHandle(pElementEEType)); + + ref byte arrayRef = ref MemoryMarshal.GetArrayDataReference(this); + nuint elementSize = ElementSize; + + for (int i = 0; i < Length; i++) + { + constructorFtn(ref arrayRef); + arrayRef = ref Unsafe.Add(ref arrayRef, elementSize); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index e000939d2c5a7c..025a6eb43a69e6 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -424,7 +424,6 @@ FCFuncEnd() FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) - FCFuncElement("Initialize", ArrayNative::Initialize) FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) FCFuncElement("CopySlow", ArrayNative::CopySlow) FCFuncElement("InternalCreate", ArrayNative::CreateInstance) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 9a7518fe1b97ac..c75b1889226e82 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -158,6 +158,7 @@ static const Entry s_QCall[] = DllImportEntry(TypeName_GetTypeArguments) DllImportEntry(TypeName_GetModifiers) DllImportEntry(TypeName_GetAssemblyName) + DllImportEntry(Array_GetElementConstructorEntrypoint) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) DllImportEntry(AssemblyNative_GetLocation) diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs index a317976281ed20..f2e2f67ac134db 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs @@ -383,6 +383,13 @@ private static void TestTypeCommonInvariants(this Type type) const BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy; foreach (MemberInfo mem in type.GetMember("*", MemberTypes.All, bf)) { + // Workaround: Do not try to test Array.Initialize, since one of its locals is a function pointer. + // Delete this workaround when https://github.com/dotnet/runtime/issues/69273 is addressed. + if (mem.DeclaringType == mem.DeclaringType.Assembly.GetType("System.Array") && mem.Name == "Initialize") + { + continue; + } + string s = mem.ToString(); Assert.Equal(type, mem.ReflectedType); Type declaringType = mem.DeclaringType; diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index 38abdebac61cc7..8e64f22c86911d 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -1614,7 +1614,7 @@ public static IEnumerable Copy_UnreliableConversion_CantPerform_TestDa yield return new object[] { new object[] { "1" }, new int[1] }; IEquatable[] interfaceArray1 = new IEquatable[10] { 0, 0, 0, 0, new NotInt32(), 0, 0, 0, 0, 0 }; - yield return new object[] { interfaceArray1, new int[10]}; + yield return new object[] { interfaceArray1, new int[10] }; IEquatable[] interfaceArray2 = new IEquatable[10] { 0, 0, 0, 0, new NotInt32(), 0, 0, 0, 0, 0 }; yield return new object[] { interfaceArray2, new int[10] }; @@ -3178,7 +3178,7 @@ public static IEnumerable Reverse_TestData() // Char yield return new object[] { new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7' }, 0, 33, new char[] { '7', '6', '5', '4', '3', '2', '1', 'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a' } }; - yield return new object[] { new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q' }, 0, 17, new char[] { 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a' } }; + yield return new object[] { new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q' }, 0, 17, new char[] { 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a' } }; yield return new object[] { new char[] { '1', '2', '3', '4', '5' }, 2, 3, new char[] { '1', '2', '5', '4', '3' } }; yield return new object[] { new char[] { '1', '2', '3', '4', '5' }, 0, 0, new char[] { '1', '2', '3', '4', '5' } }; yield return new object[] { new char[] { '1', '2', '3', '4', '5' }, 5, 0, new char[] { '1', '2', '3', '4', '5' } }; @@ -3225,8 +3225,8 @@ public static IEnumerable Reverse_TestData() var enumArray = new Int32Enum[] { Int32Enum.Case1, Int32Enum.Case2, Int32Enum.Case3, Int32Enum.Case1 }; yield return new object[] { enumArray, 0, 4, new Int32Enum[] { Int32Enum.Case1, Int32Enum.Case3, Int32Enum.Case2, Int32Enum.Case1 } }; yield return new object[] { enumArray, 2, 2, new Int32Enum[] { Int32Enum.Case1, Int32Enum.Case2, Int32Enum.Case1, Int32Enum.Case3 } }; - yield return new object[] { enumArray, 0, 0, enumArray}; - yield return new object[] { enumArray, 4, 0, enumArray}; + yield return new object[] { enumArray, 0, 0, enumArray }; + yield return new object[] { enumArray, 4, 0, enumArray }; // ValueType array ComparableValueType[] valueTypeArray = new ComparableValueType[] { new ComparableValueType(0), new ComparableValueType(1) }; @@ -3331,18 +3331,18 @@ public static IEnumerable Sort_Array_TestData() expectedPartial.SetValue(2, 3); expectedPartial.SetValue(4, 4); - yield return new object[] { actual, 1, 4, null, expectedFull }; - yield return new object[] { actual, 1, 4, new IntegerComparer(), expectedFull }; - yield return new object[] { actual, 2, 2, null, expectedPartial }; - yield return new object[] { actual, 2, 2, new IntegerComparer(), expectedPartial }; - yield return new object[] { actual, 1, 0, null, actual }; - yield return new object[] { actual, 1, 0, new IntegerComparer(), actual }; + yield return new object[] { actual, 1, 4, null, expectedFull }; + yield return new object[] { actual, 1, 4, new IntegerComparer(), expectedFull }; + yield return new object[] { actual, 2, 2, null, expectedPartial }; + yield return new object[] { actual, 2, 2, new IntegerComparer(), expectedPartial }; + yield return new object[] { actual, 1, 0, null, actual }; + yield return new object[] { actual, 1, 0, new IntegerComparer(), actual }; } } public static IEnumerable Sort_SZArray_TestData() { - // Int + // Int yield return new object[] { new int[0], 0, 0, new IntegerComparer(), new int[0] }; yield return new object[] { new int[] { 5 }, 0, 1, new IntegerComparer(), new int[] { 5 } }; yield return new object[] { new int[] { 5, 2 }, 0, 2, new IntegerComparer(), new int[] { 2, 5 } }; @@ -3359,7 +3359,7 @@ public static IEnumerable Sort_SZArray_TestData() yield return new object[] { new int[] { 4, 3, 2 }, 0, 3, new IntegerComparer(), new int[] { 2, 3, 4 } }; yield return new object[] { new int[] { 4, 3, 2 }, 0, 3, null, new int[] { 2, 3, 4 } }; - // String + // String yield return new object[] { new string[0], 0, 0, null, new string[0] }; yield return new object[] { new string[0], 0, 0, new StringComparer(), new string[0] }; yield return new object[] { new string[] { "5" }, 0, 1, null, new string[] { "5" } }; @@ -3367,9 +3367,9 @@ public static IEnumerable Sort_SZArray_TestData() yield return new object[] { new string[] { "5", "2" }, 0, 2, null, new string[] { "2", "5" } }; yield return new object[] { new string[] { "5", "2" }, 0, 2, new StringComparer(), new string[] { "2", "5" } }; yield return new object[] { new string[] { "5", "2", "3" }, 0, 3, null, new string[] { "2", "3", "5" } }; - yield return new object[] { new string[] { "5", "2", "3"}, 0, 3, new StringComparer(), new string[] { "2", "3", "5" } }; - yield return new object[] { new string[] { "5", "2", null }, 0, 3, null, new string[] {null, "2", "5" } }; - yield return new object[] { new string[] { "5", "2", null}, 0, 3, new StringComparer(), new string[] { null, "2", "5" } }; + yield return new object[] { new string[] { "5", "2", "3" }, 0, 3, new StringComparer(), new string[] { "2", "3", "5" } }; + yield return new object[] { new string[] { "5", "2", null }, 0, 3, null, new string[] { null, "2", "5" } }; + yield return new object[] { new string[] { "5", "2", null }, 0, 3, new StringComparer(), new string[] { null, "2", "5" } }; yield return new object[] { new string[] { "5", "2", "9", "8", "4", "3", "2", "4", "6" }, 0, 9, new StringComparer(), new string[] { "2", "2", "3", "4", "4", "5", "6", "8", "9" } }; yield return new object[] { new string[] { "5", null, "2", "9", "8", "4", "3", "2", "4", "6" }, 0, 10, new StringComparer(), new string[] { null, "2", "2", "3", "4", "4", "5", "6", "8", "9" } }; yield return new object[] { new string[] { "5", null, "2", "9", "8", "4", "3", "2", "4", "6" }, 3, 4, new StringComparer(), new string[] { "5", null, "2", "3", "4", "8", "9", "2", "4", "6" } }; @@ -4446,6 +4446,18 @@ public static void MaxSizes() Assert.Equal(0x7FFFFFC7, Array.MaxLength); } + [Fact] + public static void Array_Initialize() + { + var array = new StructWithDefaultConstructor[10, 10]; + array.Initialize(); + Assert.All(array.OfType(), a => Assert.True(a.Constructed)); + + var array2 = new NonGenericClass1[10]; + array2.Initialize(); + Assert.All(array2, Assert.Null); + } + private static void VerifyArray(Array array, Type elementType, int[] lengths, int[] lowerBounds, object repeatedValue) { VerifyArray(array, elementType, lengths, lowerBounds); @@ -4590,6 +4602,16 @@ private struct NonGenericStruct public int z; } + private struct StructWithDefaultConstructor + { + public StructWithDefaultConstructor() + { + Constructed = true; + } + + public bool Constructed; + } + private class IntegerComparer : IComparer, IComparer, IEqualityComparer { public int Compare(object x, object y) => Compare((int)x, (int)y); 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 56ebc4c353b09f..efb6229b01422d 100644 --- a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs @@ -332,6 +332,8 @@ internal void InternalSetValue(object? value, nint index) public void Initialize() { + object arr = this; + InitializeInternal(ObjectHandleOnStack.Create(ref arr)); } private static int IndexOfImpl(T[] array, T value, int startIndex, int count) @@ -405,6 +407,9 @@ private void SetGenericValueImpl(int pos, ref T value) [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern void SetValueImpl(object? value, int pos); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void InitializeInternal(ObjectHandleOnStack arr); + // CAUTION! No bounds checking! [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern void SetValueRelaxedImpl(object? value, int pos); diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 757d5a345603f6..71670c578440af 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -134,6 +134,7 @@ NOHANDLES(ICALL(ARRAY_5, "GetGenericValue_icall", ves_icall_System_Array_GetGene 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_10, "GetValueImpl", ves_icall_System_Array_GetValueImpl, MonoObject, 2, (MonoArray, guint32)) +HANDLES(ARRAY_10_a, "InitializeInternal", ves_icall_System_Array_InitializeInternal, void, 1, (MonoObjectHandleOnStack)) 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)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 44839bc70a2676..c1b88656d40a2f 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -233,6 +233,31 @@ ves_icall_System_Array_SetValueRelaxedImpl (MonoArrayHandle arr, MonoObjectHandl array_set_value_impl (arr, value, pos, FALSE, FALSE, error); } +void +ves_icall_System_Array_InitializeInternal (MonoObjectHandleOnStack arr_handle, MonoError *error) +{ + MonoArray *arr = *(MonoArray**)arr_handle; + MonoClass * const array_class = mono_object_class (arr); + MonoClass * const element_class = m_class_get_element_class (array_class); + if (!m_class_is_valuetype (element_class)) { + return; + } + + + MonoMethod * const method = mono_class_get_method_from_name_checked (element_class, ".ctor", 0, 0, error); + if (!method) { + return; + } + + gsize element_size = mono_array_element_size (array_class); + + for (guint32 i = 0; i < arr->max_length; i++) { + gpointer element_address = mono_array_addr_with_size_fast (arr, element_size, (gsize)i); + mono_runtime_invoke_checked (method, element_address, NULL, error); + return_if_nok (error); + } +} + // Copied from CoreCLR: https://github.com/dotnet/runtime/blob/402aa8584ed18792d6bc6ed1869f7c31b38f8139/src/coreclr/vm/invokeutil.cpp#L31 #define PT_Primitive 0x01000000