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 b84c9aec7d68f..c3cf411bc87cc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -633,98 +633,8 @@ private unsafe nint GetFlattenedIndex(ReadOnlySpan indices) return result; } - private unsafe void InternalSetValue(object? value, nint flattenedIndex) - { - MethodTable* pMethodTable = RuntimeHelpers.GetMethodTable(this); - - TypeHandle arrayElementTypeHandle = pMethodTable->GetArrayElementTypeHandle(); - - // Legacy behavior (this handles pointers and function pointers) - if (arrayElementTypeHandle.IsTypeDesc) - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.Arg_TypeNotSupported); - } - - Debug.Assert((nuint)flattenedIndex < NativeLength); - - ref byte arrayDataRef = ref MemoryMarshal.GetArrayDataReference(this); - - MethodTable* pElementMethodTable = arrayElementTypeHandle.AsMethodTable(); - - if (value == null) - { - // Null is the universal zero... - if (pElementMethodTable->IsValueType) - { - ref byte offsetDataRef = ref Unsafe.Add(ref arrayDataRef, flattenedIndex * pMethodTable->ComponentSize); - if (pElementMethodTable->ContainsGCPointers) - SpanHelpers.ClearWithReferences(ref Unsafe.As(ref offsetDataRef), pElementMethodTable->GetNumInstanceFieldBytes()); - else - SpanHelpers.ClearWithoutReferences(ref offsetDataRef, pElementMethodTable->GetNumInstanceFieldBytes()); - } - else - { - ref object? elementRef = ref Unsafe.As(ref arrayDataRef); - ref object? offsetElementRef = ref Unsafe.Add(ref elementRef, (nuint)flattenedIndex); - offsetElementRef = null; - } - } - else if (TypeHandle.AreSameType(arrayElementTypeHandle, TypeHandle.TypeHandleOf())) - { - // Everything is compatible with Object - ref object? elementRef = ref Unsafe.As(ref arrayDataRef); - ref object? offsetElementRef = ref Unsafe.Add(ref elementRef, (nuint)flattenedIndex); - offsetElementRef = value; - } - else if (!pElementMethodTable->IsValueType) - { - if (CastHelpers.IsInstanceOfAny(pElementMethodTable, value) == null) - throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); - - ref object? elementRef = ref Unsafe.As(ref arrayDataRef); - ref object? offsetElementRef = ref Unsafe.Add(ref elementRef, (nuint)flattenedIndex); - offsetElementRef = value; - } - else - { - // value class or primitive type - - ref byte offsetDataRef = ref Unsafe.Add(ref arrayDataRef, flattenedIndex * pMethodTable->ComponentSize); - if (CastHelpers.IsInstanceOfAny(pElementMethodTable, value) != null) - { - if (pElementMethodTable->IsNullable) - { - RuntimeHelpers.Unbox_Nullable(ref offsetDataRef, pElementMethodTable, value); - } - else if (pElementMethodTable->ContainsGCPointers) - { - Buffer.BulkMoveWithWriteBarrier(ref offsetDataRef, ref value.GetRawData(), pElementMethodTable->GetNumInstanceFieldBytes()); - } - else - { - SpanHelpers.Memmove(ref offsetDataRef, ref value.GetRawData(), pElementMethodTable->GetNumInstanceFieldBytes()); - } - } - else - { - // Allow enum -> primitive conversion, disallow primitive -> enum conversion - MethodTable* thSrc = RuntimeHelpers.GetMethodTable(value); - CorElementType srcType = thSrc->GetVerifierCorElementType(); - CorElementType targetType = pElementMethodTable->GetVerifierCorElementType(); - - if (!srcType.IsPrimitiveType() || !targetType.IsPrimitiveType() || !pElementMethodTable->IsTruePrimitive) - throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); - - // Get a properly widened type - if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType)) - throw new ArgumentException(SR.Arg_PrimWiden); - - PrimitiveWiden(ref value.GetRawData(), ref offsetDataRef, srcType, targetType); - } - } - - GC.KeepAlive(this); // Keep the method table alive - } + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void InternalSetValue(object? value, nint flattenedIndex); public int Length => checked((int)Unsafe.As(this).Length); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index 14c8a959cda5b..0c640dd1c2f0a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -29,7 +29,7 @@ internal static unsafe class CastHelpers // Unlike the IsInstanceOfInterface and IsInstanceOfClass functions, // this test must deal with all kinds of type tests [DebuggerHidden] - internal static object? IsInstanceOfAny(void* toTypeHnd, object? obj) + private static object? IsInstanceOfAny(void* toTypeHnd, object? obj) { if (obj != null) { diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 246c539827607..f44560b17a72d 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -195,3 +195,90 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3 Done: ; END_QCALL; } + +FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex) +{ + FCALL_CONTRACT; + + BASEARRAYREF refThis(refThisUNSAFE); + OBJECTREF obj(objUNSAFE); + + TypeHandle arrayElementType = refThis->GetArrayElementTypeHandle(); + + // Legacy behavior (this handles pointers and function pointers) + if (arrayElementType.IsTypeDesc()) + { + FCThrowResVoid(kNotSupportedException, W("NotSupported_Type")); + } + + _ASSERTE((SIZE_T)flattenedIndex < refThis->GetNumComponents()); + + MethodTable* pElementTypeMT = arrayElementType.GetMethodTable(); + PREFIX_ASSUME(NULL != pElementTypeMT); + + void* pData = refThis->GetDataPtr() + flattenedIndex * refThis->GetComponentSize(); + + if (obj == NULL) + { + // Null is the universal zero... + if (pElementTypeMT->IsValueType()) + InitValueClass(pData,pElementTypeMT); + else + ClearObjectReference((OBJECTREF*)pData); + } + else + if (arrayElementType == TypeHandle(g_pObjectClass)) + { + // Everything is compatible with Object + SetObjectReference((OBJECTREF*)pData,(OBJECTREF)obj); + } + else + if (!pElementTypeMT->IsValueType()) + { + if (ObjIsInstanceOfCached(OBJECTREFToObject(obj), arrayElementType) != TypeHandle::CanCast) + { + HELPER_METHOD_FRAME_BEGIN_2(refThis, obj); + + 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*)pData,obj); + } + else + { + // value class or primitive type + + if (!pElementTypeMT->UnBoxInto(pData, 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 = arrayElementType.GetSignatureCorElementType(); + + if (!InvokeUtil::IsPrimitiveType(srcType) || !InvokeUtil::IsPrimitiveType(targetType)) + COMPlusThrow(kInvalidCastException, W("InvalidCast_StoreArrayElement")); + + // 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(pData, ArgSlotEndiannessFixup(&value, cbSize), cbSize); + + HELPER_METHOD_FRAME_END(); + } + } +} +FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 5dd3570ce5cbb..0c03df1c2841c 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -13,7 +13,14 @@ #ifndef _ARRAYNATIVE_H_ #define _ARRAYNATIVE_H_ -#include "qcall.h" +#include "fcall.h" + +class ArrayNative +{ +public: + // This set of methods will set a value in an array + static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); +}; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 3ad62b22824ac..1172101b374a9 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -359,6 +359,10 @@ FCFuncStart(gCastHelpers) FCFuncElement("WriteBarrier", ::WriteBarrier_Helper) FCFuncEnd() +FCFuncStart(gArrayFuncs) + FCFuncElement("InternalSetValue", ArrayNative::SetValue) +FCFuncEnd() + FCFuncStart(gBufferFuncs) FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) FCFuncEnd() @@ -531,6 +535,7 @@ FCFuncEnd() // Note these have to remain sorted by name:namespace pair (Assert will wack you if you don't) // The sorting is case-sensitive +FCClassElement("Array", "System", gArrayFuncs) FCClassElement("AssemblyLoadContext", "System.Runtime.Loader", gAssemblyLoadContextFuncs) FCClassElement("Buffer", "System", gBufferFuncs) FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers)