From 566b15e5af8835a7e0f75017d03dcf4957b5b2c6 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 01:26:45 +0800 Subject: [PATCH 01/39] Implement IsSimpleCopy and CanAssignArrayType in managed code --- .../src/System/Array.CoreCLR.cs | 116 ++++++++++++++++-- .../Runtime/CompilerServices/CastHelpers.cs | 14 +++ .../RuntimeHelpers.CoreCLR.cs | 39 ++++++ .../CompilerServices/RuntimeHelpers.cs | 25 ++++ 4 files changed, 187 insertions(+), 7 deletions(-) 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 74e07398481681..50989229ef8c3e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -89,8 +89,58 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); + private static unsafe bool IsSimpleCopy(Array sourceArray, Array destinationArray) + { + TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions + return true; + + if (srcTH.IsValueType) + { + // Value class boxing + if (!destTH.IsValueType) + return false; + + CorElementType srcElType = sourceArray.GetCorElementTypeOfElementType(); + CorElementType destElType = destinationArray.GetCorElementTypeOfElementType(); + + // Copying primitives from one type to another + if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) + { + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + return true; + } + } + else + { + // Value class unboxing + if (destTH.IsValueType) + return false; + } + + return srcTH.CanCastTo(destTH); + } + + private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) + { + Debug.Assert(elementType.IsPrimitiveType()); + + // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable + // Enums with interchangeable underlying types are interchangeable + // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 + switch (elementType) + { + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_U4: + case CorElementType.ELEMENT_TYPE_U8: + case CorElementType.ELEMENT_TYPE_U: + return elementType - 1; // normalize to signed type + default: + return elementType; + } + } // Reliability-wise, this method will either possibly corrupt your // instance & might fail when called from within a CER, or if the @@ -100,9 +150,7 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; - void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; - AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); + AssignArrayEnum r = CanAssignArrayType(sourceArray, destinationArray); if (r == AssignArrayEnum.AssignWrongType) ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); @@ -144,8 +192,62 @@ private enum AssignArrayEnum AssignPrimitiveWiden, } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] - private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); + private static unsafe AssignArrayEnum CanAssignArrayType(Array sourceArray, Array destinationArray) + { + TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + Debug.Assert(!TypeHandle.AreSameType(srcTH, destTH)); // Handled by fast path + + // Value class boxing + if (srcTH.IsValueType && !destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return AssignArrayEnum.AssignBoxValueClassOrPrimitive; + else + return AssignArrayEnum.AssignWrongType; + } + + // Value class unboxing. + if (!srcTH.IsValueType && destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return AssignArrayEnum.AssignUnboxValueClass; + else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. + return AssignArrayEnum.AssignUnboxValueClass; + else + return AssignArrayEnum.AssignWrongType; + } + + CorElementType srcElType = sourceArray.GetCorElementTypeOfElementType(); + CorElementType destElType = destinationArray.GetCorElementTypeOfElementType(); + + // Copying primitives from one type to another + if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) + { + Debug.Assert(srcElType != destElType); // Handled by fast path + if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) + return AssignArrayEnum.AssignPrimitiveWiden; + else + return AssignArrayEnum.AssignWrongType; + } + + // dest Object extends src + Debug.Assert(!srcTH.CanCastTo(destTH)); // Handled by fast path + + // src Object extends dest + if (destTH.CanCastTo(srcTH)) + return AssignArrayEnum.AssignMustCast; + + // class X extends/implements src and implements dest. + if (!destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return AssignArrayEnum.AssignMustCast; + + // class X implements src and extends/implements dest + if (!srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return AssignArrayEnum.AssignMustCast; + + return AssignArrayEnum.AssignWrongType; + } // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int 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 0c640dd1c2f0a4..452b98e9771494 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 @@ -25,6 +25,20 @@ internal static unsafe class CastHelpers [MethodImpl(MethodImplOptions.InternalCall)] private static extern void WriteBarrier(ref object? dst, object? obj); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool CanCastTo(void* fromTypeHnd, void* toTypeHnd) + { + CastResult result = CastCache.TryGet(s_table!, (nuint)fromTypeHnd, (nuint)toTypeHnd); + + if (result != CastResult.MaybeCast) + return result == CastResult.CanCast; + + return CanCastTo_NoCacheLookup(fromTypeHnd, toTypeHnd); + } + // IsInstanceOf test used for unusual cases (naked type parameters, variant generic types) // Unlike the IsInstanceOfInterface and IsInstanceOfClass functions, // this test must deal with all kinds of type tests 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 43ddbfecf006fa..3d5773e9978f25 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 @@ -784,11 +784,50 @@ public bool IsTypeDesc return (MethodTable*)m_asTAddr; } + /// + /// Gets the pointer wrapped by the current instance. + /// + /// This is only safe to call if returned . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TypeDesc* AsTypeDesc() + { + Debug.Assert(IsTypeDesc); + + return (TypeDesc*)((nint)m_asTAddr - 2); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TypeHandle TypeHandleOf() { return new TypeHandle((void*)RuntimeTypeHandle.ToIntPtr(typeof(T).TypeHandle)); } + + public static bool AreSameType(TypeHandle left, TypeHandle right) => left.m_asTAddr == right.m_asTAddr; + + public bool CanCastTo(TypeHandle destTH) => CastHelpers.CanCastTo(m_asTAddr, destTH.m_asTAddr); + + public bool IsValueType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return IsTypeDesc + ? AsTypeDesc()->GetInternalCorElementType() == CorElementType.ELEMENT_TYPE_VALUETYPE + : AsMethodTable()->IsValueType; + } + } + + public bool IsInterface => !IsTypeDesc && AsMethodTable()->IsInterface; + } + + internal unsafe struct TypeDesc + { + private readonly int m_typeAndFlags; + + // This is the ELEMENT_TYPE* that would be used in the type sig for this type + // For enums this is the underlying type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CorElementType GetInternalCorElementType() => (CorElementType)(m_typeAndFlags & 0xFF); } // Helper structs used for tail calls via helper. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 63471d9f919675..938cdeb5668332 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; @@ -109,6 +110,30 @@ internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; + private static ReadOnlySpan PrimitiveWidenTable => + [ + 0x00, // ELEMENT_TYPE_END + 0x00, // ELEMENT_TYPE_VOID + 0x0004, // ELEMENT_TYPE_BOOLEAN + 0x3F88, // ELEMENT_TYPE_CHAR (W = U2, CHAR, I4, U4, I8, U8, R4, R8) (U2 == Char) + 0x3550, // ELEMENT_TYPE_I1 (W = I1, I2, I4, I8, R4, R8) + 0x3FE8, // ELEMENT_TYPE_U1 (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8) + 0x3540, // ELEMENT_TYPE_I2 (W = I2, I4, I8, R4, R8) + 0x3F88, // ELEMENT_TYPE_U2 (W = U2, CHAR, I4, U4, I8, U8, R4, R8) + 0x3500, // ELEMENT_TYPE_I4 (W = I4, I8, R4, R8) + 0x3E00, // ELEMENT_TYPE_U4 (W = U4, I8, R4, R8) + 0x3400, // ELEMENT_TYPE_I8 (W = I8, R4, R8) + 0x3800, // ELEMENT_TYPE_U8 (W = U8, R4, R8) + 0x3000, // ELEMENT_TYPE_R4 (W = R4, R8) + 0x2000, // ELEMENT_TYPE_R8 (W = R8) + ]; + + internal static bool CanPrimitiveWiden(CorElementType srcET, CorElementType dstET) + { + Debug.Assert(srcET.IsPrimitiveType() && dstET.IsPrimitiveType()); + return (PrimitiveWidenTable[(int)srcET] & (1 << (int)dstET)) != 0; + } + /// Provide a fast way to access constant data stored in a module as a ReadOnlySpan{T} /// A field handle that specifies the location of the data to be referred to by the ReadOnlySpan{T}. The Rva of the field must be aligned on a natural boundary of type T /// A ReadOnlySpan{T} of the data stored in the field From 8a592b9d7fbef2c387d91c7c3d2fc74cb7a7c997 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 01:57:22 +0800 Subject: [PATCH 02/39] Fold IsSimpleCopy back to CanAssignArrayType --- .../src/System/Array.CoreCLR.cs | 61 ++++++------------- 1 file changed, 17 insertions(+), 44 deletions(-) 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 50989229ef8c3e..3f0733d09285bf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -62,7 +62,10 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) + AssignArrayEnum assignType = AssignArrayEnum.AssignWrongType; + + if (sourceArray.GetType() == destinationArray.GetType() + || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == AssignArrayEnum.AssignSimpleCopy) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -86,40 +89,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); - } - - private static unsafe bool IsSimpleCopy(Array sourceArray, Array destinationArray) - { - TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); - TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); - if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions - return true; - - if (srcTH.IsValueType) - { - // Value class boxing - if (!destTH.IsValueType) - return false; - - CorElementType srcElType = sourceArray.GetCorElementTypeOfElementType(); - CorElementType destElType = destinationArray.GetCorElementTypeOfElementType(); - - // Copying primitives from one type to another - if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) - { - if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - return true; - } - } - else - { - // Value class unboxing - if (destTH.IsValueType) - return false; - } - - return srcTH.CanCastTo(destTH); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); } private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) @@ -146,18 +116,16 @@ private static CorElementType GetNormalizedIntegralArrayElementType(CorElementTy // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, AssignArrayEnum assignType) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - AssignArrayEnum r = CanAssignArrayType(sourceArray, destinationArray); - - if (r == AssignArrayEnum.AssignWrongType) + if (assignType == AssignArrayEnum.AssignWrongType) ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); if (length > 0) { - switch (r) + switch (assignType) { case AssignArrayEnum.AssignUnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); @@ -185,6 +153,7 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de // Must match the definition in arraynative.cpp private enum AssignArrayEnum { + AssignSimpleCopy, AssignWrongType, AssignMustCast, AssignBoxValueClassOrPrimitive, @@ -196,7 +165,9 @@ private static unsafe AssignArrayEnum CanAssignArrayType(Array sourceArray, Arra { TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); - Debug.Assert(!TypeHandle.AreSameType(srcTH, destTH)); // Handled by fast path + + if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions + return AssignArrayEnum.AssignSimpleCopy; // Value class boxing if (srcTH.IsValueType && !destTH.IsValueType) @@ -224,15 +195,17 @@ private static unsafe AssignArrayEnum CanAssignArrayType(Array sourceArray, Arra // Copying primitives from one type to another if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) { - Debug.Assert(srcElType != destElType); // Handled by fast path - if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + return AssignArrayEnum.AssignSimpleCopy; + else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) return AssignArrayEnum.AssignPrimitiveWiden; else return AssignArrayEnum.AssignWrongType; } // dest Object extends src - Debug.Assert(!srcTH.CanCastTo(destTH)); // Handled by fast path + if (srcTH.CanCastTo(destTH)) + return AssignArrayEnum.AssignSimpleCopy; // src Object extends dest if (destTH.CanCastTo(srcTH)) From 0dce3d4e7edf68d1c2bf9058824c70ebb7dbf7b9 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 01:59:02 +0800 Subject: [PATCH 03/39] Delete FCall and QCall definitions for copy --- .../classlibnative/bcltype/arraynative.cpp | 159 ------------------ .../classlibnative/bcltype/arraynative.h | 3 - src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 - 4 files changed, 164 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 02ff0360ac4b7d..00a1eeda6f6c7b 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -47,165 +47,6 @@ extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHand return ctorEntrypoint; } - // Returns whether you can directly copy an array of srcType into destType. -FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst) -{ - FCALL_CONTRACT; - - _ASSERTE(pSrc != NULL); - _ASSERTE(pDst != NULL); - - // This case is expected to be handled by the fast path - _ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable()); - - TypeHandle srcTH = pSrc->GetMethodTable()->GetArrayElementTypeHandle(); - TypeHandle destTH = pDst->GetMethodTable()->GetArrayElementTypeHandle(); - if (srcTH == destTH) // This check kicks for different array kind or dimensions - FC_RETURN_BOOL(true); - - if (srcTH.IsValueType()) - { - // Value class boxing - if (!destTH.IsValueType()) - FC_RETURN_BOOL(false); - - const CorElementType srcElType = srcTH.GetVerifierCorElementType(); - const CorElementType destElType = destTH.GetVerifierCorElementType(); - _ASSERTE(srcElType < ELEMENT_TYPE_MAX); - _ASSERTE(destElType < ELEMENT_TYPE_MAX); - - // Copying primitives from one type to another - if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) - { - if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - FC_RETURN_BOOL(true); - } - } - else - { - // Value class unboxing - if (destTH.IsValueType()) - FC_RETURN_BOOL(false); - } - - TypeHandle::CastResult r = srcTH.CanCastToCached(destTH); - if (r != TypeHandle::MaybeCast) - { - FC_RETURN_BOOL(r); - } - - struct - { - OBJECTREF src; - OBJECTREF dst; - } gc; - - gc.src = ObjectToOBJECTREF(pSrc); - gc.dst = ObjectToOBJECTREF(pDst); - - BOOL iRetVal = FALSE; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - iRetVal = srcTH.CanCastTo(destTH); - HELPER_METHOD_FRAME_END(); - - FC_RETURN_BOOL(iRetVal); -} -FCIMPLEND - - -// Return values for CanAssignArrayType -enum AssignArrayEnum -{ - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, -}; - -// Returns an enum saying whether you can copy an array of srcType into destType. -static AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - PRECONDITION(!srcTH.IsNull()); - PRECONDITION(!destTH.IsNull()); - } - CONTRACTL_END; - - _ASSERTE(srcTH != destTH); // Handled by fast path - - // Value class boxing - if (srcTH.IsValueType() && !destTH.IsValueType()) - { - if (srcTH.CanCastTo(destTH)) - return AssignBoxValueClassOrPrimitive; - else - return AssignWrongType; - } - - // Value class unboxing. - if (!srcTH.IsValueType() && destTH.IsValueType()) - { - if (srcTH.CanCastTo(destTH)) - return AssignUnboxValueClass; - else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. - return AssignUnboxValueClass; - else - return AssignWrongType; - } - - const CorElementType srcElType = srcTH.GetVerifierCorElementType(); - const CorElementType destElType = destTH.GetVerifierCorElementType(); - _ASSERTE(srcElType < ELEMENT_TYPE_MAX); - _ASSERTE(destElType < ELEMENT_TYPE_MAX); - - // Copying primitives from one type to another - if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) - { - _ASSERTE(srcElType != destElType); // Handled by fast path - if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType)) - return AssignPrimitiveWiden; - else - return AssignWrongType; - } - - // dest Object extends src - _ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path - - // src Object extends dest - if (destTH.CanCastTo(srcTH)) - return AssignMustCast; - - // class X extends/implements src and implements dest. - if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE) - return AssignMustCast; - - // class X implements src and extends/implements dest - if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE) - return AssignMustCast; - - return AssignWrongType; -} - -extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) -{ - QCALL_CONTRACT; - - INT32 ret = 0; - - BEGIN_QCALL; - - ret = CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); - - END_QCALL; - - return ret; -} - // // This is a GC safe variant of the memmove intrinsic. It sets the cards, and guarantees that the object references in the GC heap are diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index aeb264a9b28ceb..c6d0473bb4f657 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 FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); - // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); @@ -43,6 +41,5 @@ class ArrayNative 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); -extern "C" INT32 QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH); #endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 98a38b8603419c..05908387de320e 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -361,7 +361,6 @@ FCFuncEnd() FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) - FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) FCFuncElement("InternalSetValue", ArrayNative::SetValue) FCFuncEnd() diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e038db62831d93..0f919f9ed91314 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -171,7 +171,6 @@ static const Entry s_QCall[] = DllImportEntry(MdUtf8String_EqualsCaseInsensitive) DllImportEntry(Array_CreateInstance) DllImportEntry(Array_GetElementConstructorEntrypoint) - DllImportEntry(Array_CanAssignArrayType) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) DllImportEntry(AssemblyNative_GetLocation) From d29e5d84dac961d00a7cf4e494f883a1bf21c969 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 12:29:49 +0800 Subject: [PATCH 04/39] Convert InternalSetValue to managed --- .../src/System/Array.CoreCLR.cs | 358 +++++++++++------- .../Runtime/CompilerServices/CastHelpers.cs | 2 +- .../RuntimeHelpers.CoreCLR.cs | 8 + .../classlibnative/bcltype/arraynative.cpp | 87 ----- .../classlibnative/bcltype/arraynative.h | 3 - src/coreclr/vm/ecalllist.h | 1 - .../src/System/ThrowHelper.cs | 9 + 7 files changed, 238 insertions(+), 230 deletions(-) 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 3f0733d09285bf..44bba8389c500c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -324,147 +324,152 @@ private static unsafe void CopyImplPrimitiveWiden(Array sourceArray, int sourceI ref byte srcElement = ref Unsafe.Add(ref srcData, (nuint)i * srcElSize); ref byte destElement = ref Unsafe.Add(ref data, (nuint)i * destElSize); - switch (srcElType) - { - case CorElementType.ELEMENT_TYPE_U1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_CHAR: - case CorElementType.ELEMENT_TYPE_I2: - case CorElementType.ELEMENT_TYPE_U2: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = srcElement; break; - default: - Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I2: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - // U2 and CHAR are identical in conversion - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I2: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; - } - break; + PrimitiveWiden(ref srcElement, ref destElement, srcElType, destElType); + } + } - case CorElementType.ELEMENT_TYPE_I4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; - } - break; + private static void PrimitiveWiden(ref byte srcElement, ref byte destElement, CorElementType srcElType, CorElementType destElType) + { + switch (srcElType) + { + case CorElementType.ELEMENT_TYPE_U1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_I2: + case CorElementType.ELEMENT_TYPE_U2: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = srcElement; break; + default: + Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I2: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; + } + break; - case CorElementType.ELEMENT_TYPE_U8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; - } - break; + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + // U2 and CHAR are identical in conversion + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I2: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; + } + break; - case CorElementType.ELEMENT_TYPE_I8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; - } - break; + case CorElementType.ELEMENT_TYPE_U4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; + } + break; - case CorElementType.ELEMENT_TYPE_R4: - Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_U8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_R4: + Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; - } + default: + Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; } } @@ -629,8 +634,85 @@ private unsafe nint GetFlattenedIndex(ReadOnlySpan indices) return result; } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InternalSetValue(object? value, nint flattenedIndex); + 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) + { + ThrowHelper.ThrowInvalidCastException_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 (!RuntimeHelpers.TryUnboxInto(ref offsetDataRef, pElementMethodTable, value)) + { + // 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) + ThrowHelper.ThrowInvalidCastException_StoreArrayElement(); + + // Get a properly widened type + if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_PrimitiveWiden); + + PrimitiveWiden(ref Unsafe.Unbox(value), ref offsetDataRef, srcType, targetType); + } + } + + GC.KeepAlive(this); // Keep the method table alive + } 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 452b98e9771494..a1a1a9c661e6ea 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 @@ -43,7 +43,7 @@ internal static bool CanCastTo(void* fromTypeHnd, void* toTypeHnd) // Unlike the IsInstanceOfInterface and IsInstanceOfClass functions, // this test must deal with all kinds of type tests [DebuggerHidden] - private static object? IsInstanceOfAny(void* toTypeHnd, object? obj) + internal static object? IsInstanceOfAny(void* toTypeHnd, object? obj) { if (obj != null) { 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 3d5773e9978f25..e032de7b0620f2 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 @@ -323,6 +323,9 @@ internal static unsafe bool ObjectHasComponentSize(object obj) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern unsafe void Unbox_Nullable(ref byte destination, MethodTable* toTypeHnd, object? obj); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe bool TryUnboxInto(ref byte destination, MethodTable* toTypeHnd, object obj); + // Given an object reference, returns its MethodTable*. // // WARNING: The caller has to ensure that MethodTable* does not get unloaded. The most robust way @@ -686,6 +689,8 @@ public int MultiDimensionalArrayRank // Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums. public bool IsPrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_PrimitiveValueType or enum_flag_Category_TruePrimitive; + public bool IsTruePrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_TruePrimitive; + public bool HasInstantiation => (Flags & enum_flag_HasComponentSize) == 0 && (Flags & enum_flag_GenericsMask) != enum_flag_GenericsMask_NonGeneric; public bool IsGenericTypeDefinition => (Flags & (enum_flag_HasComponentSize | enum_flag_GenericsMask)) == enum_flag_GenericsMask_TypicalInst; @@ -715,6 +720,9 @@ public TypeHandle GetArrayElementTypeHandle() [MethodImpl(MethodImplOptions.InternalCall)] public extern uint GetNumInstanceFieldBytes(); + + [MethodImpl(MethodImplOptions.InternalCall)] + public extern CorElementType GetVerifierCorElementType(); } // Subset of src\vm\methodtable.h diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 00a1eeda6f6c7b..a53b68e064fa8c 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -207,93 +207,6 @@ 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 - // This method will initialize an array from a TypeHandle to a field. FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index c6d0473bb4f657..f5901311463d38 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -27,9 +27,6 @@ class ArrayNative public: static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); - // This set of methods will set a value in an array - static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); - // This method will initialize an array from a TypeHandle // to a field. static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 05908387de320e..82f807a012e2b3 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -361,7 +361,6 @@ FCFuncEnd() FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) - FCFuncElement("InternalSetValue", ArrayNative::SetValue) FCFuncEnd() FCFuncStart(gBufferFuncs) diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index eb9b5b7eb02618..f49c16c51ef425 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -83,6 +83,12 @@ internal static void ThrowInvalidCastException_DownCastArrayElement() throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); } + [DoesNotReturn] + internal static void ThrowInvalidCastException_StoreArrayElement() + { + throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); + } + [DoesNotReturn] internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { @@ -1244,6 +1250,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Format_ExpectedAsciiDigit; case ExceptionResource.Argument_HasToBeArrayClass: return SR.Argument_HasToBeArrayClass; + case ExceptionResource.Arg_PrimitiveWiden: + return SR.Arg_PrimWiden; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1440,5 +1448,6 @@ internal enum ExceptionResource Format_UnclosedFormatItem, Format_ExpectedAsciiDigit, Argument_HasToBeArrayClass, + Arg_PrimitiveWiden, } } From 5dd0e3c8a4693092095f16115a5aa7cdcf74bbc7 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 12:53:09 +0800 Subject: [PATCH 05/39] Setup FCalls --- .../Runtime/CompilerServices/CastHelpers.cs | 14 -------------- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 17 +++++++++++++++-- src/coreclr/vm/comutilnative.cpp | 14 ++++++++++++++ src/coreclr/vm/comutilnative.h | 2 ++ src/coreclr/vm/ecalllist.h | 3 +++ 5 files changed, 34 insertions(+), 16 deletions(-) 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 a1a1a9c661e6ea..14c8a959cda5b0 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 @@ -25,20 +25,6 @@ internal static unsafe class CastHelpers [MethodImpl(MethodImplOptions.InternalCall)] private static extern void WriteBarrier(ref object? dst, object? obj); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool CanCastTo(void* fromTypeHnd, void* toTypeHnd) - { - CastResult result = CastCache.TryGet(s_table!, (nuint)fromTypeHnd, (nuint)toTypeHnd); - - if (result != CastResult.MaybeCast) - return result == CastResult.CanCast; - - return CanCastTo_NoCacheLookup(fromTypeHnd, toTypeHnd); - } - // IsInstanceOf test used for unusual cases (naked type parameters, variant generic types) // Unlike the IsInstanceOfInterface and IsInstanceOfClass functions, // this test must deal with all kinds of type tests 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 e032de7b0620f2..b18bb960595793 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 @@ -723,6 +723,10 @@ public TypeHandle GetArrayElementTypeHandle() [MethodImpl(MethodImplOptions.InternalCall)] public extern CorElementType GetVerifierCorElementType(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); + } // Subset of src\vm\methodtable.h @@ -750,7 +754,7 @@ public bool CanCompareBitsOrUseFastGetHashCode /// /// A type handle, which can wrap either a pointer to a TypeDesc or to a . /// - internal unsafe struct TypeHandle + internal readonly unsafe struct TypeHandle { // Subset of src\vm\typehandle.h @@ -812,7 +816,16 @@ public static TypeHandle TypeHandleOf() public static bool AreSameType(TypeHandle left, TypeHandle right) => left.m_asTAddr == right.m_asTAddr; - public bool CanCastTo(TypeHandle destTH) => CastHelpers.CanCastTo(m_asTAddr, destTH.m_asTAddr); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CanCastTo(TypeHandle destTH) + { + CastResult result = CastCache.TryGet(CastHelpers.s_table!, (nuint)m_asTAddr, (nuint)destTH.m_asTAddr); + + if (result != CastResult.MaybeCast) + return result == CastResult.CanCast; + + return MethodTable.CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr); + } public bool IsValueType { diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index a3c9d0a848cdff..f1c3209c86a511 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1787,6 +1787,20 @@ FCIMPL1(UINT32, MethodTableNative::GetNumInstanceFieldBytes, MethodTable* mt) } FCIMPLEND +FCIMPL1(CorElementType, MethodTableNative::GetVerifierCorElementType, MethodTable* mt) +{ + FCALL_CONTRACT; + return mt->GetVerifierCorElementType(); +} +FCIMPLEND + +FCIMPL2(FC_BOOL_RET, MethodTableNative::CanCastTo, void* fromType, void* toType) +{ + FCALL_CONTRACT; + FC_RETURN_BOOL(TypeHandle::FromPtr(fromType).CanCastTo(TypeHandle::FromPtr(toType))); +} +FCIMPLEND + extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb) { QCALL_CONTRACT; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 3e64207564c847..91c0a4b5815d99 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -244,6 +244,8 @@ extern "C" void QCALLTYPE Interlocked_MemoryBarrierProcessWide(); class MethodTableNative { public: static FCDECL1(UINT32, GetNumInstanceFieldBytes, MethodTable* mt); + static FCDECL1(CorElementType, GetVerifierCorElementType, MethodTable* mt); + static FCDECL2(FC_BOOL_RET, CanCastTo, MethodTable* fromType, MethodTable* toType); }; extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 82f807a012e2b3..ca928c2f09635a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -353,6 +353,7 @@ FCFuncEnd() #endif // FEATURE_COMINTEROP FCFuncStart(gCastHelpers) + FCFuncElement("CanCastTo_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup) FCFuncElement("Unbox_Helper", ::Unbox_Helper) @@ -454,6 +455,8 @@ FCFuncEnd() FCFuncStart(gMethodTableFuncs) FCFuncElement("GetNumInstanceFieldBytes", MethodTableNative::GetNumInstanceFieldBytes) + FCFuncElement("GetVerifierCorElementType", MethodTableNative::GetVerifierCorElementType) + FCFuncElement("CanCastTo_NoCacheLookup", MethodTableNative::CanCastTo) FCFuncEnd() FCFuncStart(gStubHelperFuncs) From 527021c76290a1ba111f2fc2c5a8631185f6248d Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 13:13:03 +0800 Subject: [PATCH 06/39] Remove FCall for GetCorElementTypeOfElementType --- .../src/System/Array.CoreCLR.cs | 13 +++++++++---- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 6 +++++- src/coreclr/classlibnative/bcltype/arraynative.cpp | 11 ----------- src/coreclr/classlibnative/bcltype/arraynative.h | 2 -- src/coreclr/vm/ecalllist.h | 5 ----- 5 files changed, 14 insertions(+), 23 deletions(-) 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 44bba8389c500c..8f548951eb9133 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -189,8 +189,8 @@ private static unsafe AssignArrayEnum CanAssignArrayType(Array sourceArray, Arra return AssignArrayEnum.AssignWrongType; } - CorElementType srcElType = sourceArray.GetCorElementTypeOfElementType(); - CorElementType destElType = destinationArray.GetCorElementTypeOfElementType(); + CorElementType srcElType = srcTH.GetVerifierCorElementType(); + CorElementType destElType = destTH.GetVerifierCorElementType(); // Copying primitives from one type to another if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) @@ -770,8 +770,13 @@ public unsafe int GetLowerBound(int dimension) return Unsafe.Add(ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this), rank + dimension); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern CorElementType GetCorElementTypeOfElementType(); + internal unsafe CorElementType GetCorElementTypeOfElementType() + { + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + CorElementType et = pMT->GetArrayElementTypeHandle().GetVerifierCorElementType(); + GC.KeepAlive(this); + return et; + } private unsafe bool IsValueOfElementType(object value) { 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 b18bb960595793..58276a9a11de21 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 @@ -752,7 +752,7 @@ public bool CanCompareBitsOrUseFastGetHashCode } /// - /// A type handle, which can wrap either a pointer to a TypeDesc or to a . + /// A type handle, which can wrap either a pointer to a or to a . /// internal readonly unsafe struct TypeHandle { @@ -839,6 +839,10 @@ public bool IsValueType } public bool IsInterface => !IsTypeDesc && AsMethodTable()->IsInterface; + + public CorElementType GetVerifierCorElementType() => IsTypeDesc + ? AsTypeDesc()->GetInternalCorElementType() + : AsMethodTable()->GetVerifierCorElementType(); } internal unsafe struct TypeDesc diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index a53b68e064fa8c..aa675eb29633af 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -17,17 +17,6 @@ #include "arraynative.inl" -// Returns a bool to indicate if the array is of primitive types or not. -FCIMPL1(INT32, ArrayNative::GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE) -{ - FCALL_CONTRACT; - - _ASSERTE(arrayUNSAFE != NULL); - - return arrayUNSAFE->GetArrayElementTypeHandle().GetVerifierCorElementType(); -} -FCIMPLEND - extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd) { QCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index f5901311463d38..6f130c2215b409 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -25,8 +25,6 @@ struct FCALLRuntimeFieldHandle class ArrayNative { public: - static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); - // This method will initialize an array from a TypeHandle // to a field. static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index ca928c2f09635a..8014210f110bcd 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -360,10 +360,6 @@ FCFuncStart(gCastHelpers) FCFuncElement("WriteBarrier", ::WriteBarrier_Helper) FCFuncEnd() -FCFuncStart(gArrayFuncs) - FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) -FCFuncEnd() - FCFuncStart(gBufferFuncs) FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) FCFuncEnd() @@ -539,7 +535,6 @@ 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) From e7d843a0e00447858134db1661c53de957d25e10 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 13:31:30 +0800 Subject: [PATCH 07/39] Complete TryUnBox --- .../src/System/Array.CoreCLR.cs | 19 +++++++++++++++++-- .../RuntimeHelpers.CoreCLR.cs | 3 --- src/coreclr/vm/ecalllist.h | 1 - 3 files changed, 17 insertions(+), 6 deletions(-) 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 8f548951eb9133..8d165315e9e2b6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -693,7 +693,22 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex) // value class or primitive type ref byte offsetDataRef = ref Unsafe.Add(ref arrayDataRef, flattenedIndex * pMethodTable->ComponentSize); - if (!RuntimeHelpers.TryUnboxInto(ref offsetDataRef, pElementMethodTable, value)) + 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); @@ -707,7 +722,7 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex) if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType)) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_PrimitiveWiden); - PrimitiveWiden(ref Unsafe.Unbox(value), ref offsetDataRef, srcType, targetType); + PrimitiveWiden(ref value.GetRawData(), ref offsetDataRef, srcType, targetType); } } 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 58276a9a11de21..921af0d7967ae4 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 @@ -323,9 +323,6 @@ internal static unsafe bool ObjectHasComponentSize(object obj) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern unsafe void Unbox_Nullable(ref byte destination, MethodTable* toTypeHnd, object? obj); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe bool TryUnboxInto(ref byte destination, MethodTable* toTypeHnd, object obj); - // Given an object reference, returns its MethodTable*. // // WARNING: The caller has to ensure that MethodTable* does not get unloaded. The most robust way diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 8014210f110bcd..7a18572cc46a8a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -353,7 +353,6 @@ FCFuncEnd() #endif // FEATURE_COMINTEROP FCFuncStart(gCastHelpers) - FCFuncElement("CanCastTo_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup) FCFuncElement("Unbox_Helper", ::Unbox_Helper) From 2bb5fe78f1d1ab73237ab33c73116b08b3390bce Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 16:01:38 +0800 Subject: [PATCH 08/39] Fix FCall definition --- src/coreclr/vm/comutilnative.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 91c0a4b5815d99..ad519082674899 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -245,7 +245,7 @@ class MethodTableNative { public: static FCDECL1(UINT32, GetNumInstanceFieldBytes, MethodTable* mt); static FCDECL1(CorElementType, GetVerifierCorElementType, MethodTable* mt); - static FCDECL2(FC_BOOL_RET, CanCastTo, MethodTable* fromType, MethodTable* toType); + static FCDECL2(FC_BOOL_RET, CanCastTo, void* fromType, void* toType); }; extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); From f7da1728f3250505b71170b322ae4796f5813f99 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 15:34:46 +0800 Subject: [PATCH 09/39] Implement InitializeArray in managed --- .../RuntimeHelpers.CoreCLR.cs | 69 +++++++++++++++++-- .../src/Resources/Strings.resx | 8 ++- .../src/System/ThrowHelper.cs | 6 ++ 3 files changed, 78 insertions(+), 5 deletions(-) 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 921af0d7967ae4..ab36b1cf50c1a3 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 @@ -1,22 +1,83 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) + { + RtFieldInfo fldInfo = (RtFieldInfo)FieldInfo.GetFieldFromHandle(fldHandle); + + if (array is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) != 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); + + MethodTable* pMT = GetMethodTable(array); + nuint totalSize = pMT->ComponentSize * array.NativeLength; + + uint size = ((MethodTable*)((RuntimeType)fldInfo.FieldType).TypeHandle.Value)->GetNumInstanceFieldBytes(); + + // make certain you don't go off the end of the rva static + if (totalSize > size) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); + + Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); + + if (BitConverter.IsLittleEndian) + { + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + } + else + { + switch (pMT->ComponentSize) + { + case 1: + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + break; + case 2: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 4: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 8: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + default: + Debug.Fail("Incorrect primitive type size!"); + break; + } + } + } [MethodImpl(MethodImplOptions.InternalCall)] private static extern unsafe void* GetSpanDataFrom( diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index c4504f140527a3..4a270782171146 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4319,4 +4319,10 @@ Emitting debug info is not supported for this member. - + + The field is invalid for initializing array or span. + + + The array must be array of primitive or enum type. + + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index f49c16c51ef425..3997d47191fb83 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -1252,6 +1252,10 @@ private static string GetResourceString(ExceptionResource resource) return SR.Argument_HasToBeArrayClass; case ExceptionResource.Arg_PrimitiveWiden: return SR.Arg_PrimWiden; + case ExceptionResource.Argument_MustBePrimitiveArray: + return SR.Argument_MustBePrimitiveArray; + case ExceptionResource.Argument_BadFieldForInitializeArray: + return SR.Argument_BadFieldForInitializeArray; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1449,5 +1453,7 @@ internal enum ExceptionResource Format_ExpectedAsciiDigit, Argument_HasToBeArrayClass, Arg_PrimitiveWiden, + Argument_MustBePrimitiveArray, + Argument_BadFieldForInitializeArray, } } From 0924555cccbae35e698fbe5310586c6e1877ed3c Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 15:45:21 +0800 Subject: [PATCH 10/39] Implement GetSpanDataFrom in managed --- .../RuntimeHelpers.CoreCLR.cs | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) 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 ab36b1cf50c1a3..7e16c52e6dbbd4 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 @@ -79,11 +79,37 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan } } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetSpanDataFrom( + private static unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count); + out int count) + { + RtFieldInfo fldInfo = (RtFieldInfo)FieldInfo.GetFieldFromHandle(fldHandle); + + if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) != 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); + Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter + + if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); + + uint totalSize = ((MethodTable*)((RuntimeType)fldInfo.FieldType).TypeHandle.Value)->GetNumInstanceFieldBytes(); + uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); + + IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + if (data % targetTypeSize != 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + if (!BitConverter.IsLittleEndian) + { + throw new PlatformNotSupportedException(); + } + + count = (int)(totalSize / targetTypeSize); + return (void*)data; + } // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use From dc85381096f9e18f6ae60b4b42a124b0ccdda4cb Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 15:48:10 +0800 Subject: [PATCH 11/39] Remove FCall definition --- .../classlibnative/bcltype/arraynative.cpp | 113 ------------------ .../classlibnative/bcltype/arraynative.h | 21 +--- src/coreclr/vm/ecalllist.h | 2 - 3 files changed, 1 insertion(+), 135 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index aa675eb29633af..246c5398276073 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -195,116 +195,3 @@ void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT3 Done: ; END_QCALL; } - -// This method will initialize an array from a TypeHandle to a field. - -FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) -{ - FCALL_CONTRACT; - - BASEARRAYREF arr = BASEARRAYREF(pArrayRef); - REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - HELPER_METHOD_FRAME_BEGIN_2(arr, refField); - - if ((arr == 0) || (refField == NULL)) - COMPlusThrow(kArgumentNullException); - - FieldDesc* pField = (FieldDesc*) refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) - COMPlusThrow(kArgumentException); - - SIZE_T dwCompSize = arr->GetComponentSize(); - SIZE_T dwElemCnt = arr->GetNumComponents(); - SIZE_T dwTotalSize = dwCompSize * dwElemCnt; - - DWORD size = pField->LoadSize(); - - // make certain you don't go off the end of the rva static - if (dwTotalSize > size) - COMPlusThrow(kArgumentException); - - void *src = pField->GetStaticAddressHandle(NULL); - void *dest = arr->GetDataPtr(); - -#if BIGENDIAN - DWORD i; - switch (dwCompSize) { - case 1: - memcpyNoGCRefs(dest, src, dwElemCnt); - break; - case 2: - for (i = 0; i < dwElemCnt; i++) - *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); - break; - case 4: - for (i = 0; i < dwElemCnt; i++) - *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); - break; - case 8: - for (i = 0; i < dwElemCnt; i++) - *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); - break; - default: - // should not reach here. - UNREACHABLE_MSG("Incorrect primitive type size!"); - break; - } -#else - memcpyNoGCRefs(dest, src, dwTotalSize); -#endif - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) -{ - FCALL_CONTRACT; - struct - { - REFLECTFIELDREF refField; - REFLECTCLASSBASEREF refClass; - } gc; - gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); - void* data = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - TypeHandle targetTypeHandle = gc.refClass->GetType(); - if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) - COMPlusThrow(kArgumentException); - - DWORD totalSize = pField->LoadSize(); - DWORD targetTypeSize = targetTypeHandle.GetSize(); - - data = pField->GetStaticAddressHandle(NULL); - _ASSERTE(data != NULL); - _ASSERTE(count != NULL); - - if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) - COMPlusThrow(kArgumentException); - - *count = (INT32)totalSize / targetTypeSize; - -#if BIGENDIAN - COMPlusThrow(kPlatformNotSupportedException); -#endif - - HELPER_METHOD_FRAME_END(); - return data; -} -FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 6f130c2215b409..5dd3570ce5cbba 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -13,26 +13,7 @@ #ifndef _ARRAYNATIVE_H_ #define _ARRAYNATIVE_H_ -#include "fcall.h" -#include "runtimehandles.h" - -struct FCALLRuntimeFieldHandle -{ - ReflectFieldObject *pFieldDONOTUSEDIRECTLY; -}; -#define FCALL_RFH_TO_REFLECTFIELD(x) (x).pFieldDONOTUSEDIRECTLY - -class ArrayNative -{ -public: - // This method will initialize an array from a TypeHandle - // to a field. - static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); - - // This method will acquire data to create a span from a TypeHandle - // to a field. - static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); -}; +#include "qcall.h" 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 7a18572cc46a8a..b33ad43eae218b 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -434,8 +434,6 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) - FCFuncElement("InitializeArray", ArrayNative::InitializeArray) - FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) From f99c553d9964b2d93e741af3ca139e3008640384 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 22:48:13 +0800 Subject: [PATCH 12/39] Fix RVA field address --- src/coreclr/vm/reflectioninvocation.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index f57e21801253a3..e4ca976f2fd6c6 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1246,14 +1246,16 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF // IsFastPathSupported needs to checked before calling this method. _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); - PTR_BYTE base = 0; - if (!pFieldDesc->IsRVA()) + if (pFieldDesc->IsRVA()) { - // For RVA the base is ignored and offset is used. - base = pFieldDesc->GetBase(); + Module* pModule = pFieldDesc->GetModule(); + return pModule->GetRvaField(pFieldDesc->GetOffset()); + } + else + { + PTR_BYTE base = pFieldDesc->GetBase(); + return PTR_VOID(base + pFieldDesc->GetOffset()); } - - return PTR_VOID(base + pFieldDesc->GetOffset()); } FCIMPLEND From c5e6cc4fd8d1d876ff67d966531f9e6d3c8e01ab Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 28 May 2024 22:53:31 +0800 Subject: [PATCH 13/39] Fix RVA assert --- .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7e16c52e6dbbd4..124e27f3328087 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 @@ -21,7 +21,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan if (array is null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) != 0) + if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); // Note that we do not check that the field is actually in the PE file that is initializing @@ -86,7 +86,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan { RtFieldInfo fldInfo = (RtFieldInfo)FieldInfo.GetFieldFromHandle(fldHandle); - if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) != 0) + if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); From 470b86fcce282dfb5cdd2c74da205aa8cf694d4a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 29 May 2024 00:36:47 +0800 Subject: [PATCH 14/39] Do not use hydrated RtFieldInfo --- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 12 ++++++------ .../src/System/RuntimeHandles.cs | 12 +++++++++++- src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/runtimehandles.cpp | 13 +++++++++++++ src/coreclr/vm/runtimehandles.h | 1 + 5 files changed, 32 insertions(+), 7 deletions(-) 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 124e27f3328087..ec0a606c769a5c 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 @@ -16,12 +16,12 @@ public static partial class RuntimeHelpers [Intrinsic] public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) { - RtFieldInfo fldInfo = (RtFieldInfo)FieldInfo.GetFieldFromHandle(fldHandle); + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); if (array is null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) == 0) + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); // Note that we do not check that the field is actually in the PE file that is initializing @@ -35,7 +35,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan MethodTable* pMT = GetMethodTable(array); nuint totalSize = pMT->ComponentSize * array.NativeLength; - uint size = ((MethodTable*)((RuntimeType)fldInfo.FieldType).TypeHandle.Value)->GetNumInstanceFieldBytes(); + uint size = ((MethodTable*)RuntimeFieldHandle.GetApproxFieldType(fldInfo))->GetNumInstanceFieldBytes(); // make certain you don't go off the end of the rva static if (totalSize > size) @@ -84,9 +84,9 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan RuntimeTypeHandle targetTypeHandle, out int count) { - RtFieldInfo fldInfo = (RtFieldInfo)FieldInfo.GetFieldFromHandle(fldHandle); + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - if ((fldInfo.Attributes & FieldAttributes.HasFieldRVA) == 0) + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); @@ -95,7 +95,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); - uint totalSize = ((MethodTable*)((RuntimeType)fldInfo.FieldType).TypeHandle.Value)->GetNumInstanceFieldBytes(); + uint totalSize = ((MethodTable*)RuntimeFieldHandle.GetApproxFieldType(fldInfo))->GetNumInstanceFieldBytes(); uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 380981993451e9..7ba90efac80423 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1180,6 +1180,16 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) return type; } + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern IntPtr GetApproxFieldType(RuntimeFieldHandleInternal field); + + internal static IntPtr GetApproxFieldType(IRuntimeFieldInfo field) + { + IntPtr th = GetApproxFieldType(field.Value); + GC.KeepAlive(field); + return th; + } + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool IsFastPathSupported(RtFieldInfo field); @@ -1187,7 +1197,7 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) internal static extern int GetInstanceFieldOffset(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field); + internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index b33ad43eae218b..e5231f43e6410a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -224,6 +224,7 @@ FCFuncStart(gCOMFieldHandleNewFuncs) FCFuncElement("_GetUtf8Name", RuntimeFieldHandle::GetUtf8Name) FCFuncElement("GetAttributes", RuntimeFieldHandle::GetAttributes) FCFuncElement("GetApproxDeclaringType", RuntimeFieldHandle::GetApproxDeclaringType) + FCFuncElement("GetApproxFieldType", RuntimeFieldHandle::GetApproxFieldType) FCFuncElement("GetToken", RuntimeFieldHandle::GetToken) FCFuncElement("GetStaticFieldForGenericType", RuntimeFieldHandle::GetStaticFieldForGenericType) FCFuncElement("AcquiresContextFromThis", RuntimeFieldHandle::AcquiresContextFromThis) diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index c660763f11577b..2817ef5a76ecee 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -2622,6 +2622,19 @@ FCIMPL1(ReflectClassBaseObject*, RuntimeFieldHandle::GetApproxDeclaringType, Fie } FCIMPLEND +FCIMPL1(void*, RuntimeFieldHandle::GetApproxFieldType, FieldDesc *pField) { + CONTRACTL { + FCALL_CHECK; + } + CONTRACTL_END; + + if (!pField) + FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); + + return pField->GetApproxFieldTypeHandleThrowing().GetMethodTable(); +} +FCIMPLEND + FCIMPL1(INT32, RuntimeFieldHandle::GetToken, ReflectFieldObject *pFieldUNSAFE) { CONTRACTL { FCALL_CHECK; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 56bb2df245f9f7..a763e33dbc2c62 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -303,6 +303,7 @@ class RuntimeFieldHandle { static FCDECL1(INT32, GetAttributes, FieldDesc *pField); static FCDECL1(ReflectClassBaseObject*, GetApproxDeclaringType, FieldDesc *pField); + static FCDECL1(void*, GetApproxFieldType, FieldDesc *pField); static FCDECL1(INT32, GetToken, ReflectFieldObject *pFieldUNSAFE); static FCDECL2(FieldDesc*, GetStaticFieldForGenericType, FieldDesc *pField, ReflectClassBaseObject *pDeclaringType); static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); From c7821e6a6dcb3b97dce556da8722947a5bfe5a88 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 29 May 2024 20:19:36 +0800 Subject: [PATCH 15/39] Use QCall for LoadSize --- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 ++-- .../src/System/RuntimeHandles.cs | 15 ++++----------- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcall.h | 18 ++++++++++++++++++ src/coreclr/vm/qcallentrypoints.cpp | 1 + src/coreclr/vm/reflectioninvocation.cpp | 13 +++++++++++++ src/coreclr/vm/runtimehandles.cpp | 13 ------------- src/coreclr/vm/runtimehandles.h | 2 +- .../Runtime/CompilerServices/QCallHandles.cs | 13 +++++++++++++ 9 files changed, 52 insertions(+), 28 deletions(-) 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 ec0a606c769a5c..313e4da564c9aa 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 @@ -35,7 +35,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan MethodTable* pMT = GetMethodTable(array); nuint totalSize = pMT->ComponentSize * array.NativeLength; - uint size = ((MethodTable*)RuntimeFieldHandle.GetApproxFieldType(fldInfo))->GetNumInstanceFieldBytes(); + uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); // make certain you don't go off the end of the rva static if (totalSize > size) @@ -95,7 +95,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); - uint totalSize = ((MethodTable*)RuntimeFieldHandle.GetApproxFieldType(fldInfo))->GetNumInstanceFieldBytes(); + uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 7ba90efac80423..472f4f6318a3dc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa } [NonVersionable] - public unsafe struct RuntimeFieldHandle : IEquatable, ISerializable + public unsafe partial struct RuntimeFieldHandle : IEquatable, ISerializable { // Returns handle for interop with EE. The handle is guaranteed to be non-null. internal RuntimeFieldHandle GetNativeHandle() @@ -1180,16 +1180,6 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) return type; } - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetApproxFieldType(RuntimeFieldHandleInternal field); - - internal static IntPtr GetApproxFieldType(IRuntimeFieldInfo field) - { - IntPtr th = GetApproxFieldType(field.Value); - GC.KeepAlive(field); - return th; - } - [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool IsFastPathSupported(RtFieldInfo field); @@ -1199,6 +1189,9 @@ internal static IntPtr GetApproxFieldType(IRuntimeFieldInfo field) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")] + internal static partial uint GetFieldSize(QCallFieldHandle field); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index e5231f43e6410a..b33ad43eae218b 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -224,7 +224,6 @@ FCFuncStart(gCOMFieldHandleNewFuncs) FCFuncElement("_GetUtf8Name", RuntimeFieldHandle::GetUtf8Name) FCFuncElement("GetAttributes", RuntimeFieldHandle::GetAttributes) FCFuncElement("GetApproxDeclaringType", RuntimeFieldHandle::GetApproxDeclaringType) - FCFuncElement("GetApproxFieldType", RuntimeFieldHandle::GetApproxFieldType) FCFuncElement("GetToken", RuntimeFieldHandle::GetToken) FCFuncElement("GetStaticFieldForGenericType", RuntimeFieldHandle::GetStaticFieldForGenericType) FCFuncElement("AcquiresContextFromThis", RuntimeFieldHandle::AcquiresContextFromThis) diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index e3154c7b1334c5..b63d2ffa7beac5 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -293,6 +293,24 @@ class QCall } }; + struct FieldHandle + { + Object ** m_ppObject; + FieldDesc * m_pField; + + operator FieldDesc * () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + + FieldDesc * operator -> () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + }; + struct LoaderAllocatorHandle { LoaderAllocator * m_pLoaderAllocator; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 0f919f9ed91314..a2c96af36b6a2b 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -132,6 +132,7 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeModule_GetScopeName) DllImportEntry(RuntimeModule_GetFullyQualifiedName) DllImportEntry(RuntimeModule_GetTypes) + DllImportEntry(RuntimeFieldHandle_GetFieldSize) DllImportEntry(StackFrame_GetMethodDescFromNativeIP) DllImportEntry(ModuleBuilder_GetStringConstant) DllImportEntry(ModuleBuilder_GetTypeRef) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index e4ca976f2fd6c6..65192d5a68738a 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1259,6 +1259,19 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF } FCIMPLEND +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField) +{ + QCALL_CONTRACT; + + UINT ret = 0; + + BEGIN_QCALL; + ret = pField->LoadSize(); + END_QCALL; + + return ret; +} + extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD) { QCALL_CONTRACT; diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index 2817ef5a76ecee..c660763f11577b 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -2622,19 +2622,6 @@ FCIMPL1(ReflectClassBaseObject*, RuntimeFieldHandle::GetApproxDeclaringType, Fie } FCIMPLEND -FCIMPL1(void*, RuntimeFieldHandle::GetApproxFieldType, FieldDesc *pField) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - if (!pField) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - - return pField->GetApproxFieldTypeHandleThrowing().GetMethodTable(); -} -FCIMPLEND - FCIMPL1(INT32, RuntimeFieldHandle::GetToken, ReflectFieldObject *pFieldUNSAFE) { CONTRACTL { FCALL_CHECK; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index a763e33dbc2c62..e0504d952880d8 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -303,12 +303,12 @@ class RuntimeFieldHandle { static FCDECL1(INT32, GetAttributes, FieldDesc *pField); static FCDECL1(ReflectClassBaseObject*, GetApproxDeclaringType, FieldDesc *pField); - static FCDECL1(void*, GetApproxFieldType, FieldDesc *pField); static FCDECL1(INT32, GetToken, ReflectFieldObject *pFieldUNSAFE); static FCDECL2(FieldDesc*, GetStaticFieldForGenericType, FieldDesc *pField, ReflectClassBaseObject *pDeclaringType); static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField); }; +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField); class ModuleHandle { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs index 240e9932bf6244..698fb50375b859 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs @@ -95,4 +95,17 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth) _handle = rth.Value; } } + + // Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected + internal unsafe ref struct QCallFieldHandle + { + private void* _ptr; + private IntPtr _handle; + + internal QCallFieldHandle(ref IRuntimeFieldInfo field) + { + _ptr = Unsafe.AsPointer(ref field); + _handle = field.Value.Value; + } + } } From 522520381b4962809fe36221faaa38dcd70de461 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 29 May 2024 20:20:21 +0800 Subject: [PATCH 16/39] Fix CanAssignArrayType condition --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 8d165315e9e2b6..bbc882f0286dee 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -203,20 +203,20 @@ private static unsafe AssignArrayEnum CanAssignArrayType(Array sourceArray, Arra return AssignArrayEnum.AssignWrongType; } - // dest Object extends src + // src Object extends dest if (srcTH.CanCastTo(destTH)) return AssignArrayEnum.AssignSimpleCopy; - // src Object extends dest + // dest Object extends src if (destTH.CanCastTo(srcTH)) return AssignArrayEnum.AssignMustCast; // class X extends/implements src and implements dest. - if (!destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) return AssignArrayEnum.AssignMustCast; // class X implements src and extends/implements dest - if (!srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) return AssignArrayEnum.AssignMustCast; return AssignArrayEnum.AssignWrongType; From 07293e3a331c9274f6536683e12ed0728cafb5bb Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 29 May 2024 21:03:23 +0800 Subject: [PATCH 17/39] Fix compilation --- .../src/System/Runtime/CompilerServices/QCallHandles.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs index 698fb50375b859..e6ab54d12e5d7f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs @@ -102,10 +102,18 @@ internal unsafe ref struct QCallFieldHandle private void* _ptr; private IntPtr _handle; +#if CORECLR internal QCallFieldHandle(ref IRuntimeFieldInfo field) { _ptr = Unsafe.AsPointer(ref field); _handle = field.Value.Value; } +#endif + + internal QCallFieldHandle(ref RuntimeFieldHandle rth) + { + _ptr = Unsafe.AsPointer(ref rth); + _handle = rth.Value; + } } } From 5da88c33ad549c4e2d2f9f0e959652786c5edf5a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 29 May 2024 23:17:12 +0800 Subject: [PATCH 18/39] Simplify AssignType enum --- .../src/System/Array.CoreCLR.cs | 61 +++++++++---------- 1 file changed, 30 insertions(+), 31 deletions(-) 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 bbc882f0286dee..f8200f16d18879 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -62,10 +62,10 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - AssignArrayEnum assignType = AssignArrayEnum.AssignWrongType; + ArrayAssignType assignType = ArrayAssignType.WrongType; if (sourceArray.GetType() == destinationArray.GetType() - || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == AssignArrayEnum.AssignSimpleCopy) + || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -116,30 +116,30 @@ private static CorElementType GetNormalizedIntegralArrayElementType(CorElementTy // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, AssignArrayEnum assignType) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - if (assignType == AssignArrayEnum.AssignWrongType) + if (assignType == ArrayAssignType.WrongType) ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); if (length > 0) { switch (assignType) { - case AssignArrayEnum.AssignUnboxValueClass: + case ArrayAssignType.UnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignBoxValueClassOrPrimitive: + case ArrayAssignType.BoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignMustCast: + case ArrayAssignType.MustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignPrimitiveWiden: + case ArrayAssignType.PrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -150,43 +150,42 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - // Must match the definition in arraynative.cpp - private enum AssignArrayEnum + private enum ArrayAssignType { - AssignSimpleCopy, - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, + SimpleCopy, + WrongType, + MustCast, + BoxValueClassOrPrimitive, + UnboxValueClass, + PrimitiveWiden, } - private static unsafe AssignArrayEnum CanAssignArrayType(Array sourceArray, Array destinationArray) + private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) { TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions - return AssignArrayEnum.AssignSimpleCopy; + return ArrayAssignType.SimpleCopy; // Value class boxing if (srcTH.IsValueType && !destTH.IsValueType) { if (srcTH.CanCastTo(destTH)) - return AssignArrayEnum.AssignBoxValueClassOrPrimitive; + return ArrayAssignType.BoxValueClassOrPrimitive; else - return AssignArrayEnum.AssignWrongType; + return ArrayAssignType.WrongType; } // Value class unboxing. if (!srcTH.IsValueType && destTH.IsValueType) { if (srcTH.CanCastTo(destTH)) - return AssignArrayEnum.AssignUnboxValueClass; + return ArrayAssignType.UnboxValueClass; else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. - return AssignArrayEnum.AssignUnboxValueClass; + return ArrayAssignType.UnboxValueClass; else - return AssignArrayEnum.AssignWrongType; + return ArrayAssignType.WrongType; } CorElementType srcElType = srcTH.GetVerifierCorElementType(); @@ -196,30 +195,30 @@ private static unsafe AssignArrayEnum CanAssignArrayType(Array sourceArray, Arra if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) { if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - return AssignArrayEnum.AssignSimpleCopy; + return ArrayAssignType.SimpleCopy; else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) - return AssignArrayEnum.AssignPrimitiveWiden; + return ArrayAssignType.PrimitiveWiden; else - return AssignArrayEnum.AssignWrongType; + return ArrayAssignType.WrongType; } // src Object extends dest if (srcTH.CanCastTo(destTH)) - return AssignArrayEnum.AssignSimpleCopy; + return ArrayAssignType.SimpleCopy; // dest Object extends src if (destTH.CanCastTo(srcTH)) - return AssignArrayEnum.AssignMustCast; + return ArrayAssignType.MustCast; // class X extends/implements src and implements dest. if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return AssignArrayEnum.AssignMustCast; + return ArrayAssignType.MustCast; // class X implements src and extends/implements dest if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return AssignArrayEnum.AssignMustCast; + return ArrayAssignType.MustCast; - return AssignArrayEnum.AssignWrongType; + return ArrayAssignType.WrongType; } // Unboxes from an Object[] into a value class or primitive array. From 5222bcaeea5f7f654641c2704a3d0cbe71b8112c Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 29 May 2024 23:27:44 +0800 Subject: [PATCH 19/39] Fix I and U in CanPrimitiveWiden --- .../src/System/Runtime/CompilerServices/RuntimeHelpers.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 938cdeb5668332..3715e6df74a117 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -131,6 +131,11 @@ internal static bool IsPrimitiveType(this CorElementType et) internal static bool CanPrimitiveWiden(CorElementType srcET, CorElementType dstET) { Debug.Assert(srcET.IsPrimitiveType() && dstET.IsPrimitiveType()); + if ((int)srcET >= PrimitiveWidenTable.Length) + { + // I or U + return srcET == dstET; + } return (PrimitiveWidenTable[(int)srcET] & (1 << (int)dstET)) != 0; } From 335b2fb81588f052dc6b8767d9686ed6cba9c050 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 30 May 2024 18:33:25 +0800 Subject: [PATCH 20/39] CanCastTo should be QCall --- .../RuntimeHelpers.CoreCLR.cs | 12 +++++----- src/coreclr/vm/comutilnative.cpp | 22 +++++++++++++------ src/coreclr/vm/comutilnative.h | 2 +- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 + 5 files changed, 23 insertions(+), 15 deletions(-) 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 313e4da564c9aa..cd1dc4dc6472f7 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 @@ -807,10 +807,6 @@ public TypeHandle GetArrayElementTypeHandle() [MethodImpl(MethodImplOptions.InternalCall)] public extern CorElementType GetVerifierCorElementType(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); - } // Subset of src\vm\methodtable.h @@ -838,7 +834,7 @@ public bool CanCompareBitsOrUseFastGetHashCode /// /// A type handle, which can wrap either a pointer to a or to a . /// - internal readonly unsafe struct TypeHandle + internal readonly unsafe partial struct TypeHandle { // Subset of src\vm\typehandle.h @@ -908,9 +904,13 @@ public bool CanCastTo(TypeHandle destTH) if (result != CastResult.MaybeCast) return result == CastResult.CanCast; - return MethodTable.CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr); + return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr); } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeHandle_CanCastTo")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); + public bool IsValueType { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index f1c3209c86a511..1823ffa2c07ace 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1794,13 +1794,6 @@ FCIMPL1(CorElementType, MethodTableNative::GetVerifierCorElementType, MethodTabl } FCIMPLEND -FCIMPL2(FC_BOOL_RET, MethodTableNative::CanCastTo, void* fromType, void* toType) -{ - FCALL_CONTRACT; - FC_RETURN_BOOL(TypeHandle::FromPtr(fromType).CanCastTo(TypeHandle::FromPtr(toType))); -} -FCIMPLEND - extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb) { QCALL_CONTRACT; @@ -1816,6 +1809,21 @@ extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, Metho return bResult; } +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd) +{ + QCALL_CONTRACT; + + BOOL ret = false; + + BEGIN_QCALL; + + ret = TypeHandle::FromPtr(fromTypeHnd).CanCastTo(TypeHandle::FromPtr(toTypeHnd)); + + END_QCALL; + + return ret; +} + static MethodTable * g_pStreamMT; static WORD g_slotBeginRead, g_slotEndRead; static WORD g_slotBeginWrite, g_slotEndWrite; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index ad519082674899..fb1038d7f3f5db 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -245,11 +245,11 @@ class MethodTableNative { public: static FCDECL1(UINT32, GetNumInstanceFieldBytes, MethodTable* mt); static FCDECL1(CorElementType, GetVerifierCorElementType, MethodTable* mt); - static FCDECL2(FC_BOOL_RET, CanCastTo, void* fromType, void* toType); }; extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt); +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd); extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize, MethodTable** fieldMT); class StreamNative { diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index b33ad43eae218b..3ad62b22824ac1 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -449,7 +449,6 @@ FCFuncEnd() FCFuncStart(gMethodTableFuncs) FCFuncElement("GetNumInstanceFieldBytes", MethodTableNative::GetNumInstanceFieldBytes) FCFuncElement("GetVerifierCorElementType", MethodTableNative::GetVerifierCorElementType) - FCFuncElement("CanCastTo_NoCacheLookup", MethodTableNative::CanCastTo) FCFuncEnd() FCFuncStart(gStubHelperFuncs) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index a2c96af36b6a2b..05a62c15df1eb3 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -103,6 +103,7 @@ static const Entry s_QCall[] = DllImportEntry(QCall_FreeGCHandleForTypeHandle) DllImportEntry(MethodTable_AreTypesEquivalent) DllImportEntry(MethodTable_CanCompareBitsOrUseFastGetHashCode) + DllImportEntry(TypeHandle_CanCastTo) DllImportEntry(ValueType_GetHashCodeStrategy) DllImportEntry(RuntimeTypeHandle_MakePointer) DllImportEntry(RuntimeTypeHandle_MakeByRef) From 93ec9d8477c471c1a90634d04768676b1d40d574 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 31 May 2024 20:48:06 +0800 Subject: [PATCH 21/39] Remove ThrowHelper usages that not in hot path --- .../src/System/Array.CoreCLR.cs | 12 ++++----- .../RuntimeHelpers.CoreCLR.cs | 12 ++++----- .../src/System/ThrowHelper.cs | 27 ------------------- 3 files changed, 11 insertions(+), 40 deletions(-) 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 f8200f16d18879..7c3cad7d86637d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -121,7 +121,7 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de Debug.Assert(sourceArray.Rank == destinationArray.Rank); if (assignType == ArrayAssignType.WrongType) - ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); if (length > 0) { @@ -250,7 +250,7 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc } else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT) { - ThrowHelper.ThrowInvalidCastException_DownCastArrayElement(); + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); } else if (pDestMT->ContainsGCPointers) { @@ -679,9 +679,7 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex) else if (!pElementMethodTable->IsValueType) { if (CastHelpers.IsInstanceOfAny(pElementMethodTable, value) == null) - { - ThrowHelper.ThrowInvalidCastException_StoreArrayElement(); - } + throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); ref object? elementRef = ref Unsafe.As(ref arrayDataRef); ref object? offsetElementRef = ref Unsafe.Add(ref elementRef, (nuint)flattenedIndex); @@ -715,11 +713,11 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex) CorElementType targetType = pElementMethodTable->GetVerifierCorElementType(); if (!srcType.IsPrimitiveType() || !targetType.IsPrimitiveType() || !pElementMethodTable->IsTruePrimitive) - ThrowHelper.ThrowInvalidCastException_StoreArrayElement(); + throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); // Get a properly widened type if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType)) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_PrimitiveWiden); + throw new ArgumentException(SR.Arg_PrimWiden); PrimitiveWiden(ref value.GetRawData(), ref offsetDataRef, srcType, targetType); } 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 cd1dc4dc6472f7..51c60b74f41683 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 @@ -22,7 +22,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); // Note that we do not check that the field is actually in the PE file that is initializing // the array. Basically the data being published is can be accessed by anyone with the proper @@ -30,7 +30,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan // snooping) if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); MethodTable* pMT = GetMethodTable(array); nuint totalSize = pMT->ComponentSize * array.NativeLength; @@ -39,7 +39,7 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan // make certain you don't go off the end of the rva static if (totalSize > size) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); @@ -87,20 +87,20 @@ public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHan IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); if (data % targetTypeSize != 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); if (!BitConverter.IsLittleEndian) { diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 3997d47191fb83..f742fe7a77e1c9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -71,24 +71,6 @@ internal static void ThrowArrayTypeMismatchException() throw new ArrayTypeMismatchException(); } - [DoesNotReturn] - internal static void ThrowArrayTypeMismatchException_CantAssignType() - { - throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); - } - - [DoesNotReturn] - internal static void ThrowInvalidCastException_DownCastArrayElement() - { - throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); - } - - [DoesNotReturn] - internal static void ThrowInvalidCastException_StoreArrayElement() - { - throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); - } - [DoesNotReturn] internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { @@ -1250,12 +1232,6 @@ private static string GetResourceString(ExceptionResource resource) return SR.Format_ExpectedAsciiDigit; case ExceptionResource.Argument_HasToBeArrayClass: return SR.Argument_HasToBeArrayClass; - case ExceptionResource.Arg_PrimitiveWiden: - return SR.Arg_PrimWiden; - case ExceptionResource.Argument_MustBePrimitiveArray: - return SR.Argument_MustBePrimitiveArray; - case ExceptionResource.Argument_BadFieldForInitializeArray: - return SR.Argument_BadFieldForInitializeArray; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1452,8 +1428,5 @@ internal enum ExceptionResource Format_UnclosedFormatItem, Format_ExpectedAsciiDigit, Argument_HasToBeArrayClass, - Arg_PrimitiveWiden, - Argument_MustBePrimitiveArray, - Argument_BadFieldForInitializeArray, } } From 44445a316e790af41e6f0ee09b445bd4912e3a33 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 2 Jun 2024 13:50:58 +0800 Subject: [PATCH 22/39] Revert --- .../src/System/Array.CoreCLR.cs | 123 +++----------- .../classlibnative/bcltype/arraynative.cpp | 159 ++++++++++++++++++ .../classlibnative/bcltype/arraynative.h | 17 +- src/coreclr/vm/ecalllist.h | 4 + src/coreclr/vm/qcallentrypoints.cpp | 1 + 5 files changed, 204 insertions(+), 100 deletions(-) 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 7c3cad7d86637d..2ec3fa8ee934b1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -34,7 +34,6 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i fromArrayType: true, ObjectHandleOnStack.Create(ref retArray)); return retArray!; } - private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { if (sourceArray == null) @@ -62,10 +61,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - ArrayAssignType assignType = ArrayAssignType.WrongType; - - if (sourceArray.GetType() == destinationArray.GetType() - || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) + if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -89,57 +85,44 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); } - private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) - { - Debug.Assert(elementType.IsPrimitiveType()); - - // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable - // Enums with interchangeable underlying types are interchangeable - // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 - switch (elementType) - { - case CorElementType.ELEMENT_TYPE_U1: - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_U4: - case CorElementType.ELEMENT_TYPE_U8: - case CorElementType.ELEMENT_TYPE_U: - return elementType - 1; // normalize to signed type - default: - return elementType; - } - } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); // Reliability-wise, this method will either possibly corrupt your // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - if (assignType == ArrayAssignType.WrongType) + void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; + void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; + AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); + + if (r == AssignArrayEnum.AssignWrongType) throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); if (length > 0) { - switch (assignType) + switch (r) { - case ArrayAssignType.UnboxValueClass: + case AssignArrayEnum.AssignUnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.BoxValueClassOrPrimitive: + case AssignArrayEnum.AssignBoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.MustCast: + case AssignArrayEnum.AssignMustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.PrimitiveWiden: + case AssignArrayEnum.AssignPrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -150,76 +133,18 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - private enum ArrayAssignType + // Must match the definition in arraynative.cpp + private enum AssignArrayEnum { - SimpleCopy, - WrongType, - MustCast, - BoxValueClassOrPrimitive, - UnboxValueClass, - PrimitiveWiden, + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, } - private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) - { - TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); - TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); - - if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions - return ArrayAssignType.SimpleCopy; - - // Value class boxing - if (srcTH.IsValueType && !destTH.IsValueType) - { - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.BoxValueClassOrPrimitive; - else - return ArrayAssignType.WrongType; - } - - // Value class unboxing. - if (!srcTH.IsValueType && destTH.IsValueType) - { - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.UnboxValueClass; - else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. - return ArrayAssignType.UnboxValueClass; - else - return ArrayAssignType.WrongType; - } - - CorElementType srcElType = srcTH.GetVerifierCorElementType(); - CorElementType destElType = destTH.GetVerifierCorElementType(); - - // Copying primitives from one type to another - if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) - { - if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - return ArrayAssignType.SimpleCopy; - else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) - return ArrayAssignType.PrimitiveWiden; - else - return ArrayAssignType.WrongType; - } - - // src Object extends dest - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.SimpleCopy; - - // dest Object extends src - if (destTH.CanCastTo(srcTH)) - return ArrayAssignType.MustCast; - - // class X extends/implements src and implements dest. - if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return ArrayAssignType.MustCast; - - // class X implements src and extends/implements dest - if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return ArrayAssignType.MustCast; - - return ArrayAssignType.WrongType; - } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] + private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 246c5398276073..2e8a41a827910f 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -36,6 +36,165 @@ extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHand return ctorEntrypoint; } + // Returns whether you can directly copy an array of srcType into destType. +FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst) +{ + FCALL_CONTRACT; + + _ASSERTE(pSrc != NULL); + _ASSERTE(pDst != NULL); + + // This case is expected to be handled by the fast path + _ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable()); + + TypeHandle srcTH = pSrc->GetMethodTable()->GetArrayElementTypeHandle(); + TypeHandle destTH = pDst->GetMethodTable()->GetArrayElementTypeHandle(); + if (srcTH == destTH) // This check kicks for different array kind or dimensions + FC_RETURN_BOOL(true); + + if (srcTH.IsValueType()) + { + // Value class boxing + if (!destTH.IsValueType()) + FC_RETURN_BOOL(false); + + const CorElementType srcElType = srcTH.GetVerifierCorElementType(); + const CorElementType destElType = destTH.GetVerifierCorElementType(); + _ASSERTE(srcElType < ELEMENT_TYPE_MAX); + _ASSERTE(destElType < ELEMENT_TYPE_MAX); + + // Copying primitives from one type to another + if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) + { + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + FC_RETURN_BOOL(true); + } + } + else + { + // Value class unboxing + if (destTH.IsValueType()) + FC_RETURN_BOOL(false); + } + + TypeHandle::CastResult r = srcTH.CanCastToCached(destTH); + if (r != TypeHandle::MaybeCast) + { + FC_RETURN_BOOL(r); + } + + struct + { + OBJECTREF src; + OBJECTREF dst; + } gc; + + gc.src = ObjectToOBJECTREF(pSrc); + gc.dst = ObjectToOBJECTREF(pDst); + + BOOL iRetVal = FALSE; + + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + iRetVal = srcTH.CanCastTo(destTH); + HELPER_METHOD_FRAME_END(); + + FC_RETURN_BOOL(iRetVal); +} +FCIMPLEND + + +// Return values for CanAssignArrayType +enum AssignArrayEnum +{ + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, +}; + +// Returns an enum saying whether you can copy an array of srcType into destType. +static AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(!srcTH.IsNull()); + PRECONDITION(!destTH.IsNull()); + } + CONTRACTL_END; + + _ASSERTE(srcTH != destTH); // Handled by fast path + + // Value class boxing + if (srcTH.IsValueType() && !destTH.IsValueType()) + { + if (srcTH.CanCastTo(destTH)) + return AssignBoxValueClassOrPrimitive; + else + return AssignWrongType; + } + + // Value class unboxing. + if (!srcTH.IsValueType() && destTH.IsValueType()) + { + if (srcTH.CanCastTo(destTH)) + return AssignUnboxValueClass; + else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. + return AssignUnboxValueClass; + else + return AssignWrongType; + } + + const CorElementType srcElType = srcTH.GetVerifierCorElementType(); + const CorElementType destElType = destTH.GetVerifierCorElementType(); + _ASSERTE(srcElType < ELEMENT_TYPE_MAX); + _ASSERTE(destElType < ELEMENT_TYPE_MAX); + + // Copying primitives from one type to another + if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) + { + _ASSERTE(srcElType != destElType); // Handled by fast path + if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType)) + return AssignPrimitiveWiden; + else + return AssignWrongType; + } + + // dest Object extends src + _ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path + + // src Object extends dest + if (destTH.CanCastTo(srcTH)) + return AssignMustCast; + + // class X extends/implements src and implements dest. + if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE) + return AssignMustCast; + + // class X implements src and extends/implements dest + if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE) + return AssignMustCast; + + return AssignWrongType; +} + +extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) +{ + QCALL_CONTRACT; + + INT32 ret = 0; + + BEGIN_QCALL; + + ret = CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); + + END_QCALL; + + return ret; +} + // // This is a GC safe variant of the memmove intrinsic. It sets the cards, and guarantees that the object references in the GC heap are diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 5dd3570ce5cbba..19bc1a4fa85ac9 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -13,9 +13,24 @@ #ifndef _ARRAYNATIVE_H_ #define _ARRAYNATIVE_H_ -#include "qcall.h" +#include "fcall.h" +#include "runtimehandles.h" + +struct FCALLRuntimeFieldHandle +{ + ReflectFieldObject *pFieldDONOTUSEDIRECTLY; +}; +#define FCALL_RFH_TO_REFLECTFIELD(x) (x).pFieldDONOTUSEDIRECTLY + +class ArrayNative +{ +public: + + static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); +}; 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); +extern "C" INT32 QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH); #endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 3ad62b22824ac1..ee1393130bbbb9 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("IsSimpleCopy", ArrayNative::IsSimpleCopy) +FCFuncEnd() + FCFuncStart(gBufferFuncs) FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) FCFuncEnd() diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 05a62c15df1eb3..084e6df167aca1 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -173,6 +173,7 @@ static const Entry s_QCall[] = DllImportEntry(MdUtf8String_EqualsCaseInsensitive) DllImportEntry(Array_CreateInstance) DllImportEntry(Array_GetElementConstructorEntrypoint) + DllImportEntry(Array_CanAssignArrayType) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) DllImportEntry(AssemblyNative_GetLocation) From b59fa9dbf90d5fe54d76898c6715c508c129dbc1 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 2 Jun 2024 18:08:46 +0800 Subject: [PATCH 23/39] GetCorElementTypeOfElementType --- .../src/System/Array.CoreCLR.cs | 9 ++------- src/coreclr/classlibnative/bcltype/arraynative.cpp | 11 +++++++++++ src/coreclr/classlibnative/bcltype/arraynative.h | 1 + src/coreclr/vm/ecalllist.h | 2 ++ 4 files changed, 16 insertions(+), 7 deletions(-) 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 2ec3fa8ee934b1..ed9b78357f6e07 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -707,13 +707,8 @@ public unsafe int GetLowerBound(int dimension) return Unsafe.Add(ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this), rank + dimension); } - internal unsafe CorElementType GetCorElementTypeOfElementType() - { - MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - CorElementType et = pMT->GetArrayElementTypeHandle().GetVerifierCorElementType(); - GC.KeepAlive(this); - return et; - } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern CorElementType GetCorElementTypeOfElementType(); private unsafe bool IsValueOfElementType(object value) { diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 2e8a41a827910f..7a42245f76888a 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -17,6 +17,17 @@ #include "arraynative.inl" +// Returns a bool to indicate if the array is of primitive types or not. +FCIMPL1(INT32, ArrayNative::GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE) +{ + FCALL_CONTRACT; + + _ASSERTE(arrayUNSAFE != NULL); + + return arrayUNSAFE->GetArrayElementTypeHandle().GetVerifierCorElementType(); +} +FCIMPLEND + extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd) { QCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 19bc1a4fa85ac9..56a1dd5b379549 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -25,6 +25,7 @@ struct FCALLRuntimeFieldHandle class ArrayNative { public: + static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); }; diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index ee1393130bbbb9..244982efc6f019 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -360,6 +360,7 @@ FCFuncStart(gCastHelpers) FCFuncEnd() FCFuncStart(gArrayFuncs) + FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) FCFuncEnd() @@ -535,6 +536,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) From 7ffe745d88a59de2492bf4c2d95a165c2b6bbdfb Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 2 Jun 2024 22:17:00 +0800 Subject: [PATCH 24/39] InternalSetValue --- .../src/System/Array.CoreCLR.cs | 94 +------------------ .../classlibnative/bcltype/arraynative.cpp | 87 +++++++++++++++++ .../classlibnative/bcltype/arraynative.h | 3 + src/coreclr/vm/ecalllist.h | 1 + 4 files changed, 93 insertions(+), 92 deletions(-) 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 ed9b78357f6e07..c67a10ee04ca36 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -558,98 +558,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/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 7a42245f76888a..2c56c99b108171 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -365,3 +365,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 56a1dd5b379549..6ad5ad4bb4c150 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -28,6 +28,9 @@ class ArrayNative static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); + + // 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); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 244982efc6f019..0b183fe9493629 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -362,6 +362,7 @@ FCFuncEnd() FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) + FCFuncElement("InternalSetValue", ArrayNative::SetValue) FCFuncEnd() FCFuncStart(gBufferFuncs) From b610b35ff3999d5c30d05780b14f1128d7908133 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 3 Jun 2024 05:38:10 +0800 Subject: [PATCH 25/39] Revert "GetCorElementTypeOfElementType" This reverts commit b59fa9dbf90d5fe54d76898c6715c508c129dbc1. --- .../src/System/Array.CoreCLR.cs | 9 +++++++-- src/coreclr/classlibnative/bcltype/arraynative.cpp | 11 ----------- src/coreclr/classlibnative/bcltype/arraynative.h | 1 - src/coreclr/vm/ecalllist.h | 2 -- 4 files changed, 7 insertions(+), 16 deletions(-) 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 c67a10ee04ca36..60a1eb628566a3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -617,8 +617,13 @@ public unsafe int GetLowerBound(int dimension) return Unsafe.Add(ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this), rank + dimension); } - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern CorElementType GetCorElementTypeOfElementType(); + internal unsafe CorElementType GetCorElementTypeOfElementType() + { + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + CorElementType et = pMT->GetArrayElementTypeHandle().GetVerifierCorElementType(); + GC.KeepAlive(this); + return et; + } private unsafe bool IsValueOfElementType(object value) { diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 2c56c99b108171..2e4f61e934f240 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -17,17 +17,6 @@ #include "arraynative.inl" -// Returns a bool to indicate if the array is of primitive types or not. -FCIMPL1(INT32, ArrayNative::GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE) -{ - FCALL_CONTRACT; - - _ASSERTE(arrayUNSAFE != NULL); - - return arrayUNSAFE->GetArrayElementTypeHandle().GetVerifierCorElementType(); -} -FCIMPLEND - extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd) { QCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 6ad5ad4bb4c150..15fd5c3daea511 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -25,7 +25,6 @@ struct FCALLRuntimeFieldHandle class ArrayNative { public: - static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 0b183fe9493629..e17df7c465b9d0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -360,7 +360,6 @@ FCFuncStart(gCastHelpers) FCFuncEnd() FCFuncStart(gArrayFuncs) - FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) FCFuncElement("InternalSetValue", ArrayNative::SetValue) FCFuncEnd() @@ -537,7 +536,6 @@ 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) From 3e350ac26e79c27a99c13e738b883ec09446eb34 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 3 Jun 2024 17:59:06 +0800 Subject: [PATCH 26/39] Go with CorElementType only --- .../src/System/Array.CoreCLR.cs | 290 +++++++++--------- .../Runtime/CompilerServices/CastHelpers.cs | 2 +- .../RuntimeHelpers.CoreCLR.cs | 101 +----- .../src/System/RuntimeHandles.cs | 7 +- .../classlibnative/bcltype/arraynative.cpp | 113 +++++++ .../classlibnative/bcltype/arraynative.h | 8 + src/coreclr/vm/comutilnative.cpp | 15 - src/coreclr/vm/comutilnative.h | 1 - src/coreclr/vm/ecalllist.h | 3 + src/coreclr/vm/qcall.h | 18 -- src/coreclr/vm/qcallentrypoints.cpp | 2 - src/coreclr/vm/reflectioninvocation.cpp | 27 +- src/coreclr/vm/runtimehandles.h | 1 - .../Runtime/CompilerServices/QCallHandles.cs | 21 -- .../CompilerServices/RuntimeHelpers.cs | 30 -- .../src/System/ThrowHelper.cs | 12 + 16 files changed, 295 insertions(+), 356 deletions(-) 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 60a1eb628566a3..4d793d17aba18f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -34,6 +34,7 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i fromArrayType: true, ObjectHandleOnStack.Create(ref retArray)); return retArray!; } + private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { if (sourceArray == null) @@ -104,7 +105,7 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); if (r == AssignArrayEnum.AssignWrongType) - throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); if (length > 0) { @@ -175,7 +176,7 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc } else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT) { - throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + ThrowHelper.ThrowInvalidCastException_DownCastArrayElement(); } else if (pDestMT->ContainsGCPointers) { @@ -248,152 +249,147 @@ private static unsafe void CopyImplPrimitiveWiden(Array sourceArray, int sourceI ref byte srcElement = ref Unsafe.Add(ref srcData, (nuint)i * srcElSize); ref byte destElement = ref Unsafe.Add(ref data, (nuint)i * destElSize); - PrimitiveWiden(ref srcElement, ref destElement, srcElType, destElType); - } - } + switch (srcElType) + { + case CorElementType.ELEMENT_TYPE_U1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_I2: + case CorElementType.ELEMENT_TYPE_U2: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = srcElement; break; + default: + Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; + } + break; - private static void PrimitiveWiden(ref byte srcElement, ref byte destElement, CorElementType srcElType, CorElementType destElType) - { - switch (srcElType) - { - case CorElementType.ELEMENT_TYPE_U1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_CHAR: - case CorElementType.ELEMENT_TYPE_I2: - case CorElementType.ELEMENT_TYPE_U2: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = srcElement; break; - default: - Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I2: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - // U2 and CHAR are identical in conversion - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I2: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_R4: - Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - - default: - Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; + case CorElementType.ELEMENT_TYPE_I1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I2: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + // U2 and CHAR are identical in conversion + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I2: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_R4: + Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + + default: + Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; + } } } 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 14c8a959cda5b0..0c640dd1c2f0a4 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/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 51c60b74f41683..a8cbf2ae7969ac 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 @@ -1,115 +1,28 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if (array is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included - throw new ArgumentException(SR.Argument_MustBePrimitiveArray); - - MethodTable* pMT = GetMethodTable(array); - nuint totalSize = pMT->ComponentSize * array.NativeLength; - - uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - - // make certain you don't go off the end of the rva static - if (totalSize > size) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); - - Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); - - if (BitConverter.IsLittleEndian) - { - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - } - else - { - switch (pMT->ComponentSize) - { - case 1: - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - break; - case 2: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 4: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 8: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - default: - Debug.Fail("Incorrect primitive type size!"); - break; - } - } - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); - private static unsafe void* GetSpanDataFrom( + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); - Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter - - if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included - throw new ArgumentException(SR.Argument_MustBePrimitiveArray); - - uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); - - IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - if (data % targetTypeSize != 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - if (!BitConverter.IsLittleEndian) - { - throw new PlatformNotSupportedException(); - } - - count = (int)(totalSize / targetTypeSize); - return (void*)data; - } + out int count); // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 472f4f6318a3dc..380981993451e9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa } [NonVersionable] - public unsafe partial struct RuntimeFieldHandle : IEquatable, ISerializable + public unsafe struct RuntimeFieldHandle : IEquatable, ISerializable { // Returns handle for interop with EE. The handle is guaranteed to be non-null. internal RuntimeFieldHandle GetNativeHandle() @@ -1187,10 +1187,7 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) internal static extern int GetInstanceFieldOffset(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")] - internal static partial uint GetFieldSize(QCallFieldHandle field); + internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 2e4f61e934f240..a4b458b8ef5712 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -441,3 +441,116 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE } } FCIMPLEND + +// This method will initialize an array from a TypeHandle to a field. + +FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) +{ + FCALL_CONTRACT; + + BASEARRAYREF arr = BASEARRAYREF(pArrayRef); + REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + HELPER_METHOD_FRAME_BEGIN_2(arr, refField); + + if ((arr == 0) || (refField == NULL)) + COMPlusThrow(kArgumentNullException); + + FieldDesc* pField = (FieldDesc*) refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) + COMPlusThrow(kArgumentException); + + SIZE_T dwCompSize = arr->GetComponentSize(); + SIZE_T dwElemCnt = arr->GetNumComponents(); + SIZE_T dwTotalSize = dwCompSize * dwElemCnt; + + DWORD size = pField->LoadSize(); + + // make certain you don't go off the end of the rva static + if (dwTotalSize > size) + COMPlusThrow(kArgumentException); + + void *src = pField->GetStaticAddressHandle(NULL); + void *dest = arr->GetDataPtr(); + +#if BIGENDIAN + DWORD i; + switch (dwCompSize) { + case 1: + memcpyNoGCRefs(dest, src, dwElemCnt); + break; + case 2: + for (i = 0; i < dwElemCnt; i++) + *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); + break; + case 4: + for (i = 0; i < dwElemCnt; i++) + *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); + break; + case 8: + for (i = 0; i < dwElemCnt; i++) + *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); + break; + default: + // should not reach here. + UNREACHABLE_MSG("Incorrect primitive type size!"); + break; + } +#else + memcpyNoGCRefs(dest, src, dwTotalSize); +#endif + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) +{ + FCALL_CONTRACT; + struct + { + REFLECTFIELDREF refField; + REFLECTCLASSBASEREF refClass; + } gc; + gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); + void* data = NULL; + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + + FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + TypeHandle targetTypeHandle = gc.refClass->GetType(); + if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) + COMPlusThrow(kArgumentException); + + DWORD totalSize = pField->LoadSize(); + DWORD targetTypeSize = targetTypeHandle.GetSize(); + + data = pField->GetStaticAddressHandle(NULL); + _ASSERTE(data != NULL); + _ASSERTE(count != NULL); + + if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) + COMPlusThrow(kArgumentException); + + *count = (INT32)totalSize / targetTypeSize; + +#if BIGENDIAN + COMPlusThrow(kPlatformNotSupportedException); +#endif + + HELPER_METHOD_FRAME_END(); + return data; +} +FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 15fd5c3daea511..508cd776ddff7a 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -30,6 +30,14 @@ class ArrayNative // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); + + // This method will initialize an array from a TypeHandle + // to a field. + static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); + + // This method will acquire data to create a span from a TypeHandle + // to a field. + static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 1823ffa2c07ace..b569143bbaa515 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1809,21 +1809,6 @@ extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, Metho return bResult; } -extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd) -{ - QCALL_CONTRACT; - - BOOL ret = false; - - BEGIN_QCALL; - - ret = TypeHandle::FromPtr(fromTypeHnd).CanCastTo(TypeHandle::FromPtr(toTypeHnd)); - - END_QCALL; - - return ret; -} - static MethodTable * g_pStreamMT; static WORD g_slotBeginRead, g_slotEndRead; static WORD g_slotBeginWrite, g_slotEndWrite; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index fb1038d7f3f5db..74f0c7967e7450 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -249,7 +249,6 @@ class MethodTableNative { extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt); -extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd); extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize, MethodTable** fieldMT); class StreamNative { diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index e17df7c465b9d0..7d7681894bef7f 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -439,6 +439,8 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) + FCFuncElement("InitializeArray", ArrayNative::InitializeArray) + FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) @@ -536,6 +538,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) diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index b63d2ffa7beac5..e3154c7b1334c5 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -293,24 +293,6 @@ class QCall } }; - struct FieldHandle - { - Object ** m_ppObject; - FieldDesc * m_pField; - - operator FieldDesc * () - { - LIMITED_METHOD_CONTRACT; - return m_pField; - } - - FieldDesc * operator -> () - { - LIMITED_METHOD_CONTRACT; - return m_pField; - } - }; - struct LoaderAllocatorHandle { LoaderAllocator * m_pLoaderAllocator; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 084e6df167aca1..e038db62831d93 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -103,7 +103,6 @@ static const Entry s_QCall[] = DllImportEntry(QCall_FreeGCHandleForTypeHandle) DllImportEntry(MethodTable_AreTypesEquivalent) DllImportEntry(MethodTable_CanCompareBitsOrUseFastGetHashCode) - DllImportEntry(TypeHandle_CanCastTo) DllImportEntry(ValueType_GetHashCodeStrategy) DllImportEntry(RuntimeTypeHandle_MakePointer) DllImportEntry(RuntimeTypeHandle_MakeByRef) @@ -133,7 +132,6 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeModule_GetScopeName) DllImportEntry(RuntimeModule_GetFullyQualifiedName) DllImportEntry(RuntimeModule_GetTypes) - DllImportEntry(RuntimeFieldHandle_GetFieldSize) DllImportEntry(StackFrame_GetMethodDescFromNativeIP) DllImportEntry(ModuleBuilder_GetStringConstant) DllImportEntry(ModuleBuilder_GetTypeRef) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 65192d5a68738a..f57e21801253a3 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1246,31 +1246,16 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF // IsFastPathSupported needs to checked before calling this method. _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); - if (pFieldDesc->IsRVA()) + PTR_BYTE base = 0; + if (!pFieldDesc->IsRVA()) { - Module* pModule = pFieldDesc->GetModule(); - return pModule->GetRvaField(pFieldDesc->GetOffset()); + // For RVA the base is ignored and offset is used. + base = pFieldDesc->GetBase(); } - else - { - PTR_BYTE base = pFieldDesc->GetBase(); - return PTR_VOID(base + pFieldDesc->GetOffset()); - } -} -FCIMPLEND -extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField) -{ - QCALL_CONTRACT; - - UINT ret = 0; - - BEGIN_QCALL; - ret = pField->LoadSize(); - END_QCALL; - - return ret; + return PTR_VOID(base + pFieldDesc->GetOffset()); } +FCIMPLEND extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD) { diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index e0504d952880d8..56bb2df245f9f7 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -308,7 +308,6 @@ class RuntimeFieldHandle { static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField); }; -extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField); class ModuleHandle { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs index e6ab54d12e5d7f..240e9932bf6244 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs @@ -95,25 +95,4 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth) _handle = rth.Value; } } - - // Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected - internal unsafe ref struct QCallFieldHandle - { - private void* _ptr; - private IntPtr _handle; - -#if CORECLR - internal QCallFieldHandle(ref IRuntimeFieldInfo field) - { - _ptr = Unsafe.AsPointer(ref field); - _handle = field.Value.Value; - } -#endif - - internal QCallFieldHandle(ref RuntimeFieldHandle rth) - { - _ptr = Unsafe.AsPointer(ref rth); - _handle = rth.Value; - } - } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 3715e6df74a117..63471d9f919675 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; @@ -110,35 +109,6 @@ internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; - private static ReadOnlySpan PrimitiveWidenTable => - [ - 0x00, // ELEMENT_TYPE_END - 0x00, // ELEMENT_TYPE_VOID - 0x0004, // ELEMENT_TYPE_BOOLEAN - 0x3F88, // ELEMENT_TYPE_CHAR (W = U2, CHAR, I4, U4, I8, U8, R4, R8) (U2 == Char) - 0x3550, // ELEMENT_TYPE_I1 (W = I1, I2, I4, I8, R4, R8) - 0x3FE8, // ELEMENT_TYPE_U1 (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8) - 0x3540, // ELEMENT_TYPE_I2 (W = I2, I4, I8, R4, R8) - 0x3F88, // ELEMENT_TYPE_U2 (W = U2, CHAR, I4, U4, I8, U8, R4, R8) - 0x3500, // ELEMENT_TYPE_I4 (W = I4, I8, R4, R8) - 0x3E00, // ELEMENT_TYPE_U4 (W = U4, I8, R4, R8) - 0x3400, // ELEMENT_TYPE_I8 (W = I8, R4, R8) - 0x3800, // ELEMENT_TYPE_U8 (W = U8, R4, R8) - 0x3000, // ELEMENT_TYPE_R4 (W = R4, R8) - 0x2000, // ELEMENT_TYPE_R8 (W = R8) - ]; - - internal static bool CanPrimitiveWiden(CorElementType srcET, CorElementType dstET) - { - Debug.Assert(srcET.IsPrimitiveType() && dstET.IsPrimitiveType()); - if ((int)srcET >= PrimitiveWidenTable.Length) - { - // I or U - return srcET == dstET; - } - return (PrimitiveWidenTable[(int)srcET] & (1 << (int)dstET)) != 0; - } - /// Provide a fast way to access constant data stored in a module as a ReadOnlySpan{T} /// A field handle that specifies the location of the data to be referred to by the ReadOnlySpan{T}. The Rva of the field must be aligned on a natural boundary of type T /// A ReadOnlySpan{T} of the data stored in the field diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index f742fe7a77e1c9..eb9b5b7eb02618 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -71,6 +71,18 @@ internal static void ThrowArrayTypeMismatchException() throw new ArrayTypeMismatchException(); } + [DoesNotReturn] + internal static void ThrowArrayTypeMismatchException_CantAssignType() + { + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + + [DoesNotReturn] + internal static void ThrowInvalidCastException_DownCastArrayElement() + { + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + } + [DoesNotReturn] internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { From 0112e24d0e42e2d04381340a36c852e14268195a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 4 Jun 2024 01:15:44 +0800 Subject: [PATCH 27/39] Add InitializeArray --- .../RuntimeHelpers.CoreCLR.cs | 101 ++++++++++++++-- .../src/System/RuntimeHandles.cs | 7 +- .../classlibnative/bcltype/arraynative.cpp | 113 ------------------ .../classlibnative/bcltype/arraynative.h | 7 -- src/coreclr/vm/ecalllist.h | 2 - src/coreclr/vm/qcall.h | 18 +++ src/coreclr/vm/qcallentrypoints.cpp | 1 + src/coreclr/vm/reflectioninvocation.cpp | 27 ++++- src/coreclr/vm/runtimehandles.h | 1 + .../Runtime/CompilerServices/QCallHandles.cs | 21 ++++ 10 files changed, 161 insertions(+), 137 deletions(-) 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 a8cbf2ae7969ac..51c60b74f41683 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 @@ -1,28 +1,115 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetSpanDataFrom( + if (array is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); + + MethodTable* pMT = GetMethodTable(array); + nuint totalSize = pMT->ComponentSize * array.NativeLength; + + uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + + // make certain you don't go off the end of the rva static + if (totalSize > size) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); + + Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); + + if (BitConverter.IsLittleEndian) + { + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + } + else + { + switch (pMT->ComponentSize) + { + case 1: + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + break; + case 2: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 4: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 8: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + default: + Debug.Fail("Incorrect primitive type size!"); + break; + } + } + } + + private static unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count); + out int count) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); + Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter + + if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); + + uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); + + IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + if (data % targetTypeSize != 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + if (!BitConverter.IsLittleEndian) + { + throw new PlatformNotSupportedException(); + } + + count = (int)(totalSize / targetTypeSize); + return (void*)data; + } // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 380981993451e9..472f4f6318a3dc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa } [NonVersionable] - public unsafe struct RuntimeFieldHandle : IEquatable, ISerializable + public unsafe partial struct RuntimeFieldHandle : IEquatable, ISerializable { // Returns handle for interop with EE. The handle is guaranteed to be non-null. internal RuntimeFieldHandle GetNativeHandle() @@ -1187,7 +1187,10 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) internal static extern int GetInstanceFieldOffset(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field); + internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")] + internal static partial uint GetFieldSize(QCallFieldHandle field); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index a4b458b8ef5712..2e4f61e934f240 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -441,116 +441,3 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE } } FCIMPLEND - -// This method will initialize an array from a TypeHandle to a field. - -FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) -{ - FCALL_CONTRACT; - - BASEARRAYREF arr = BASEARRAYREF(pArrayRef); - REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - HELPER_METHOD_FRAME_BEGIN_2(arr, refField); - - if ((arr == 0) || (refField == NULL)) - COMPlusThrow(kArgumentNullException); - - FieldDesc* pField = (FieldDesc*) refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) - COMPlusThrow(kArgumentException); - - SIZE_T dwCompSize = arr->GetComponentSize(); - SIZE_T dwElemCnt = arr->GetNumComponents(); - SIZE_T dwTotalSize = dwCompSize * dwElemCnt; - - DWORD size = pField->LoadSize(); - - // make certain you don't go off the end of the rva static - if (dwTotalSize > size) - COMPlusThrow(kArgumentException); - - void *src = pField->GetStaticAddressHandle(NULL); - void *dest = arr->GetDataPtr(); - -#if BIGENDIAN - DWORD i; - switch (dwCompSize) { - case 1: - memcpyNoGCRefs(dest, src, dwElemCnt); - break; - case 2: - for (i = 0; i < dwElemCnt; i++) - *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); - break; - case 4: - for (i = 0; i < dwElemCnt; i++) - *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); - break; - case 8: - for (i = 0; i < dwElemCnt; i++) - *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); - break; - default: - // should not reach here. - UNREACHABLE_MSG("Incorrect primitive type size!"); - break; - } -#else - memcpyNoGCRefs(dest, src, dwTotalSize); -#endif - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) -{ - FCALL_CONTRACT; - struct - { - REFLECTFIELDREF refField; - REFLECTCLASSBASEREF refClass; - } gc; - gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); - void* data = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - TypeHandle targetTypeHandle = gc.refClass->GetType(); - if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) - COMPlusThrow(kArgumentException); - - DWORD totalSize = pField->LoadSize(); - DWORD targetTypeSize = targetTypeHandle.GetSize(); - - data = pField->GetStaticAddressHandle(NULL); - _ASSERTE(data != NULL); - _ASSERTE(count != NULL); - - if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) - COMPlusThrow(kArgumentException); - - *count = (INT32)totalSize / targetTypeSize; - -#if BIGENDIAN - COMPlusThrow(kPlatformNotSupportedException); -#endif - - HELPER_METHOD_FRAME_END(); - return data; -} -FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 508cd776ddff7a..409bca1e7408ea 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -31,13 +31,6 @@ class ArrayNative // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); - // This method will initialize an array from a TypeHandle - // to a field. - static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); - - // This method will acquire data to create a span from a TypeHandle - // to a field. - static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 7d7681894bef7f..67b855835deb07 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -439,8 +439,6 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) - FCFuncElement("InitializeArray", ArrayNative::InitializeArray) - FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index e3154c7b1334c5..b63d2ffa7beac5 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -293,6 +293,24 @@ class QCall } }; + struct FieldHandle + { + Object ** m_ppObject; + FieldDesc * m_pField; + + operator FieldDesc * () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + + FieldDesc * operator -> () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + }; + struct LoaderAllocatorHandle { LoaderAllocator * m_pLoaderAllocator; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e038db62831d93..71f7883fa15e68 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -132,6 +132,7 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeModule_GetScopeName) DllImportEntry(RuntimeModule_GetFullyQualifiedName) DllImportEntry(RuntimeModule_GetTypes) + DllImportEntry(RuntimeFieldHandle_GetFieldSize) DllImportEntry(StackFrame_GetMethodDescFromNativeIP) DllImportEntry(ModuleBuilder_GetStringConstant) DllImportEntry(ModuleBuilder_GetTypeRef) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index f57e21801253a3..65192d5a68738a 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1246,17 +1246,32 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF // IsFastPathSupported needs to checked before calling this method. _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); - PTR_BYTE base = 0; - if (!pFieldDesc->IsRVA()) + if (pFieldDesc->IsRVA()) { - // For RVA the base is ignored and offset is used. - base = pFieldDesc->GetBase(); + Module* pModule = pFieldDesc->GetModule(); + return pModule->GetRvaField(pFieldDesc->GetOffset()); + } + else + { + PTR_BYTE base = pFieldDesc->GetBase(); + return PTR_VOID(base + pFieldDesc->GetOffset()); } - - return PTR_VOID(base + pFieldDesc->GetOffset()); } FCIMPLEND +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField) +{ + QCALL_CONTRACT; + + UINT ret = 0; + + BEGIN_QCALL; + ret = pField->LoadSize(); + END_QCALL; + + return ret; +} + extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD) { QCALL_CONTRACT; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 56bb2df245f9f7..e0504d952880d8 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -308,6 +308,7 @@ class RuntimeFieldHandle { static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField); }; +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField); class ModuleHandle { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs index 240e9932bf6244..e6ab54d12e5d7f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs @@ -95,4 +95,25 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth) _handle = rth.Value; } } + + // Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected + internal unsafe ref struct QCallFieldHandle + { + private void* _ptr; + private IntPtr _handle; + +#if CORECLR + internal QCallFieldHandle(ref IRuntimeFieldInfo field) + { + _ptr = Unsafe.AsPointer(ref field); + _handle = field.Value.Value; + } +#endif + + internal QCallFieldHandle(ref RuntimeFieldHandle rth) + { + _ptr = Unsafe.AsPointer(ref rth); + _handle = rth.Value; + } + } } From 5841ed8240b5d3158e01ccd25951d45dbcaae70f Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 4 Jun 2024 10:22:50 +0800 Subject: [PATCH 28/39] Revert "Add InitializeArray" This reverts commit 0112e24d0e42e2d04381340a36c852e14268195a. --- .../RuntimeHelpers.CoreCLR.cs | 101 ++-------------- .../src/System/RuntimeHandles.cs | 7 +- .../classlibnative/bcltype/arraynative.cpp | 113 ++++++++++++++++++ .../classlibnative/bcltype/arraynative.h | 7 ++ src/coreclr/vm/ecalllist.h | 2 + src/coreclr/vm/qcall.h | 18 --- src/coreclr/vm/qcallentrypoints.cpp | 1 - src/coreclr/vm/reflectioninvocation.cpp | 27 +---- src/coreclr/vm/runtimehandles.h | 1 - .../Runtime/CompilerServices/QCallHandles.cs | 21 ---- 10 files changed, 137 insertions(+), 161 deletions(-) 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 51c60b74f41683..a8cbf2ae7969ac 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 @@ -1,115 +1,28 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if (array is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included - throw new ArgumentException(SR.Argument_MustBePrimitiveArray); - - MethodTable* pMT = GetMethodTable(array); - nuint totalSize = pMT->ComponentSize * array.NativeLength; - - uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - - // make certain you don't go off the end of the rva static - if (totalSize > size) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); - - Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); - - if (BitConverter.IsLittleEndian) - { - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - } - else - { - switch (pMT->ComponentSize) - { - case 1: - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - break; - case 2: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 4: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 8: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - default: - Debug.Fail("Incorrect primitive type size!"); - break; - } - } - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); - private static unsafe void* GetSpanDataFrom( + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); - Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter - - if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included - throw new ArgumentException(SR.Argument_MustBePrimitiveArray); - - uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); - - IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - if (data % targetTypeSize != 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - if (!BitConverter.IsLittleEndian) - { - throw new PlatformNotSupportedException(); - } - - count = (int)(totalSize / targetTypeSize); - return (void*)data; - } + out int count); // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 472f4f6318a3dc..380981993451e9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa } [NonVersionable] - public unsafe partial struct RuntimeFieldHandle : IEquatable, ISerializable + public unsafe struct RuntimeFieldHandle : IEquatable, ISerializable { // Returns handle for interop with EE. The handle is guaranteed to be non-null. internal RuntimeFieldHandle GetNativeHandle() @@ -1187,10 +1187,7 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) internal static extern int GetInstanceFieldOffset(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")] - internal static partial uint GetFieldSize(QCallFieldHandle field); + internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 2e4f61e934f240..a4b458b8ef5712 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -441,3 +441,116 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE } } FCIMPLEND + +// This method will initialize an array from a TypeHandle to a field. + +FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) +{ + FCALL_CONTRACT; + + BASEARRAYREF arr = BASEARRAYREF(pArrayRef); + REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + HELPER_METHOD_FRAME_BEGIN_2(arr, refField); + + if ((arr == 0) || (refField == NULL)) + COMPlusThrow(kArgumentNullException); + + FieldDesc* pField = (FieldDesc*) refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) + COMPlusThrow(kArgumentException); + + SIZE_T dwCompSize = arr->GetComponentSize(); + SIZE_T dwElemCnt = arr->GetNumComponents(); + SIZE_T dwTotalSize = dwCompSize * dwElemCnt; + + DWORD size = pField->LoadSize(); + + // make certain you don't go off the end of the rva static + if (dwTotalSize > size) + COMPlusThrow(kArgumentException); + + void *src = pField->GetStaticAddressHandle(NULL); + void *dest = arr->GetDataPtr(); + +#if BIGENDIAN + DWORD i; + switch (dwCompSize) { + case 1: + memcpyNoGCRefs(dest, src, dwElemCnt); + break; + case 2: + for (i = 0; i < dwElemCnt; i++) + *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); + break; + case 4: + for (i = 0; i < dwElemCnt; i++) + *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); + break; + case 8: + for (i = 0; i < dwElemCnt; i++) + *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); + break; + default: + // should not reach here. + UNREACHABLE_MSG("Incorrect primitive type size!"); + break; + } +#else + memcpyNoGCRefs(dest, src, dwTotalSize); +#endif + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) +{ + FCALL_CONTRACT; + struct + { + REFLECTFIELDREF refField; + REFLECTCLASSBASEREF refClass; + } gc; + gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); + void* data = NULL; + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + + FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + TypeHandle targetTypeHandle = gc.refClass->GetType(); + if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) + COMPlusThrow(kArgumentException); + + DWORD totalSize = pField->LoadSize(); + DWORD targetTypeSize = targetTypeHandle.GetSize(); + + data = pField->GetStaticAddressHandle(NULL); + _ASSERTE(data != NULL); + _ASSERTE(count != NULL); + + if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) + COMPlusThrow(kArgumentException); + + *count = (INT32)totalSize / targetTypeSize; + +#if BIGENDIAN + COMPlusThrow(kPlatformNotSupportedException); +#endif + + HELPER_METHOD_FRAME_END(); + return data; +} +FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 409bca1e7408ea..508cd776ddff7a 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -31,6 +31,13 @@ class ArrayNative // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); + // This method will initialize an array from a TypeHandle + // to a field. + static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); + + // This method will acquire data to create a span from a TypeHandle + // to a field. + static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 67b855835deb07..7d7681894bef7f 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -439,6 +439,8 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) + FCFuncElement("InitializeArray", ArrayNative::InitializeArray) + FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index b63d2ffa7beac5..e3154c7b1334c5 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -293,24 +293,6 @@ class QCall } }; - struct FieldHandle - { - Object ** m_ppObject; - FieldDesc * m_pField; - - operator FieldDesc * () - { - LIMITED_METHOD_CONTRACT; - return m_pField; - } - - FieldDesc * operator -> () - { - LIMITED_METHOD_CONTRACT; - return m_pField; - } - }; - struct LoaderAllocatorHandle { LoaderAllocator * m_pLoaderAllocator; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 71f7883fa15e68..e038db62831d93 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -132,7 +132,6 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeModule_GetScopeName) DllImportEntry(RuntimeModule_GetFullyQualifiedName) DllImportEntry(RuntimeModule_GetTypes) - DllImportEntry(RuntimeFieldHandle_GetFieldSize) DllImportEntry(StackFrame_GetMethodDescFromNativeIP) DllImportEntry(ModuleBuilder_GetStringConstant) DllImportEntry(ModuleBuilder_GetTypeRef) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 65192d5a68738a..f57e21801253a3 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1246,31 +1246,16 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF // IsFastPathSupported needs to checked before calling this method. _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); - if (pFieldDesc->IsRVA()) + PTR_BYTE base = 0; + if (!pFieldDesc->IsRVA()) { - Module* pModule = pFieldDesc->GetModule(); - return pModule->GetRvaField(pFieldDesc->GetOffset()); + // For RVA the base is ignored and offset is used. + base = pFieldDesc->GetBase(); } - else - { - PTR_BYTE base = pFieldDesc->GetBase(); - return PTR_VOID(base + pFieldDesc->GetOffset()); - } -} -FCIMPLEND -extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField) -{ - QCALL_CONTRACT; - - UINT ret = 0; - - BEGIN_QCALL; - ret = pField->LoadSize(); - END_QCALL; - - return ret; + return PTR_VOID(base + pFieldDesc->GetOffset()); } +FCIMPLEND extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD) { diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index e0504d952880d8..56bb2df245f9f7 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -308,7 +308,6 @@ class RuntimeFieldHandle { static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField); }; -extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField); class ModuleHandle { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs index e6ab54d12e5d7f..240e9932bf6244 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs @@ -95,25 +95,4 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth) _handle = rth.Value; } } - - // Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected - internal unsafe ref struct QCallFieldHandle - { - private void* _ptr; - private IntPtr _handle; - -#if CORECLR - internal QCallFieldHandle(ref IRuntimeFieldInfo field) - { - _ptr = Unsafe.AsPointer(ref field); - _handle = field.Value.Value; - } -#endif - - internal QCallFieldHandle(ref RuntimeFieldHandle rth) - { - _ptr = Unsafe.AsPointer(ref rth); - _handle = rth.Value; - } - } } From a795d59037402a988fb3a1ffbcd6a5566ca5ae19 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 4 Jun 2024 10:28:13 +0800 Subject: [PATCH 29/39] Add CopySlow and AssignType --- .../src/System/Array.CoreCLR.cs | 407 +++++++++++------- .../Runtime/CompilerServices/CastHelpers.cs | 2 +- src/coreclr/vm/comutilnative.cpp | 15 + src/coreclr/vm/comutilnative.h | 1 + src/coreclr/vm/qcallentrypoints.cpp | 1 + .../CompilerServices/RuntimeHelpers.cs | 30 ++ 6 files changed, 291 insertions(+), 165 deletions(-) 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 4d793d17aba18f..5a66a0dd45b81c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -62,7 +62,10 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) + ArrayAssignType assignType = ArrayAssignType.WrongType; + + if (sourceArray.GetType() == destinationArray.GetType() + || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -86,44 +89,57 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); + private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) + { + Debug.Assert(elementType.IsPrimitiveType()); + + // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable + // Enums with interchangeable underlying types are interchangeable + // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 + switch (elementType) + { + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_U4: + case CorElementType.ELEMENT_TYPE_U8: + case CorElementType.ELEMENT_TYPE_U: + return elementType - 1; // normalize to signed type + default: + return elementType; + } + } // Reliability-wise, this method will either possibly corrupt your // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; - void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; - AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); - - if (r == AssignArrayEnum.AssignWrongType) + if (assignType == ArrayAssignType.WrongType) ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); if (length > 0) { - switch (r) + switch (assignType) { - case AssignArrayEnum.AssignUnboxValueClass: + case ArrayAssignType.UnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignBoxValueClassOrPrimitive: + case ArrayAssignType.BoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignMustCast: + case ArrayAssignType.MustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignPrimitiveWiden: + case ArrayAssignType.PrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -134,18 +150,76 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - // Must match the definition in arraynative.cpp - private enum AssignArrayEnum + private enum ArrayAssignType { - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, + SimpleCopy, + WrongType, + MustCast, + BoxValueClassOrPrimitive, + UnboxValueClass, + PrimitiveWiden, } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] - private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); + private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) + { + TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + + if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions + return ArrayAssignType.SimpleCopy; + + // Value class boxing + if (srcTH.IsValueType && !destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.BoxValueClassOrPrimitive; + else + return ArrayAssignType.WrongType; + } + + // Value class unboxing. + if (!srcTH.IsValueType && destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.UnboxValueClass; + else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. + return ArrayAssignType.UnboxValueClass; + else + return ArrayAssignType.WrongType; + } + + CorElementType srcElType = srcTH.GetVerifierCorElementType(); + CorElementType destElType = destTH.GetVerifierCorElementType(); + + // Copying primitives from one type to another + if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) + { + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + return ArrayAssignType.SimpleCopy; + else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) + return ArrayAssignType.PrimitiveWiden; + else + return ArrayAssignType.WrongType; + } + + // src Object extends dest + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.SimpleCopy; + + // dest Object extends src + if (destTH.CanCastTo(srcTH)) + return ArrayAssignType.MustCast; + + // class X extends/implements src and implements dest. + if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return ArrayAssignType.MustCast; + + // class X implements src and extends/implements dest + if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return ArrayAssignType.MustCast; + + return ArrayAssignType.WrongType; + } // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) @@ -249,147 +323,152 @@ private static unsafe void CopyImplPrimitiveWiden(Array sourceArray, int sourceI ref byte srcElement = ref Unsafe.Add(ref srcData, (nuint)i * srcElSize); ref byte destElement = ref Unsafe.Add(ref data, (nuint)i * destElSize); - switch (srcElType) - { - case CorElementType.ELEMENT_TYPE_U1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_CHAR: - case CorElementType.ELEMENT_TYPE_I2: - case CorElementType.ELEMENT_TYPE_U2: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = srcElement; break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = srcElement; break; - default: - Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I2: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - // U2 and CHAR are identical in conversion - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I2: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - case CorElementType.ELEMENT_TYPE_R8: - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; - default: - Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_R4: - Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); - Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + PrimitiveWiden(ref srcElement, ref destElement, srcElType, destElType); + } + } - default: - Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; - } + private static void PrimitiveWiden(ref byte srcElement, ref byte destElement, CorElementType srcElType, CorElementType destElType) + { + switch (srcElType) + { + case CorElementType.ELEMENT_TYPE_U1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_I2: + case CorElementType.ELEMENT_TYPE_U2: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = srcElement; break; + default: + Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I2: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + // U2 and CHAR are identical in conversion + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I2: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_R4: + Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + + default: + Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; } } 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 0c640dd1c2f0a4..14c8a959cda5b0 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] - private static object? IsInstanceOfAny(void* toTypeHnd, object? obj) + internal static object? IsInstanceOfAny(void* toTypeHnd, object? obj) { if (obj != null) { diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index b569143bbaa515..1823ffa2c07ace 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1809,6 +1809,21 @@ extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, Metho return bResult; } +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd) +{ + QCALL_CONTRACT; + + BOOL ret = false; + + BEGIN_QCALL; + + ret = TypeHandle::FromPtr(fromTypeHnd).CanCastTo(TypeHandle::FromPtr(toTypeHnd)); + + END_QCALL; + + return ret; +} + static MethodTable * g_pStreamMT; static WORD g_slotBeginRead, g_slotEndRead; static WORD g_slotBeginWrite, g_slotEndWrite; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 74f0c7967e7450..fb1038d7f3f5db 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -249,6 +249,7 @@ class MethodTableNative { extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt); +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd); extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize, MethodTable** fieldMT); class StreamNative { diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e038db62831d93..9cd32a2d892593 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -103,6 +103,7 @@ static const Entry s_QCall[] = DllImportEntry(QCall_FreeGCHandleForTypeHandle) DllImportEntry(MethodTable_AreTypesEquivalent) DllImportEntry(MethodTable_CanCompareBitsOrUseFastGetHashCode) + DllImportEntry(TypeHandle_CanCastTo) DllImportEntry(ValueType_GetHashCodeStrategy) DllImportEntry(RuntimeTypeHandle_MakePointer) DllImportEntry(RuntimeTypeHandle_MakeByRef) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 63471d9f919675..3715e6df74a117 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; @@ -109,6 +110,35 @@ internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; + private static ReadOnlySpan PrimitiveWidenTable => + [ + 0x00, // ELEMENT_TYPE_END + 0x00, // ELEMENT_TYPE_VOID + 0x0004, // ELEMENT_TYPE_BOOLEAN + 0x3F88, // ELEMENT_TYPE_CHAR (W = U2, CHAR, I4, U4, I8, U8, R4, R8) (U2 == Char) + 0x3550, // ELEMENT_TYPE_I1 (W = I1, I2, I4, I8, R4, R8) + 0x3FE8, // ELEMENT_TYPE_U1 (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8) + 0x3540, // ELEMENT_TYPE_I2 (W = I2, I4, I8, R4, R8) + 0x3F88, // ELEMENT_TYPE_U2 (W = U2, CHAR, I4, U4, I8, U8, R4, R8) + 0x3500, // ELEMENT_TYPE_I4 (W = I4, I8, R4, R8) + 0x3E00, // ELEMENT_TYPE_U4 (W = U4, I8, R4, R8) + 0x3400, // ELEMENT_TYPE_I8 (W = I8, R4, R8) + 0x3800, // ELEMENT_TYPE_U8 (W = U8, R4, R8) + 0x3000, // ELEMENT_TYPE_R4 (W = R4, R8) + 0x2000, // ELEMENT_TYPE_R8 (W = R8) + ]; + + internal static bool CanPrimitiveWiden(CorElementType srcET, CorElementType dstET) + { + Debug.Assert(srcET.IsPrimitiveType() && dstET.IsPrimitiveType()); + if ((int)srcET >= PrimitiveWidenTable.Length) + { + // I or U + return srcET == dstET; + } + return (PrimitiveWidenTable[(int)srcET] & (1 << (int)dstET)) != 0; + } + /// Provide a fast way to access constant data stored in a module as a ReadOnlySpan{T} /// A field handle that specifies the location of the data to be referred to by the ReadOnlySpan{T}. The Rva of the field must be aligned on a natural boundary of type T /// A ReadOnlySpan{T} of the data stored in the field From b300d25d0b9e7c725a60b0e8be83d09f6e766fa9 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 5 Jun 2024 00:15:05 +0800 Subject: [PATCH 30/39] Go back to known broken commit --- .../src/System/Array.CoreCLR.cs | 127 ++++-------------- .../RuntimeHelpers.CoreCLR.cs | 101 +++++++++++++- .../src/System/RuntimeHandles.cs | 7 +- .../classlibnative/bcltype/arraynative.cpp | 113 ---------------- .../classlibnative/bcltype/arraynative.h | 8 -- src/coreclr/vm/ecalllist.h | 3 - src/coreclr/vm/qcall.h | 18 +++ src/coreclr/vm/qcallentrypoints.cpp | 1 + src/coreclr/vm/reflectioninvocation.cpp | 27 +++- src/coreclr/vm/runtimehandles.h | 1 + .../Runtime/CompilerServices/QCallHandles.cs | 21 +++ .../src/System/ThrowHelper.cs | 12 -- 12 files changed, 187 insertions(+), 252 deletions(-) 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 5a66a0dd45b81c..60a1eb628566a3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -34,7 +34,6 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i fromArrayType: true, ObjectHandleOnStack.Create(ref retArray)); return retArray!; } - private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { if (sourceArray == null) @@ -62,10 +61,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - ArrayAssignType assignType = ArrayAssignType.WrongType; - - if (sourceArray.GetType() == destinationArray.GetType() - || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) + if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -89,57 +85,44 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); } - private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) - { - Debug.Assert(elementType.IsPrimitiveType()); - - // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable - // Enums with interchangeable underlying types are interchangeable - // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 - switch (elementType) - { - case CorElementType.ELEMENT_TYPE_U1: - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_U4: - case CorElementType.ELEMENT_TYPE_U8: - case CorElementType.ELEMENT_TYPE_U: - return elementType - 1; // normalize to signed type - default: - return elementType; - } - } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); // Reliability-wise, this method will either possibly corrupt your // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - if (assignType == ArrayAssignType.WrongType) - ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); + void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; + void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; + AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); + + if (r == AssignArrayEnum.AssignWrongType) + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); if (length > 0) { - switch (assignType) + switch (r) { - case ArrayAssignType.UnboxValueClass: + case AssignArrayEnum.AssignUnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.BoxValueClassOrPrimitive: + case AssignArrayEnum.AssignBoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.MustCast: + case AssignArrayEnum.AssignMustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.PrimitiveWiden: + case AssignArrayEnum.AssignPrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -150,76 +133,18 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - private enum ArrayAssignType + // Must match the definition in arraynative.cpp + private enum AssignArrayEnum { - SimpleCopy, - WrongType, - MustCast, - BoxValueClassOrPrimitive, - UnboxValueClass, - PrimitiveWiden, + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, } - private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) - { - TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); - TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); - - if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions - return ArrayAssignType.SimpleCopy; - - // Value class boxing - if (srcTH.IsValueType && !destTH.IsValueType) - { - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.BoxValueClassOrPrimitive; - else - return ArrayAssignType.WrongType; - } - - // Value class unboxing. - if (!srcTH.IsValueType && destTH.IsValueType) - { - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.UnboxValueClass; - else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. - return ArrayAssignType.UnboxValueClass; - else - return ArrayAssignType.WrongType; - } - - CorElementType srcElType = srcTH.GetVerifierCorElementType(); - CorElementType destElType = destTH.GetVerifierCorElementType(); - - // Copying primitives from one type to another - if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) - { - if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - return ArrayAssignType.SimpleCopy; - else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) - return ArrayAssignType.PrimitiveWiden; - else - return ArrayAssignType.WrongType; - } - - // src Object extends dest - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.SimpleCopy; - - // dest Object extends src - if (destTH.CanCastTo(srcTH)) - return ArrayAssignType.MustCast; - - // class X extends/implements src and implements dest. - if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return ArrayAssignType.MustCast; - - // class X implements src and extends/implements dest - if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return ArrayAssignType.MustCast; - - return ArrayAssignType.WrongType; - } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] + private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) @@ -250,7 +175,7 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc } else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT) { - ThrowHelper.ThrowInvalidCastException_DownCastArrayElement(); + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); } else if (pDestMT->ContainsGCPointers) { 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 a8cbf2ae7969ac..51c60b74f41683 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 @@ -1,28 +1,115 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetSpanDataFrom( + if (array is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); + + MethodTable* pMT = GetMethodTable(array); + nuint totalSize = pMT->ComponentSize * array.NativeLength; + + uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + + // make certain you don't go off the end of the rva static + if (totalSize > size) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); + + Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); + + if (BitConverter.IsLittleEndian) + { + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + } + else + { + switch (pMT->ComponentSize) + { + case 1: + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + break; + case 2: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 4: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 8: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + default: + Debug.Fail("Incorrect primitive type size!"); + break; + } + } + } + + private static unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count); + out int count) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); + Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter + + if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); + + uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); + + IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + if (data % targetTypeSize != 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + if (!BitConverter.IsLittleEndian) + { + throw new PlatformNotSupportedException(); + } + + count = (int)(totalSize / targetTypeSize); + return (void*)data; + } // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 380981993451e9..472f4f6318a3dc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa } [NonVersionable] - public unsafe struct RuntimeFieldHandle : IEquatable, ISerializable + public unsafe partial struct RuntimeFieldHandle : IEquatable, ISerializable { // Returns handle for interop with EE. The handle is guaranteed to be non-null. internal RuntimeFieldHandle GetNativeHandle() @@ -1187,7 +1187,10 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) internal static extern int GetInstanceFieldOffset(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field); + internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")] + internal static partial uint GetFieldSize(QCallFieldHandle field); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index a4b458b8ef5712..2e4f61e934f240 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -441,116 +441,3 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE } } FCIMPLEND - -// This method will initialize an array from a TypeHandle to a field. - -FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) -{ - FCALL_CONTRACT; - - BASEARRAYREF arr = BASEARRAYREF(pArrayRef); - REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - HELPER_METHOD_FRAME_BEGIN_2(arr, refField); - - if ((arr == 0) || (refField == NULL)) - COMPlusThrow(kArgumentNullException); - - FieldDesc* pField = (FieldDesc*) refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) - COMPlusThrow(kArgumentException); - - SIZE_T dwCompSize = arr->GetComponentSize(); - SIZE_T dwElemCnt = arr->GetNumComponents(); - SIZE_T dwTotalSize = dwCompSize * dwElemCnt; - - DWORD size = pField->LoadSize(); - - // make certain you don't go off the end of the rva static - if (dwTotalSize > size) - COMPlusThrow(kArgumentException); - - void *src = pField->GetStaticAddressHandle(NULL); - void *dest = arr->GetDataPtr(); - -#if BIGENDIAN - DWORD i; - switch (dwCompSize) { - case 1: - memcpyNoGCRefs(dest, src, dwElemCnt); - break; - case 2: - for (i = 0; i < dwElemCnt; i++) - *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); - break; - case 4: - for (i = 0; i < dwElemCnt; i++) - *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); - break; - case 8: - for (i = 0; i < dwElemCnt; i++) - *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); - break; - default: - // should not reach here. - UNREACHABLE_MSG("Incorrect primitive type size!"); - break; - } -#else - memcpyNoGCRefs(dest, src, dwTotalSize); -#endif - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) -{ - FCALL_CONTRACT; - struct - { - REFLECTFIELDREF refField; - REFLECTCLASSBASEREF refClass; - } gc; - gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); - void* data = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - TypeHandle targetTypeHandle = gc.refClass->GetType(); - if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) - COMPlusThrow(kArgumentException); - - DWORD totalSize = pField->LoadSize(); - DWORD targetTypeSize = targetTypeHandle.GetSize(); - - data = pField->GetStaticAddressHandle(NULL); - _ASSERTE(data != NULL); - _ASSERTE(count != NULL); - - if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) - COMPlusThrow(kArgumentException); - - *count = (INT32)totalSize / targetTypeSize; - -#if BIGENDIAN - COMPlusThrow(kPlatformNotSupportedException); -#endif - - HELPER_METHOD_FRAME_END(); - return data; -} -FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 508cd776ddff7a..15fd5c3daea511 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -30,14 +30,6 @@ class ArrayNative // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); - - // This method will initialize an array from a TypeHandle - // to a field. - static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); - - // This method will acquire data to create a span from a TypeHandle - // to a field. - static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 7d7681894bef7f..e17df7c465b9d0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -439,8 +439,6 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) - FCFuncElement("InitializeArray", ArrayNative::InitializeArray) - FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) @@ -538,7 +536,6 @@ 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) diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index e3154c7b1334c5..b63d2ffa7beac5 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -293,6 +293,24 @@ class QCall } }; + struct FieldHandle + { + Object ** m_ppObject; + FieldDesc * m_pField; + + operator FieldDesc * () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + + FieldDesc * operator -> () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + }; + struct LoaderAllocatorHandle { LoaderAllocator * m_pLoaderAllocator; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 9cd32a2d892593..084e6df167aca1 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -133,6 +133,7 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeModule_GetScopeName) DllImportEntry(RuntimeModule_GetFullyQualifiedName) DllImportEntry(RuntimeModule_GetTypes) + DllImportEntry(RuntimeFieldHandle_GetFieldSize) DllImportEntry(StackFrame_GetMethodDescFromNativeIP) DllImportEntry(ModuleBuilder_GetStringConstant) DllImportEntry(ModuleBuilder_GetTypeRef) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index f57e21801253a3..65192d5a68738a 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1246,17 +1246,32 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF // IsFastPathSupported needs to checked before calling this method. _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); - PTR_BYTE base = 0; - if (!pFieldDesc->IsRVA()) + if (pFieldDesc->IsRVA()) { - // For RVA the base is ignored and offset is used. - base = pFieldDesc->GetBase(); + Module* pModule = pFieldDesc->GetModule(); + return pModule->GetRvaField(pFieldDesc->GetOffset()); + } + else + { + PTR_BYTE base = pFieldDesc->GetBase(); + return PTR_VOID(base + pFieldDesc->GetOffset()); } - - return PTR_VOID(base + pFieldDesc->GetOffset()); } FCIMPLEND +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField) +{ + QCALL_CONTRACT; + + UINT ret = 0; + + BEGIN_QCALL; + ret = pField->LoadSize(); + END_QCALL; + + return ret; +} + extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD) { QCALL_CONTRACT; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 56bb2df245f9f7..e0504d952880d8 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -308,6 +308,7 @@ class RuntimeFieldHandle { static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField); }; +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField); class ModuleHandle { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs index 240e9932bf6244..e6ab54d12e5d7f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs @@ -95,4 +95,25 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth) _handle = rth.Value; } } + + // Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected + internal unsafe ref struct QCallFieldHandle + { + private void* _ptr; + private IntPtr _handle; + +#if CORECLR + internal QCallFieldHandle(ref IRuntimeFieldInfo field) + { + _ptr = Unsafe.AsPointer(ref field); + _handle = field.Value.Value; + } +#endif + + internal QCallFieldHandle(ref RuntimeFieldHandle rth) + { + _ptr = Unsafe.AsPointer(ref rth); + _handle = rth.Value; + } + } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index eb9b5b7eb02618..f742fe7a77e1c9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -71,18 +71,6 @@ internal static void ThrowArrayTypeMismatchException() throw new ArrayTypeMismatchException(); } - [DoesNotReturn] - internal static void ThrowArrayTypeMismatchException_CantAssignType() - { - throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); - } - - [DoesNotReturn] - internal static void ThrowInvalidCastException_DownCastArrayElement() - { - throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); - } - [DoesNotReturn] internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { From e369ca9d5100ea2f699d60da46fac12e2a04da34 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 5 Jun 2024 02:00:36 +0800 Subject: [PATCH 31/39] Exclude InitializeArray --- .../RuntimeHelpers.CoreCLR.cs | 101 ++-------------- .../classlibnative/bcltype/arraynative.cpp | 113 ++++++++++++++++++ .../classlibnative/bcltype/arraynative.h | 8 ++ src/coreclr/vm/ecalllist.h | 2 + 4 files changed, 130 insertions(+), 94 deletions(-) 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 51c60b74f41683..a8cbf2ae7969ac 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 @@ -1,115 +1,28 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if (array is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included - throw new ArgumentException(SR.Argument_MustBePrimitiveArray); - - MethodTable* pMT = GetMethodTable(array); - nuint totalSize = pMT->ComponentSize * array.NativeLength; - - uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - - // make certain you don't go off the end of the rva static - if (totalSize > size) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); - - Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); - - if (BitConverter.IsLittleEndian) - { - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - } - else - { - switch (pMT->ComponentSize) - { - case 1: - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - break; - case 2: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 4: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 8: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - default: - Debug.Fail("Incorrect primitive type size!"); - break; - } - } - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); - private static unsafe void* GetSpanDataFrom( + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); - Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter - - if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included - throw new ArgumentException(SR.Argument_MustBePrimitiveArray); - - uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); - - IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - if (data % targetTypeSize != 0) - throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); - - if (!BitConverter.IsLittleEndian) - { - throw new PlatformNotSupportedException(); - } - - count = (int)(totalSize / targetTypeSize); - return (void*)data; - } + out int count); // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 2e4f61e934f240..a4b458b8ef5712 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -441,3 +441,116 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE } } FCIMPLEND + +// This method will initialize an array from a TypeHandle to a field. + +FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) +{ + FCALL_CONTRACT; + + BASEARRAYREF arr = BASEARRAYREF(pArrayRef); + REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + HELPER_METHOD_FRAME_BEGIN_2(arr, refField); + + if ((arr == 0) || (refField == NULL)) + COMPlusThrow(kArgumentNullException); + + FieldDesc* pField = (FieldDesc*) refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) + COMPlusThrow(kArgumentException); + + SIZE_T dwCompSize = arr->GetComponentSize(); + SIZE_T dwElemCnt = arr->GetNumComponents(); + SIZE_T dwTotalSize = dwCompSize * dwElemCnt; + + DWORD size = pField->LoadSize(); + + // make certain you don't go off the end of the rva static + if (dwTotalSize > size) + COMPlusThrow(kArgumentException); + + void *src = pField->GetStaticAddressHandle(NULL); + void *dest = arr->GetDataPtr(); + +#if BIGENDIAN + DWORD i; + switch (dwCompSize) { + case 1: + memcpyNoGCRefs(dest, src, dwElemCnt); + break; + case 2: + for (i = 0; i < dwElemCnt; i++) + *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); + break; + case 4: + for (i = 0; i < dwElemCnt; i++) + *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); + break; + case 8: + for (i = 0; i < dwElemCnt; i++) + *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); + break; + default: + // should not reach here. + UNREACHABLE_MSG("Incorrect primitive type size!"); + break; + } +#else + memcpyNoGCRefs(dest, src, dwTotalSize); +#endif + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) +{ + FCALL_CONTRACT; + struct + { + REFLECTFIELDREF refField; + REFLECTCLASSBASEREF refClass; + } gc; + gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); + void* data = NULL; + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + + FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + TypeHandle targetTypeHandle = gc.refClass->GetType(); + if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) + COMPlusThrow(kArgumentException); + + DWORD totalSize = pField->LoadSize(); + DWORD targetTypeSize = targetTypeHandle.GetSize(); + + data = pField->GetStaticAddressHandle(NULL); + _ASSERTE(data != NULL); + _ASSERTE(count != NULL); + + if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) + COMPlusThrow(kArgumentException); + + *count = (INT32)totalSize / targetTypeSize; + +#if BIGENDIAN + COMPlusThrow(kPlatformNotSupportedException); +#endif + + HELPER_METHOD_FRAME_END(); + return data; +} +FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 15fd5c3daea511..508cd776ddff7a 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -30,6 +30,14 @@ class ArrayNative // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); + + // This method will initialize an array from a TypeHandle + // to a field. + static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); + + // This method will acquire data to create a span from a TypeHandle + // to a field. + static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index e17df7c465b9d0..fe92e3c9171848 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -439,6 +439,8 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) + FCFuncElement("InitializeArray", ArrayNative::InitializeArray) + FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) From 68a48f7f3324bac7221300c44332373415c5bbc1 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 5 Jun 2024 13:51:24 +0800 Subject: [PATCH 32/39] Fix FCall --- src/coreclr/vm/ecalllist.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index fe92e3c9171848..7d7681894bef7f 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -538,6 +538,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) From ea361ca1865ee1a048e15e494188ece168638d59 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 5 Jun 2024 20:56:56 +0800 Subject: [PATCH 33/39] Revert to original failing commit --- .../src/System/Array.CoreCLR.cs | 223 +++++++++-- .../RuntimeHelpers.CoreCLR.cs | 101 ++++- .../classlibnative/bcltype/arraynative.cpp | 359 ------------------ .../classlibnative/bcltype/arraynative.h | 28 +- src/coreclr/vm/ecalllist.h | 8 - src/coreclr/vm/qcallentrypoints.cpp | 1 - .../src/System/ThrowHelper.cs | 27 ++ 7 files changed, 317 insertions(+), 430 deletions(-) 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 60a1eb628566a3..f8200f16d18879 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -34,6 +34,7 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i fromArrayType: true, ObjectHandleOnStack.Create(ref retArray)); return retArray!; } + private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { if (sourceArray == null) @@ -61,7 +62,10 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) + ArrayAssignType assignType = ArrayAssignType.WrongType; + + if (sourceArray.GetType() == destinationArray.GetType() + || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -85,44 +89,57 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); + private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) + { + Debug.Assert(elementType.IsPrimitiveType()); + + // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable + // Enums with interchangeable underlying types are interchangeable + // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 + switch (elementType) + { + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_U4: + case CorElementType.ELEMENT_TYPE_U8: + case CorElementType.ELEMENT_TYPE_U: + return elementType - 1; // normalize to signed type + default: + return elementType; + } + } // Reliability-wise, this method will either possibly corrupt your // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; - void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; - AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); - - if (r == AssignArrayEnum.AssignWrongType) - throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + if (assignType == ArrayAssignType.WrongType) + ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); if (length > 0) { - switch (r) + switch (assignType) { - case AssignArrayEnum.AssignUnboxValueClass: + case ArrayAssignType.UnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignBoxValueClassOrPrimitive: + case ArrayAssignType.BoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignMustCast: + case ArrayAssignType.MustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignPrimitiveWiden: + case ArrayAssignType.PrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -133,18 +150,76 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - // Must match the definition in arraynative.cpp - private enum AssignArrayEnum + private enum ArrayAssignType { - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, + SimpleCopy, + WrongType, + MustCast, + BoxValueClassOrPrimitive, + UnboxValueClass, + PrimitiveWiden, } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] - private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); + private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) + { + TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + + if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions + return ArrayAssignType.SimpleCopy; + + // Value class boxing + if (srcTH.IsValueType && !destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.BoxValueClassOrPrimitive; + else + return ArrayAssignType.WrongType; + } + + // Value class unboxing. + if (!srcTH.IsValueType && destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.UnboxValueClass; + else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. + return ArrayAssignType.UnboxValueClass; + else + return ArrayAssignType.WrongType; + } + + CorElementType srcElType = srcTH.GetVerifierCorElementType(); + CorElementType destElType = destTH.GetVerifierCorElementType(); + + // Copying primitives from one type to another + if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) + { + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + return ArrayAssignType.SimpleCopy; + else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) + return ArrayAssignType.PrimitiveWiden; + else + return ArrayAssignType.WrongType; + } + + // src Object extends dest + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.SimpleCopy; + + // dest Object extends src + if (destTH.CanCastTo(srcTH)) + return ArrayAssignType.MustCast; + + // class X extends/implements src and implements dest. + if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return ArrayAssignType.MustCast; + + // class X implements src and extends/implements dest + if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return ArrayAssignType.MustCast; + + return ArrayAssignType.WrongType; + } // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) @@ -175,7 +250,7 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc } else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT) { - throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + ThrowHelper.ThrowInvalidCastException_DownCastArrayElement(); } else if (pDestMT->ContainsGCPointers) { @@ -558,8 +633,100 @@ private unsafe nint GetFlattenedIndex(ReadOnlySpan indices) return result; } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InternalSetValue(object? value, nint flattenedIndex); + 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) + { + ThrowHelper.ThrowInvalidCastException_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) + ThrowHelper.ThrowInvalidCastException_StoreArrayElement(); + + // Get a properly widened type + if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_PrimitiveWiden); + + PrimitiveWiden(ref value.GetRawData(), ref offsetDataRef, srcType, targetType); + } + } + + GC.KeepAlive(this); // Keep the method table alive + } public int Length => checked((int)Unsafe.As(this).Length); 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 a8cbf2ae7969ac..cd1dc4dc6472f7 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 @@ -1,28 +1,115 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetSpanDataFrom( + if (array is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); + + MethodTable* pMT = GetMethodTable(array); + nuint totalSize = pMT->ComponentSize * array.NativeLength; + + uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + + // make certain you don't go off the end of the rva static + if (totalSize > size) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); + + Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); + + if (BitConverter.IsLittleEndian) + { + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + } + else + { + switch (pMT->ComponentSize) + { + case 1: + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + break; + case 2: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 4: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 8: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + default: + Debug.Fail("Incorrect primitive type size!"); + break; + } + } + } + + private static unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count); + out int count) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); + Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter + + if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); + + uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); + + IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + if (data % targetTypeSize != 0) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); + + if (!BitConverter.IsLittleEndian) + { + throw new PlatformNotSupportedException(); + } + + count = (int)(totalSize / targetTypeSize); + return (void*)data; + } // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index a4b458b8ef5712..246c5398276073 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -36,165 +36,6 @@ extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHand return ctorEntrypoint; } - // Returns whether you can directly copy an array of srcType into destType. -FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst) -{ - FCALL_CONTRACT; - - _ASSERTE(pSrc != NULL); - _ASSERTE(pDst != NULL); - - // This case is expected to be handled by the fast path - _ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable()); - - TypeHandle srcTH = pSrc->GetMethodTable()->GetArrayElementTypeHandle(); - TypeHandle destTH = pDst->GetMethodTable()->GetArrayElementTypeHandle(); - if (srcTH == destTH) // This check kicks for different array kind or dimensions - FC_RETURN_BOOL(true); - - if (srcTH.IsValueType()) - { - // Value class boxing - if (!destTH.IsValueType()) - FC_RETURN_BOOL(false); - - const CorElementType srcElType = srcTH.GetVerifierCorElementType(); - const CorElementType destElType = destTH.GetVerifierCorElementType(); - _ASSERTE(srcElType < ELEMENT_TYPE_MAX); - _ASSERTE(destElType < ELEMENT_TYPE_MAX); - - // Copying primitives from one type to another - if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) - { - if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - FC_RETURN_BOOL(true); - } - } - else - { - // Value class unboxing - if (destTH.IsValueType()) - FC_RETURN_BOOL(false); - } - - TypeHandle::CastResult r = srcTH.CanCastToCached(destTH); - if (r != TypeHandle::MaybeCast) - { - FC_RETURN_BOOL(r); - } - - struct - { - OBJECTREF src; - OBJECTREF dst; - } gc; - - gc.src = ObjectToOBJECTREF(pSrc); - gc.dst = ObjectToOBJECTREF(pDst); - - BOOL iRetVal = FALSE; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - iRetVal = srcTH.CanCastTo(destTH); - HELPER_METHOD_FRAME_END(); - - FC_RETURN_BOOL(iRetVal); -} -FCIMPLEND - - -// Return values for CanAssignArrayType -enum AssignArrayEnum -{ - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, -}; - -// Returns an enum saying whether you can copy an array of srcType into destType. -static AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - PRECONDITION(!srcTH.IsNull()); - PRECONDITION(!destTH.IsNull()); - } - CONTRACTL_END; - - _ASSERTE(srcTH != destTH); // Handled by fast path - - // Value class boxing - if (srcTH.IsValueType() && !destTH.IsValueType()) - { - if (srcTH.CanCastTo(destTH)) - return AssignBoxValueClassOrPrimitive; - else - return AssignWrongType; - } - - // Value class unboxing. - if (!srcTH.IsValueType() && destTH.IsValueType()) - { - if (srcTH.CanCastTo(destTH)) - return AssignUnboxValueClass; - else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. - return AssignUnboxValueClass; - else - return AssignWrongType; - } - - const CorElementType srcElType = srcTH.GetVerifierCorElementType(); - const CorElementType destElType = destTH.GetVerifierCorElementType(); - _ASSERTE(srcElType < ELEMENT_TYPE_MAX); - _ASSERTE(destElType < ELEMENT_TYPE_MAX); - - // Copying primitives from one type to another - if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) - { - _ASSERTE(srcElType != destElType); // Handled by fast path - if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType)) - return AssignPrimitiveWiden; - else - return AssignWrongType; - } - - // dest Object extends src - _ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path - - // src Object extends dest - if (destTH.CanCastTo(srcTH)) - return AssignMustCast; - - // class X extends/implements src and implements dest. - if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE) - return AssignMustCast; - - // class X implements src and extends/implements dest - if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE) - return AssignMustCast; - - return AssignWrongType; -} - -extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) -{ - QCALL_CONTRACT; - - INT32 ret = 0; - - BEGIN_QCALL; - - ret = CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); - - END_QCALL; - - return ret; -} - // // This is a GC safe variant of the memmove intrinsic. It sets the cards, and guarantees that the object references in the GC heap are @@ -354,203 +195,3 @@ 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 - -// This method will initialize an array from a TypeHandle to a field. - -FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) -{ - FCALL_CONTRACT; - - BASEARRAYREF arr = BASEARRAYREF(pArrayRef); - REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - HELPER_METHOD_FRAME_BEGIN_2(arr, refField); - - if ((arr == 0) || (refField == NULL)) - COMPlusThrow(kArgumentNullException); - - FieldDesc* pField = (FieldDesc*) refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) - COMPlusThrow(kArgumentException); - - SIZE_T dwCompSize = arr->GetComponentSize(); - SIZE_T dwElemCnt = arr->GetNumComponents(); - SIZE_T dwTotalSize = dwCompSize * dwElemCnt; - - DWORD size = pField->LoadSize(); - - // make certain you don't go off the end of the rva static - if (dwTotalSize > size) - COMPlusThrow(kArgumentException); - - void *src = pField->GetStaticAddressHandle(NULL); - void *dest = arr->GetDataPtr(); - -#if BIGENDIAN - DWORD i; - switch (dwCompSize) { - case 1: - memcpyNoGCRefs(dest, src, dwElemCnt); - break; - case 2: - for (i = 0; i < dwElemCnt; i++) - *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); - break; - case 4: - for (i = 0; i < dwElemCnt; i++) - *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); - break; - case 8: - for (i = 0; i < dwElemCnt; i++) - *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); - break; - default: - // should not reach here. - UNREACHABLE_MSG("Incorrect primitive type size!"); - break; - } -#else - memcpyNoGCRefs(dest, src, dwTotalSize); -#endif - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) -{ - FCALL_CONTRACT; - struct - { - REFLECTFIELDREF refField; - REFLECTCLASSBASEREF refClass; - } gc; - gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); - void* data = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - TypeHandle targetTypeHandle = gc.refClass->GetType(); - if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) - COMPlusThrow(kArgumentException); - - DWORD totalSize = pField->LoadSize(); - DWORD targetTypeSize = targetTypeHandle.GetSize(); - - data = pField->GetStaticAddressHandle(NULL); - _ASSERTE(data != NULL); - _ASSERTE(count != NULL); - - if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) - COMPlusThrow(kArgumentException); - - *count = (INT32)totalSize / targetTypeSize; - -#if BIGENDIAN - COMPlusThrow(kPlatformNotSupportedException); -#endif - - HELPER_METHOD_FRAME_END(); - return data; -} -FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 508cd776ddff7a..5dd3570ce5cbba 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -13,35 +13,9 @@ #ifndef _ARRAYNATIVE_H_ #define _ARRAYNATIVE_H_ -#include "fcall.h" -#include "runtimehandles.h" - -struct FCALLRuntimeFieldHandle -{ - ReflectFieldObject *pFieldDONOTUSEDIRECTLY; -}; -#define FCALL_RFH_TO_REFLECTFIELD(x) (x).pFieldDONOTUSEDIRECTLY - -class ArrayNative -{ -public: - - static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); - - // This set of methods will set a value in an array - static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); - - // This method will initialize an array from a TypeHandle - // to a field. - static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); - - // This method will acquire data to create a span from a TypeHandle - // to a field. - static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); -}; +#include "qcall.h" 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); -extern "C" INT32 QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH); #endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 7d7681894bef7f..3ad62b22824ac1 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -359,11 +359,6 @@ FCFuncStart(gCastHelpers) FCFuncElement("WriteBarrier", ::WriteBarrier_Helper) FCFuncEnd() -FCFuncStart(gArrayFuncs) - FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) - FCFuncElement("InternalSetValue", ArrayNative::SetValue) -FCFuncEnd() - FCFuncStart(gBufferFuncs) FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) FCFuncEnd() @@ -439,8 +434,6 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) - FCFuncElement("InitializeArray", ArrayNative::InitializeArray) - FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) @@ -538,7 +531,6 @@ 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) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 084e6df167aca1..05a62c15df1eb3 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -173,7 +173,6 @@ static const Entry s_QCall[] = DllImportEntry(MdUtf8String_EqualsCaseInsensitive) DllImportEntry(Array_CreateInstance) DllImportEntry(Array_GetElementConstructorEntrypoint) - DllImportEntry(Array_CanAssignArrayType) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) DllImportEntry(AssemblyNative_GetLocation) diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index f742fe7a77e1c9..3997d47191fb83 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -71,6 +71,24 @@ internal static void ThrowArrayTypeMismatchException() throw new ArrayTypeMismatchException(); } + [DoesNotReturn] + internal static void ThrowArrayTypeMismatchException_CantAssignType() + { + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + + [DoesNotReturn] + internal static void ThrowInvalidCastException_DownCastArrayElement() + { + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + } + + [DoesNotReturn] + internal static void ThrowInvalidCastException_StoreArrayElement() + { + throw new InvalidCastException(SR.InvalidCast_StoreArrayElement); + } + [DoesNotReturn] internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { @@ -1232,6 +1250,12 @@ private static string GetResourceString(ExceptionResource resource) return SR.Format_ExpectedAsciiDigit; case ExceptionResource.Argument_HasToBeArrayClass: return SR.Argument_HasToBeArrayClass; + case ExceptionResource.Arg_PrimitiveWiden: + return SR.Arg_PrimWiden; + case ExceptionResource.Argument_MustBePrimitiveArray: + return SR.Argument_MustBePrimitiveArray; + case ExceptionResource.Argument_BadFieldForInitializeArray: + return SR.Argument_BadFieldForInitializeArray; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1428,5 +1452,8 @@ internal enum ExceptionResource Format_UnclosedFormatItem, Format_ExpectedAsciiDigit, Argument_HasToBeArrayClass, + Arg_PrimitiveWiden, + Argument_MustBePrimitiveArray, + Argument_BadFieldForInitializeArray, } } From 94acf75ae47b50009e24672879e8e872dd8ceb48 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 5 Jun 2024 22:58:55 +0800 Subject: [PATCH 34/39] Keep all native implementations --- .../classlibnative/bcltype/arraynative.cpp | 370 ++++++++++++++++++ .../classlibnative/bcltype/arraynative.h | 29 +- src/coreclr/vm/ecalllist.h | 9 + src/coreclr/vm/qcallentrypoints.cpp | 1 + 4 files changed, 408 insertions(+), 1 deletion(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 246c5398276073..02ff0360ac4b7d 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -17,6 +17,17 @@ #include "arraynative.inl" +// Returns a bool to indicate if the array is of primitive types or not. +FCIMPL1(INT32, ArrayNative::GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE) +{ + FCALL_CONTRACT; + + _ASSERTE(arrayUNSAFE != NULL); + + return arrayUNSAFE->GetArrayElementTypeHandle().GetVerifierCorElementType(); +} +FCIMPLEND + extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd) { QCALL_CONTRACT; @@ -36,6 +47,165 @@ extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHand return ctorEntrypoint; } + // Returns whether you can directly copy an array of srcType into destType. +FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst) +{ + FCALL_CONTRACT; + + _ASSERTE(pSrc != NULL); + _ASSERTE(pDst != NULL); + + // This case is expected to be handled by the fast path + _ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable()); + + TypeHandle srcTH = pSrc->GetMethodTable()->GetArrayElementTypeHandle(); + TypeHandle destTH = pDst->GetMethodTable()->GetArrayElementTypeHandle(); + if (srcTH == destTH) // This check kicks for different array kind or dimensions + FC_RETURN_BOOL(true); + + if (srcTH.IsValueType()) + { + // Value class boxing + if (!destTH.IsValueType()) + FC_RETURN_BOOL(false); + + const CorElementType srcElType = srcTH.GetVerifierCorElementType(); + const CorElementType destElType = destTH.GetVerifierCorElementType(); + _ASSERTE(srcElType < ELEMENT_TYPE_MAX); + _ASSERTE(destElType < ELEMENT_TYPE_MAX); + + // Copying primitives from one type to another + if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) + { + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + FC_RETURN_BOOL(true); + } + } + else + { + // Value class unboxing + if (destTH.IsValueType()) + FC_RETURN_BOOL(false); + } + + TypeHandle::CastResult r = srcTH.CanCastToCached(destTH); + if (r != TypeHandle::MaybeCast) + { + FC_RETURN_BOOL(r); + } + + struct + { + OBJECTREF src; + OBJECTREF dst; + } gc; + + gc.src = ObjectToOBJECTREF(pSrc); + gc.dst = ObjectToOBJECTREF(pDst); + + BOOL iRetVal = FALSE; + + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + iRetVal = srcTH.CanCastTo(destTH); + HELPER_METHOD_FRAME_END(); + + FC_RETURN_BOOL(iRetVal); +} +FCIMPLEND + + +// Return values for CanAssignArrayType +enum AssignArrayEnum +{ + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, +}; + +// Returns an enum saying whether you can copy an array of srcType into destType. +static AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(!srcTH.IsNull()); + PRECONDITION(!destTH.IsNull()); + } + CONTRACTL_END; + + _ASSERTE(srcTH != destTH); // Handled by fast path + + // Value class boxing + if (srcTH.IsValueType() && !destTH.IsValueType()) + { + if (srcTH.CanCastTo(destTH)) + return AssignBoxValueClassOrPrimitive; + else + return AssignWrongType; + } + + // Value class unboxing. + if (!srcTH.IsValueType() && destTH.IsValueType()) + { + if (srcTH.CanCastTo(destTH)) + return AssignUnboxValueClass; + else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. + return AssignUnboxValueClass; + else + return AssignWrongType; + } + + const CorElementType srcElType = srcTH.GetVerifierCorElementType(); + const CorElementType destElType = destTH.GetVerifierCorElementType(); + _ASSERTE(srcElType < ELEMENT_TYPE_MAX); + _ASSERTE(destElType < ELEMENT_TYPE_MAX); + + // Copying primitives from one type to another + if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) + { + _ASSERTE(srcElType != destElType); // Handled by fast path + if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType)) + return AssignPrimitiveWiden; + else + return AssignWrongType; + } + + // dest Object extends src + _ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path + + // src Object extends dest + if (destTH.CanCastTo(srcTH)) + return AssignMustCast; + + // class X extends/implements src and implements dest. + if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE) + return AssignMustCast; + + // class X implements src and extends/implements dest + if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE) + return AssignMustCast; + + return AssignWrongType; +} + +extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) +{ + QCALL_CONTRACT; + + INT32 ret = 0; + + BEGIN_QCALL; + + ret = CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); + + END_QCALL; + + return ret; +} + // // This is a GC safe variant of the memmove intrinsic. It sets the cards, and guarantees that the object references in the GC heap are @@ -195,3 +365,203 @@ 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 + +// This method will initialize an array from a TypeHandle to a field. + +FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) +{ + FCALL_CONTRACT; + + BASEARRAYREF arr = BASEARRAYREF(pArrayRef); + REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + HELPER_METHOD_FRAME_BEGIN_2(arr, refField); + + if ((arr == 0) || (refField == NULL)) + COMPlusThrow(kArgumentNullException); + + FieldDesc* pField = (FieldDesc*) refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) + COMPlusThrow(kArgumentException); + + SIZE_T dwCompSize = arr->GetComponentSize(); + SIZE_T dwElemCnt = arr->GetNumComponents(); + SIZE_T dwTotalSize = dwCompSize * dwElemCnt; + + DWORD size = pField->LoadSize(); + + // make certain you don't go off the end of the rva static + if (dwTotalSize > size) + COMPlusThrow(kArgumentException); + + void *src = pField->GetStaticAddressHandle(NULL); + void *dest = arr->GetDataPtr(); + +#if BIGENDIAN + DWORD i; + switch (dwCompSize) { + case 1: + memcpyNoGCRefs(dest, src, dwElemCnt); + break; + case 2: + for (i = 0; i < dwElemCnt; i++) + *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); + break; + case 4: + for (i = 0; i < dwElemCnt; i++) + *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); + break; + case 8: + for (i = 0; i < dwElemCnt; i++) + *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); + break; + default: + // should not reach here. + UNREACHABLE_MSG("Incorrect primitive type size!"); + break; + } +#else + memcpyNoGCRefs(dest, src, dwTotalSize); +#endif + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) +{ + FCALL_CONTRACT; + struct + { + REFLECTFIELDREF refField; + REFLECTCLASSBASEREF refClass; + } gc; + gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); + gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); + void* data = NULL; + HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); + + FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); + + if (!pField->IsRVA()) + COMPlusThrow(kArgumentException); + + TypeHandle targetTypeHandle = gc.refClass->GetType(); + if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) + COMPlusThrow(kArgumentException); + + DWORD totalSize = pField->LoadSize(); + DWORD targetTypeSize = targetTypeHandle.GetSize(); + + data = pField->GetStaticAddressHandle(NULL); + _ASSERTE(data != NULL); + _ASSERTE(count != NULL); + + if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) + COMPlusThrow(kArgumentException); + + *count = (INT32)totalSize / targetTypeSize; + +#if BIGENDIAN + COMPlusThrow(kPlatformNotSupportedException); +#endif + + HELPER_METHOD_FRAME_END(); + return data; +} +FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 5dd3570ce5cbba..aeb264a9b28ceb 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -13,9 +13,36 @@ #ifndef _ARRAYNATIVE_H_ #define _ARRAYNATIVE_H_ -#include "qcall.h" +#include "fcall.h" +#include "runtimehandles.h" + +struct FCALLRuntimeFieldHandle +{ + ReflectFieldObject *pFieldDONOTUSEDIRECTLY; +}; +#define FCALL_RFH_TO_REFLECTFIELD(x) (x).pFieldDONOTUSEDIRECTLY + +class ArrayNative +{ +public: + static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); + + static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); + + // This set of methods will set a value in an array + static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); + + // This method will initialize an array from a TypeHandle + // to a field. + static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); + + // This method will acquire data to create a span from a TypeHandle + // to a field. + static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); +}; 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); +extern "C" INT32 QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH); #endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 3ad62b22824ac1..f06fc007e2ccb1 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -359,6 +359,12 @@ FCFuncStart(gCastHelpers) FCFuncElement("WriteBarrier", ::WriteBarrier_Helper) FCFuncEnd() +FCFuncStart(gArrayFuncs) + FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) + FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) + FCFuncElement("InternalSetValue", ArrayNative::SetValue) +FCFuncEnd() + FCFuncStart(gBufferFuncs) FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier) FCFuncEnd() @@ -434,6 +440,8 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) + FCFuncElement("InitializeArray", ArrayNative::InitializeArray) + FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) @@ -531,6 +539,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) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 05a62c15df1eb3..084e6df167aca1 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -173,6 +173,7 @@ static const Entry s_QCall[] = DllImportEntry(MdUtf8String_EqualsCaseInsensitive) DllImportEntry(Array_CreateInstance) DllImportEntry(Array_GetElementConstructorEntrypoint) + DllImportEntry(Array_CanAssignArrayType) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) DllImportEntry(AssemblyNative_GetLocation) From 272883fe7a5132f141969cfe38aca26567978809 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 5 Jun 2024 23:01:36 +0800 Subject: [PATCH 35/39] First exclude InternalSetValue --- .../src/System/Array.CoreCLR.cs | 96 +------------------ 1 file changed, 2 insertions(+), 94 deletions(-) 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 f8200f16d18879..5a66a0dd45b81c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -633,100 +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) - { - ThrowHelper.ThrowInvalidCastException_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) - ThrowHelper.ThrowInvalidCastException_StoreArrayElement(); - - // Get a properly widened type - if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType)) - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_PrimitiveWiden); - - 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); From fa327da357ab8500d7eeabcb7fc6d81e5d1bf1f2 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 6 Jun 2024 09:59:30 +0800 Subject: [PATCH 36/39] Revert "First exclude InternalSetValue" This reverts commit 272883fe7a5132f141969cfe38aca26567978809. --- .../src/System/Array.CoreCLR.cs | 96 ++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) 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 5a66a0dd45b81c..f8200f16d18879 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -633,8 +633,100 @@ private unsafe nint GetFlattenedIndex(ReadOnlySpan indices) return result; } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InternalSetValue(object? value, nint flattenedIndex); + 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) + { + ThrowHelper.ThrowInvalidCastException_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) + ThrowHelper.ThrowInvalidCastException_StoreArrayElement(); + + // Get a properly widened type + if (!RuntimeHelpers.CanPrimitiveWiden(srcType, targetType)) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_PrimitiveWiden); + + PrimitiveWiden(ref value.GetRawData(), ref offsetDataRef, srcType, targetType); + } + } + + GC.KeepAlive(this); // Keep the method table alive + } public int Length => checked((int)Unsafe.As(this).Length); From 93f99e8557fdd2cbbb88b99aa2fd8104c33c972c Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 6 Jun 2024 10:01:54 +0800 Subject: [PATCH 37/39] Exclude CopyImpl --- .../src/System/Array.CoreCLR.cs | 122 ++++-------------- 1 file changed, 24 insertions(+), 98 deletions(-) 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 f8200f16d18879..1b8445114ebc84 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -62,10 +62,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - ArrayAssignType assignType = ArrayAssignType.WrongType; - - if (sourceArray.GetType() == destinationArray.GetType() - || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) + if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -89,57 +86,44 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); } - private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) - { - Debug.Assert(elementType.IsPrimitiveType()); - - // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable - // Enums with interchangeable underlying types are interchangeable - // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 - switch (elementType) - { - case CorElementType.ELEMENT_TYPE_U1: - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_U4: - case CorElementType.ELEMENT_TYPE_U8: - case CorElementType.ELEMENT_TYPE_U: - return elementType - 1; // normalize to signed type - default: - return elementType; - } - } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); // Reliability-wise, this method will either possibly corrupt your // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - if (assignType == ArrayAssignType.WrongType) + void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; + void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; + AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); + + if (r == AssignArrayEnum.AssignWrongType) ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); if (length > 0) { - switch (assignType) + switch (r) { - case ArrayAssignType.UnboxValueClass: + case AssignArrayEnum.AssignUnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.BoxValueClassOrPrimitive: + case AssignArrayEnum.AssignBoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.MustCast: + case AssignArrayEnum.AssignMustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case ArrayAssignType.PrimitiveWiden: + case AssignArrayEnum.AssignPrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -150,76 +134,18 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - private enum ArrayAssignType + // Must match the definition in arraynative.cpp + private enum AssignArrayEnum { - SimpleCopy, - WrongType, - MustCast, - BoxValueClassOrPrimitive, - UnboxValueClass, - PrimitiveWiden, + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, } - private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) - { - TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); - TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); - - if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions - return ArrayAssignType.SimpleCopy; - - // Value class boxing - if (srcTH.IsValueType && !destTH.IsValueType) - { - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.BoxValueClassOrPrimitive; - else - return ArrayAssignType.WrongType; - } - - // Value class unboxing. - if (!srcTH.IsValueType && destTH.IsValueType) - { - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.UnboxValueClass; - else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. - return ArrayAssignType.UnboxValueClass; - else - return ArrayAssignType.WrongType; - } - - CorElementType srcElType = srcTH.GetVerifierCorElementType(); - CorElementType destElType = destTH.GetVerifierCorElementType(); - - // Copying primitives from one type to another - if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) - { - if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - return ArrayAssignType.SimpleCopy; - else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) - return ArrayAssignType.PrimitiveWiden; - else - return ArrayAssignType.WrongType; - } - - // src Object extends dest - if (srcTH.CanCastTo(destTH)) - return ArrayAssignType.SimpleCopy; - - // dest Object extends src - if (destTH.CanCastTo(srcTH)) - return ArrayAssignType.MustCast; - - // class X extends/implements src and implements dest. - if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return ArrayAssignType.MustCast; - - // class X implements src and extends/implements dest - if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) - return ArrayAssignType.MustCast; - - return ArrayAssignType.WrongType; - } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] + private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) From 9853076f6d2a10c183ef19c2a36a4459fe8ec0f2 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 6 Jun 2024 14:00:31 +0800 Subject: [PATCH 38/39] Exclude InitializeArray --- .../RuntimeHelpers.CoreCLR.cs | 99 ++----------------- 1 file changed, 6 insertions(+), 93 deletions(-) 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 816456aface318..415e0ad612929e 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 @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; @@ -14,102 +15,14 @@ namespace System.Runtime.CompilerServices public static partial class RuntimeHelpers { [Intrinsic] - public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if (array is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); - - MethodTable* pMT = GetMethodTable(array); - nuint totalSize = pMT->ComponentSize * array.NativeLength; - - uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - - // make certain you don't go off the end of the rva static - if (totalSize > size) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); - - void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); - - Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); - - if (BitConverter.IsLittleEndian) - { - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - } - else - { - switch (pMT->ComponentSize) - { - case 1: - SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); - break; - case 2: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 4: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - case 8: - BinaryPrimitives.ReverseEndianness( - new ReadOnlySpan(src, array.Length), - new Span(ref Unsafe.As(ref dst), array.Length)); - break; - default: - Debug.Fail("Incorrect primitive type size!"); - break; - } - } - } + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); - private static unsafe void* GetSpanDataFrom( + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count) - { - IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - - if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); - - TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); - Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter - - if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_MustBePrimitiveArray); - - uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); - uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); - - IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); - if (data % targetTypeSize != 0) - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_BadFieldForInitializeArray); - - if (!BitConverter.IsLittleEndian) - { - throw new PlatformNotSupportedException(); - } - - count = (int)(totalSize / targetTypeSize); - return (void*)data; - } + out int count); // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use From 46b1d4a8b814836cf74de7efbeaab8152d484968 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 6 Jun 2024 17:21:29 +0800 Subject: [PATCH 39/39] Exclude GetCorElementTypeOfElementType --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) 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 1d5569f73bf2c1..169270737b7380 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -710,13 +710,8 @@ public unsafe int GetLowerBound(int dimension) return Unsafe.Add(ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this), rank + dimension); } - internal unsafe CorElementType GetCorElementTypeOfElementType() - { - MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - CorElementType et = pMT->GetArrayElementTypeHandle().GetVerifierCorElementType(); - GC.KeepAlive(this); - return et; - } + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern CorElementType GetCorElementTypeOfElementType(); private unsafe bool IsValueOfElementType(object value) {