diff options
Diffstat (limited to 'src/classlibnative/bcltype/arraynative.cpp')
-rw-r--r-- | src/classlibnative/bcltype/arraynative.cpp | 1562 |
1 files changed, 1562 insertions, 0 deletions
diff --git a/src/classlibnative/bcltype/arraynative.cpp b/src/classlibnative/bcltype/arraynative.cpp new file mode 100644 index 0000000000..b1aa6f8751 --- /dev/null +++ b/src/classlibnative/bcltype/arraynative.cpp @@ -0,0 +1,1562 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// File: ArrayNative.cpp +// + +// +// This file contains the native methods that support the Array class +// + + +#include "common.h" +#include "arraynative.h" +#include "excep.h" +#include "field.h" +#include "security.h" +#include "invokeutil.h" + +FCIMPL1(INT32, ArrayNative::GetRank, ArrayBase* array) +{ + FCALL_CONTRACT; + + VALIDATEOBJECT(array); + + if (array == NULL) + FCThrow(kNullReferenceException); + + return array->GetRank(); +} +FCIMPLEND + + +FCIMPL2(INT32, ArrayNative::GetLowerBound, ArrayBase* array, unsigned int dimension) +{ + FCALL_CONTRACT; + + VALIDATEOBJECT(array); + + if (array == NULL) + FCThrow(kNullReferenceException); + + if (dimension != 0) + { + // Check the dimension is within our rank + unsigned int rank = array->GetRank(); + + if (dimension >= rank) + FCThrowRes(kIndexOutOfRangeException, W("IndexOutOfRange_ArrayRankIndex")); + } + + return array->GetLowerBoundsPtr()[dimension]; +} +FCIMPLEND + + +// Get inclusive upper bound +FCIMPL2(INT32, ArrayNative::GetUpperBound, ArrayBase* array, unsigned int dimension) +{ + FCALL_CONTRACT; + + VALIDATEOBJECT(array); + + if (array == NULL) + FCThrow(kNullReferenceException); + + if (dimension != 0) + { + // Check the dimension is within our rank + unsigned int rank = array->GetRank(); + + if (dimension >= rank) + FCThrowRes(kIndexOutOfRangeException, W("IndexOutOfRange_ArrayRankIndex")); + } + + return array->GetBoundsPtr()[dimension] + array->GetLowerBoundsPtr()[dimension] - 1; +} +FCIMPLEND + + +FCIMPL2(INT32, ArrayNative::GetLength, ArrayBase* array, unsigned int dimension) +{ + FCALL_CONTRACT; + + VALIDATEOBJECT(array); + + if (array==NULL) + FCThrow(kNullReferenceException); + + if (dimension != 0) + { + // Check the dimension is within our rank + unsigned int rank = array->GetRank(); + if (dimension >= rank) + FCThrow(kIndexOutOfRangeException); + } + + return array->GetBoundsPtr()[dimension]; +} +FCIMPLEND + + +FCIMPL1(INT32, ArrayNative::GetLengthNoRank, ArrayBase* array) +{ + FCALL_CONTRACT; + + VALIDATEOBJECT(array); + + if (array==NULL) + FCThrow(kNullReferenceException); + + SIZE_T numComponents = array->GetNumComponents(); + if (numComponents > INT32_MAX) + FCThrow(kOverflowException); + + return (INT32)numComponents; +} +FCIMPLEND + + +FCIMPL1(INT64, ArrayNative::GetLongLengthNoRank, ArrayBase* array) +{ + FCALL_CONTRACT; + + VALIDATEOBJECT(array); + + if (array==NULL) + FCThrow(kNullReferenceException); + + return array->GetNumComponents(); +} +FCIMPLEND + + +FCIMPL1(INT32, ArrayNative::GetDataPtrOffsetInternal, ArrayBase* array) +{ + FCALL_CONTRACT; + + VALIDATEOBJECT(array); + + if (array == NULL) + FCThrow(kNullReferenceException); + + return ArrayBase::GetDataPtrOffset(array->GetMethodTable()); +} +FCIMPLEND + + + + + + +// array is GC protected by caller +void ArrayInitializeWorker(ARRAYBASEREF * arrayRef, + MethodTable* pArrayMT, + MethodTable* pElemMT) +{ + STATIC_CONTRACT_MODE_COOPERATIVE; + STATIC_CONTRACT_SO_INTOLERANT; + + // Ensure that the array element type is fully loaded before executing its code + pElemMT->EnsureInstanceActive(); + + //can not use contract here because of SEH + _ASSERTE(IsProtectedByGCFrame (arrayRef)); + + SIZE_T offset = ArrayBase::GetDataPtrOffset(pArrayMT); + SIZE_T size = pArrayMT->GetComponentSize(); + SIZE_T cElements = (*arrayRef)->GetNumComponents(); + + MethodTable * pCanonMT = pElemMT->GetCanonicalMethodTable(); + WORD slot = pCanonMT->GetDefaultConstructorSlot(); + + PCODE ctorFtn = pCanonMT->GetSlot(slot); + +#ifdef _X86_ + BEGIN_CALL_TO_MANAGED(); + + typedef void (__fastcall * CtorFtnType)(BYTE*, BYTE*); + + for (SIZE_T i = 0; i < cElements; i++) + { + // Since GetSlot() is not idempotent and may have returned + // a non-optimal entry-point the first time round. + if (i == 1) + { + ctorFtn = pCanonMT->GetSlot(slot); + } + + BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset); + +#ifdef _DEBUG + __asm { + mov ECX, thisPtr + mov EDX, pElemMT // Instantiation argument if the type is generic + call [ctorFtn] + nop // Mark the fact that we can call managed code + } +#else // _DEBUG + (*(CtorFtnType)ctorFtn)(thisPtr, (BYTE*)pElemMT); +#endif // _DEBUG + + offset += size; + } + + END_CALL_TO_MANAGED(); +#else // _X86_ + // + // This is quite a bit slower, but it is portable. + // + + for (SIZE_T i =0; i < cElements; i++) + { + // Since GetSlot() is not idempotent and may have returned + // a non-optimal entry-point the first time round. + if (i == 1) + { + ctorFtn = pCanonMT->GetSlot(slot); + } + + BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset); + + PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(ctorFtn); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = PTR_TO_ARGHOLDER(thisPtr); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(pElemMT); // Instantiation argument if the type is generic + CALL_MANAGED_METHOD_NORET(args); + + offset += size; + } +#endif // _X86_ +} + + +FCIMPL1(void, ArrayNative::Initialize, ArrayBase* array) +{ + FCALL_CONTRACT; + + if (array == NULL) + { + FCThrowVoid(kNullReferenceException); + } + + + MethodTable* pArrayMT = array->GetMethodTable(); + + TypeHandle thElem = pArrayMT->GetApproxArrayElementTypeHandle(); + if (thElem.IsTypeDesc()) + return; + + MethodTable * pElemMT = thElem.AsMethodTable(); + if (!pElemMT->HasDefaultConstructor() || !pElemMT->IsValueType()) + return; + + ARRAYBASEREF arrayRef (array); + HELPER_METHOD_FRAME_BEGIN_1(arrayRef); + + ArrayInitializeWorker(&arrayRef, pArrayMT, pElemMT); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + + + + + + // Returns an enum saying whether you can copy an array of srcType into destType. +ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayTypeNoGC(const BASEARRAYREF pSrc, const BASEARRAYREF pDest) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + SO_TOLERANT; + PRECONDITION(pSrc != NULL); + PRECONDITION(pDest != NULL); + } + CONTRACTL_END; + + // The next 50 lines are a little tricky. Change them with great care. + // + + // This first bit is a minor optimization: e.g. when copying byte[] to byte[] + // we do not need to call GetArrayElementTypeHandle(). + MethodTable *pSrcMT = pSrc->GetMethodTable(); + MethodTable *pDestMT = pDest->GetMethodTable(); + if (pSrcMT == pDestMT) + return AssignWillWork; + + TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle(); + TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle(); + if (srcTH == destTH) // This check kicks for different array kind or dimensions + return AssignWillWork; + + // Value class boxing + if (srcTH.IsValueType() && !destTH.IsValueType()) + { + switch (srcTH.CanCastToNoGC(destTH)) + { + case TypeHandle::CanCast : return AssignBoxValueClassOrPrimitive; + case TypeHandle::CannotCast : return AssignWrongType; + default : return AssignDontKnow; + } + } + + // Value class unboxing. + if (!srcTH.IsValueType() && destTH.IsValueType()) + { + if (srcTH.CanCastToNoGC(destTH) == TypeHandle::CanCast) + return AssignUnboxValueClass; + else if (destTH.CanCastToNoGC(srcTH) == TypeHandle::CanCast) // V extends IV. Copying from IV to V, or Object to V. + return AssignUnboxValueClass; + else + return AssignDontKnow; + } + + const CorElementType srcElType = srcTH.GetSignatureCorElementType(); + const CorElementType destElType = destTH.GetSignatureCorElementType(); + _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 (InvokeUtil::CanPrimitiveWiden(destElType, srcElType)) + return AssignPrimitiveWiden; + else + return AssignWrongType; + } + + // dest Object extends src + if (srcTH.CanCastToNoGC(destTH) == TypeHandle::CanCast) + return AssignWillWork; + + // src Object extends dest + if (destTH.CanCastToNoGC(srcTH) == TypeHandle::CanCast) + 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; + + // Enum is stored as a primitive of type dest. + if (srcTH.IsEnum() && srcTH.GetInternalCorElementType() == destElType) + return AssignWillWork; + + return AssignDontKnow; +} + + +// Returns an enum saying whether you can copy an array of srcType into destType. +ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pSrc != NULL); + PRECONDITION(pDest != NULL); + } + CONTRACTL_END; + + // The next 50 lines are a little tricky. Change them with great care. + // + + // This first bit is a minor optimization: e.g. when copying byte[] to byte[] + // we do not need to call GetArrayElementTypeHandle(). + MethodTable *pSrcMT = pSrc->GetMethodTable(); + MethodTable *pDestMT = pDest->GetMethodTable(); + if (pSrcMT == pDestMT) + return AssignWillWork; + + TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle(); + TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle(); + if (srcTH == destTH) // This check kicks for different array kind or dimensions + return AssignWillWork; + + // 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.GetSignatureCorElementType(); + const CorElementType destElType = destTH.GetSignatureCorElementType(); + _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 (InvokeUtil::CanPrimitiveWiden(destElType, srcElType)) + return AssignPrimitiveWiden; + else + return AssignWrongType; + } + + // dest Object extends src + if (srcTH.CanCastTo(destTH)) + return AssignWillWork; + + // 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; + + // Enum is stored as a primitive of type dest. + if (srcTH.IsEnum() && srcTH.GetInternalCorElementType() == destElType) + return AssignWillWork; + + return AssignWrongType; +} + + +// Casts and assigns each element of src array to the dest array type. +void ArrayNative::CastCheckEachElement(const BASEARRAYREF pSrcUnsafe, const unsigned int srcIndex, BASEARRAYREF pDestUnsafe, unsigned int destIndex, const unsigned int len) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pSrcUnsafe != NULL); + PRECONDITION(srcIndex >= 0); + PRECONDITION(pDestUnsafe != NULL); + PRECONDITION(len > 0); + } + CONTRACTL_END; + + // pSrc is either a PTRARRAYREF or a multidimensional array. + TypeHandle destTH = pDestUnsafe->GetArrayElementTypeHandle(); + + struct _gc + { + OBJECTREF obj; + BASEARRAYREF pDest; + BASEARRAYREF pSrc; + } gc; + + gc.obj = NULL; + gc.pDest = pDestUnsafe; + gc.pSrc = pSrcUnsafe; + + GCPROTECT_BEGIN(gc); + + for(unsigned int i=srcIndex; i<srcIndex + len; ++i) + { + gc.obj = ObjectToOBJECTREF(*((Object**) gc.pSrc->GetDataPtr() + i)); + + // Now that we have grabbed obj, we are no longer subject to races from another + // mutator thread. + if (gc.obj != NULL && !ObjIsInstanceOf(OBJECTREFToObject(gc.obj), destTH)) + COMPlusThrow(kInvalidCastException, W("InvalidCast_DownCastArrayElement")); + + OBJECTREF * destData = (OBJECTREF*)(gc.pDest->GetDataPtr()) + i - srcIndex + destIndex; + SetObjectReference(destData, gc.obj, gc.pDest->GetAppDomain()); + } + + GCPROTECT_END(); + + return; +} + + +// Will box each element in an array of value classes or primitives into an array of Objects. +void ArrayNative::BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pSrc != NULL); + PRECONDITION(srcIndex >= 0); + PRECONDITION(pDest != NULL); + PRECONDITION(length > 0); + } + CONTRACTL_END; + + // pDest is either a PTRARRAYREF or a multidimensional array. + _ASSERTE(pSrc!=NULL && srcIndex>=0 && pDest!=NULL && destIndex>=0 && length>=0); + TypeHandle srcTH = pSrc->GetArrayElementTypeHandle(); +#ifdef _DEBUG + TypeHandle destTH = pDest->GetArrayElementTypeHandle(); +#endif + _ASSERTE(srcTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || srcTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pSrc->GetArrayElementType())); + _ASSERTE(!destTH.GetMethodTable()->IsValueType()); + + // Get method table of type we're copying from - we need to allocate objects of that type. + MethodTable * pSrcMT = srcTH.GetMethodTable(); + PREFIX_ASSUME(pSrcMT != NULL); + + if (!pSrcMT->IsClassInited()) + { + BASEARRAYREF pSrcTmp = pSrc; + BASEARRAYREF pDestTmp = pDest; + GCPROTECT_BEGIN (pSrcTmp); + GCPROTECT_BEGIN (pDestTmp); + pSrcMT->CheckRunClassInitThrowing(); + pSrc = pSrcTmp; + pDest = pDestTmp; + GCPROTECT_END (); + GCPROTECT_END (); + } + + const unsigned int srcSize = pSrcMT->GetNumInstanceFieldBytes(); + unsigned int srcArrayOffset = srcIndex * srcSize; + + struct _gc + { + BASEARRAYREF src; + BASEARRAYREF dest; + OBJECTREF obj; + } gc; + + gc.src = pSrc; + gc.dest = pDest; + gc.obj = NULL; + + void* srcPtr = 0; + GCPROTECT_BEGIN(gc); + GCPROTECT_BEGININTERIOR(srcPtr); + for (unsigned int i=destIndex; i < destIndex+length; i++, srcArrayOffset += srcSize) + { + srcPtr = (BYTE*)gc.src->GetDataPtr() + srcArrayOffset; + gc.obj = pSrcMT->FastBox(&srcPtr); + + OBJECTREF * destData = (OBJECTREF*)((gc.dest)->GetDataPtr()) + i; + SetObjectReference(destData, gc.obj, gc.dest->GetAppDomain()); + } + GCPROTECT_END(); + GCPROTECT_END(); +} + + +// Unboxes from an Object[] into a value class or primitive array. +void ArrayNative::UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pSrc != NULL); + PRECONDITION(srcIndex >= 0); + PRECONDITION(pDest != NULL); + PRECONDITION(destIndex >= 0); + PRECONDITION(length > 0); + } + CONTRACTL_END; + +#ifdef _DEBUG + TypeHandle srcTH = pSrc->GetArrayElementTypeHandle(); +#endif + TypeHandle destTH = pDest->GetArrayElementTypeHandle(); + _ASSERTE(destTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || destTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pDest->GetArrayElementType())); + _ASSERTE(!srcTH.GetMethodTable()->IsValueType()); + + MethodTable * pDestMT = destTH.GetMethodTable(); + PREFIX_ASSUME(pDestMT != NULL); + + const unsigned int destSize = pDestMT->GetNumInstanceFieldBytes(); + BYTE* srcData = (BYTE*) pSrc->GetDataPtr() + srcIndex * sizeof(OBJECTREF); + BYTE* data = (BYTE*) pDest->GetDataPtr() + destIndex * destSize; + + for(; length>0; length--, srcData += sizeof(OBJECTREF), data += destSize) + { + OBJECTREF obj = ObjectToOBJECTREF(*(Object**)srcData); + + // Now that we have retrieved the element, we are no longer subject to race + // conditions from another array mutator. + + if (!pDestMT->UnBoxInto(data, obj)) + goto fail; + } + return; + +fail: + COMPlusThrow(kInvalidCastException, W("InvalidCast_DownCastArrayElement")); +} + + +// Widen primitive types to another primitive type. +void ArrayNative::PrimitiveWiden(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(pSrc != NULL); + PRECONDITION(srcIndex >= 0); + PRECONDITION(pDest != NULL); + PRECONDITION(destIndex >= 0); + PRECONDITION(length > 0); + } + CONTRACTL_END; + + // Get appropriate sizes, which requires method tables. + TypeHandle srcTH = pSrc->GetArrayElementTypeHandle(); + TypeHandle destTH = pDest->GetArrayElementTypeHandle(); + + const CorElementType srcElType = srcTH.GetSignatureCorElementType(); + const CorElementType destElType = destTH.GetSignatureCorElementType(); + const unsigned int srcSize = GetSizeForCorElementType(srcElType); + const unsigned int destSize = GetSizeForCorElementType(destElType); + + BYTE* srcData = (BYTE*) pSrc->GetDataPtr() + srcIndex * srcSize; + BYTE* data = (BYTE*) pDest->GetDataPtr() + destIndex * destSize; + + _ASSERTE(srcElType != destElType); // We shouldn't be here if these are the same type. + _ASSERTE(CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)); + + for(; length>0; length--, srcData += srcSize, data += destSize) + { + // We pretty much have to do some fancy datatype mangling every time here, for + // converting w/ sign extension and floating point conversions. + switch (srcElType) + { + case ELEMENT_TYPE_U1: + switch (destElType) + { + case ELEMENT_TYPE_R4: + *(float*)data = *(UINT8*)srcData; + break; + + case ELEMENT_TYPE_R8: + *(double*)data = *(UINT8*)srcData; + break; +#ifndef BIGENDIAN + default: + *(UINT8*)data = *(UINT8*)srcData; + memset(data+1, 0, destSize - 1); + break; +#else // BIGENDIAN + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + *(INT16*)data = *(UINT8*)srcData; + break; + + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + *(INT32*)data = *(UINT8*)srcData; + break; + + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + *(INT64*)data = *(UINT8*)srcData; + break; + + default: + _ASSERTE(!"Array.Copy from U1 to another type hit unsupported widening conversion"); +#endif // BIGENDIAN + } + break; + + + case ELEMENT_TYPE_I1: + switch (destElType) + { + case ELEMENT_TYPE_I2: + *(INT16*)data = *(INT8*)srcData; + break; + + case ELEMENT_TYPE_I4: + *(INT32*)data = *(INT8*)srcData; + break; + + case ELEMENT_TYPE_I8: + *(INT64*)data = *(INT8*)srcData; + break; + + case ELEMENT_TYPE_R4: + *(float*)data = *(INT8*)srcData; + break; + + case ELEMENT_TYPE_R8: + *(double*)data = *(INT8*)srcData; + break; + + default: + _ASSERTE(!"Array.Copy from I1 to another type hit unsupported widening conversion"); + } + break; + + + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: + switch (destElType) + { + case ELEMENT_TYPE_R4: + *(float*)data = *(UINT16*)srcData; + break; + + case ELEMENT_TYPE_R8: + *(double*)data = *(UINT16*)srcData; + break; +#ifndef BIGENDIAN + default: + *(UINT16*)data = *(UINT16*)srcData; + memset(data+2, 0, destSize - 2); + break; +#else // BIGENDIAN + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: + *(UINT16*)data = *(UINT16*)srcData; + break; + + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + *(UINT32*)data = *(UINT16*)srcData; + break; + + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + *(UINT64*)data = *(UINT16*)srcData; + break; + + default: + _ASSERTE(!"Array.Copy from U1 to another type hit unsupported widening conversion"); +#endif // BIGENDIAN + } + break; + + + case ELEMENT_TYPE_I2: + switch (destElType) + { + case ELEMENT_TYPE_I4: + *(INT32*)data = *(INT16*)srcData; + break; + + case ELEMENT_TYPE_I8: + *(INT64*)data = *(INT16*)srcData; + break; + + case ELEMENT_TYPE_R4: + *(float*)data = *(INT16*)srcData; + break; + + case ELEMENT_TYPE_R8: + *(double*)data = *(INT16*)srcData; + break; + + default: + _ASSERTE(!"Array.Copy from I2 to another type hit unsupported widening conversion"); + } + break; + + + case ELEMENT_TYPE_I4: + switch (destElType) + { + case ELEMENT_TYPE_I8: + *(INT64*)data = *(INT32*)srcData; + break; + + case ELEMENT_TYPE_R4: + *(float*)data = (float)*(INT32*)srcData; + break; + + case ELEMENT_TYPE_R8: + *(double*)data = *(INT32*)srcData; + break; + + default: + _ASSERTE(!"Array.Copy from I4 to another type hit unsupported widening conversion"); + } + break; + + + case ELEMENT_TYPE_U4: + switch (destElType) + { + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + *(INT64*)data = *(UINT32*)srcData; + break; + + case ELEMENT_TYPE_R4: + *(float*)data = (float)*(UINT32*)srcData; + break; + + case ELEMENT_TYPE_R8: + *(double*)data = *(UINT32*)srcData; + break; + + default: + _ASSERTE(!"Array.Copy from U4 to another type hit unsupported widening conversion"); + } + break; + + + case ELEMENT_TYPE_I8: + if (destElType == ELEMENT_TYPE_R4) + { + *(float*) data = (float) *(INT64*)srcData; + } + else + { + _ASSERTE(destElType==ELEMENT_TYPE_R8); + *(double*) data = (double) *(INT64*)srcData; + } + break; + + + case ELEMENT_TYPE_U8: + if (destElType == ELEMENT_TYPE_R4) + { + //*(float*) data = (float) *(UINT64*)srcData; + INT64 srcVal = *(INT64*)srcData; + float f = (float) srcVal; + if (srcVal < 0) + f += 4294967296.0f * 4294967296.0f; // This is 2^64 + + *(float*) data = f; + } + else + { + _ASSERTE(destElType==ELEMENT_TYPE_R8); + //*(double*) data = (double) *(UINT64*)srcData; + INT64 srcVal = *(INT64*)srcData; + double d = (double) srcVal; + if (srcVal < 0) + d += 4294967296.0 * 4294967296.0; // This is 2^64 + + *(double*) data = d; + } + break; + + + case ELEMENT_TYPE_R4: + *(double*) data = *(float*)srcData; + break; + + default: + _ASSERTE(!"Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); + } + } +} + +// +// 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 +// updated atomically. +// +// The CRT version of memmove does not always guarantee that updates of aligned fields stay atomic (e.g. it is using "rep movsb" in some cases). +// Type safety guarantees and background GC scanning requires object references in GC heap to be updated atomically. +// +void memmoveGCRefs(void *dest, const void *src, size_t len) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(dest)); + PRECONDITION(CheckPointer(src)); + PRECONDITION(len >= 0); + SO_TOLERANT; + } + CONTRACTL_END; + + // Make sure everything is pointer aligned + _ASSERTE(IS_ALIGNED(dest, sizeof(SIZE_T))); + _ASSERTE(IS_ALIGNED(src, sizeof(SIZE_T))); + _ASSERTE(IS_ALIGNED(len, sizeof(SIZE_T))); + + size_t size = len; + BYTE * dmem = (BYTE *)dest; + BYTE * smem = (BYTE *)src; + + GCHeapMemoryBarrier(); + + if (dmem <= smem || smem + size <= dmem) + { + // copy 16 bytes at a time + while (size >= 4 * sizeof(SIZE_T)) + { + size -= 4 * sizeof(SIZE_T); + ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0]; + ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1]; + ((SIZE_T *)dmem)[2] = ((SIZE_T *)smem)[2]; + ((SIZE_T *)dmem)[3] = ((SIZE_T *)smem)[3]; + smem += 4 * sizeof(SIZE_T); + dmem += 4 * sizeof(SIZE_T); + } + + if ((size & (2 * sizeof(SIZE_T))) != 0) + { + ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0]; + ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1]; + smem += 2 * sizeof(SIZE_T); + dmem += 2 * sizeof(SIZE_T); + } + + if ((size & sizeof(SIZE_T)) != 0) + { + ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0]; + } + } + else + { + smem += size; + dmem += size; + + // copy 16 bytes at a time + while (size >= 4 * sizeof(SIZE_T)) + { + size -= 4 * sizeof(SIZE_T); + smem -= 4 * sizeof(SIZE_T); + dmem -= 4 * sizeof(SIZE_T); + ((SIZE_T *)dmem)[3] = ((SIZE_T *)smem)[3]; + ((SIZE_T *)dmem)[2] = ((SIZE_T *)smem)[2]; + ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1]; + ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0]; + } + + if ((size & (2 * sizeof(SIZE_T))) != 0) + { + smem -= 2 * sizeof(SIZE_T); + dmem -= 2 * sizeof(SIZE_T); + ((SIZE_T *)dmem)[1] = ((SIZE_T *)smem)[1]; + ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0]; + } + + if ((size & sizeof(SIZE_T)) != 0) + { + smem -= sizeof(SIZE_T); + dmem -= sizeof(SIZE_T); + ((SIZE_T *)dmem)[0] = ((SIZE_T *)smem)[0]; + } + } + + GCHeap::GetGCHeap()->SetCardsAfterBulkCopy((Object**)dest, len); +} + +void ArrayNative::ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + SO_TOLERANT; + PRECONDITION(pSrc != NULL); + PRECONDITION(srcIndex >= 0); + PRECONDITION(pDest != NULL); + PRECONDITION(length > 0); + } + CONTRACTL_END; + + BYTE *src = (BYTE*)pSrc->GetDataPtr(); + BYTE *dst = (BYTE*)pDest->GetDataPtr(); + SIZE_T size = pSrc->GetComponentSize(); + + src += srcIndex * size; + dst += destIndex * size; + + if (pDest->GetMethodTable()->ContainsPointers()) + { + memmoveGCRefs(dst, src, length * size); + } + else + { + memmove(dst, src, length * size); + } +} + +FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable) +{ + FCALL_CONTRACT; + + BYTE *src; + BYTE *dst; + SIZE_T size; + + struct _gc + { + BASEARRAYREF pSrc; + BASEARRAYREF pDst; + } gc; + + gc.pSrc = (BASEARRAYREF)m_pSrc; + gc.pDst = (BASEARRAYREF)m_pDst; + + // + // creating a HelperMethodFrame is quite expensive, + // so we want to delay this for the most common case which doesn't trigger a GC. + // FCThrow is needed to throw an exception without a HelperMethodFrame + // + + // cannot pass null for source or destination + if (gc.pSrc == NULL || gc.pDst == NULL) { + FCThrowArgumentNullVoid(gc.pSrc==NULL ? W("source") : W("dest")); + } + + // source and destination must be arrays + _ASSERTE(gc.pSrc->GetMethodTable()->IsArray()); + _ASSERTE(gc.pDst->GetMethodTable()->IsArray()); + + // Equal method tables should imply equal rank + _ASSERTE(!(gc.pSrc->GetMethodTable() == gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank())); + + // Which enables us to avoid touching the EEClass in simple cases + if (gc.pSrc->GetMethodTable() != gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank()) { + FCThrowResVoid(kRankException, W("Rank_MustMatch")); + } + + g_IBCLogger.LogMethodTableAccess(gc.pSrc->GetMethodTable()); + g_IBCLogger.LogMethodTableAccess(gc.pDst->GetMethodTable()); + + int srcLB = gc.pSrc->GetLowerBoundsPtr()[0]; + int destLB = gc.pDst->GetLowerBoundsPtr()[0]; + // array bounds checking + const unsigned int srcLen = gc.pSrc->GetNumComponents(); + const unsigned int destLen = gc.pDst->GetNumComponents(); + if (m_iLength < 0) + FCThrowArgumentOutOfRangeVoid(W("length"), W("ArgumentOutOfRange_NeedNonNegNum")); + + if (m_iSrcIndex < srcLB || (m_iSrcIndex - srcLB < 0)) + FCThrowArgumentOutOfRangeVoid(W("srcIndex"), W("ArgumentOutOfRange_ArrayLB")); + + if (m_iDstIndex < destLB || (m_iDstIndex - destLB < 0)) + FCThrowArgumentOutOfRangeVoid(W("dstIndex"), W("ArgumentOutOfRange_ArrayLB")); + + if ((DWORD)(m_iSrcIndex - srcLB + m_iLength) > srcLen) + FCThrowResVoid(kArgumentException, W("Arg_LongerThanSrcArray")); + + if ((DWORD)(m_iDstIndex - destLB + m_iLength) > destLen) + FCThrowResVoid(kArgumentException, W("Arg_LongerThanDestArray")); + + int r = 0; + + // Small perf optimization - we copy from one portion of an array back to + // itself a lot when resizing collections, etc. The cost of doing the type + // checking is significant for copying small numbers of bytes (~half of the time + // for copying 1 byte within one array from element 0 to element 1). + if (gc.pSrc == gc.pDst) + r = AssignWillWork; + else + r = CanAssignArrayTypeNoGC(gc.pSrc, gc.pDst); + + if (r == AssignWrongType) { + FCThrowResVoid(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType")); + } + + if (r == AssignWillWork) { + if (m_iLength > 0) + ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength); + + FC_GC_POLL(); + return; + } + else if (reliable) { + FCThrowResVoid(kArrayTypeMismatchException, W("ArrayTypeMismatch_ConstrainedCopy")); + } + + HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); + if (r == AssignDontKnow) + { + r = CanAssignArrayType(gc.pSrc, gc.pDst); + } + CONSISTENCY_CHECK(r != AssignDontKnow); + + if (r == AssignWrongType) + COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType")); + + // If we were called from Array.ConstrainedCopy, ensure that the array copy + // is guaranteed to succeed. + _ASSERTE(!reliable || r == AssignWillWork); + + if (m_iLength > 0) + { + switch (r) + { + case AssignWillWork: + ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength); + break; + + case AssignUnboxValueClass: + UnBoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength); + break; + + case AssignBoxValueClassOrPrimitive: + BoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength); + break; + + case AssignMustCast: + CastCheckEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength); + break; + + case AssignPrimitiveWiden: + PrimitiveWiden(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength); + break; + + default: + _ASSERTE(!"Fell through switch in Array.Copy!"); + } + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + +FCIMPL3(void, ArrayNative::ArrayClear, ArrayBase* pArrayUNSAFE, INT32 iIndex, INT32 iLength) +{ + FCALL_CONTRACT; + + BASEARRAYREF pArray = (BASEARRAYREF)pArrayUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_1(pArray); + + // cannot pass null for array + if (pArray == NULL) + COMPlusThrowArgumentNull(W("array"), W("ArgumentNull_Array")); + + // array must be an array + _ASSERTE(pArray->GetMethodTable()->IsArray()); + + // array bounds checking + int lb = pArray->GetLowerBoundsPtr()[0]; + if (iIndex < lb || (iIndex - lb) < 0 || iLength < 0) + COMPlusThrow(kIndexOutOfRangeException); + + if ((iIndex - lb) > (int)pArray->GetNumComponents() - iLength) + COMPlusThrow(kIndexOutOfRangeException); + + if (iLength > 0) + { + char* array = (char*)pArray->GetDataPtr(); + + SIZE_T size = pArray->GetComponentSize(); + _ASSERTE(size >= 1); + + ZeroMemoryInGCHeap(array + (iIndex - lb) * size, iLength * size); + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + + + + + +// Check we're allowed to create an array with the given element type. +void ArrayNative::CheckElementType(TypeHandle elementType) +{ + // Check for simple types first. + if (!elementType.IsTypeDesc()) + { + MethodTable *pMT = elementType.AsMethodTable(); + + // TODO: We also should check for type/member visibility here. To do that we can replace + // the following chunk of code with a simple InvokeUtil::CanAccessClass call. + // But it's too late to make this change in Dev10 and we want SL4 to be compatible with Dev10. +#ifndef FEATURE_CORECLR + // Make sure security allows us access to the array type - if it is critical, convert that to a + // demand for full trust + if (!SecurityStackWalk::HasFlagsOrFullyTrusted(0)) + { + if (Security::TypeRequiresTransparencyCheck(pMT, true)) + { + // If we're creating a critical type, convert the critical check into a demand for full trust + Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_FULL_TRUST); + } + } +#else + if (Security::TypeRequiresTransparencyCheck(pMT)) + { + // The AccessCheckOptions flag doesn't matter because we just need to get the caller. + RefSecContext sCtx(AccessCheckOptions::kMemberAccess); + + AccessCheckOptions accessCheckOptions(InvokeUtil::GetInvocationAccessCheckType(), + NULL /*pAccessContext*/, + TRUE /*throwIfTargetIsInaccessible*/, + pMT /*pTargetMT*/); + + accessCheckOptions.DemandMemberAccessOrFail(&sCtx, pMT, FALSE /*visibilityCheck*/); + } +#endif // !FEATURE_CORECLR + + // Check for byref-like types. + if (pMT->IsByRefLike()) + COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike[]")); + + // Check for open generic types. + if (pMT->IsGenericTypeDefinition() || pMT->ContainsGenericVariables()) + COMPlusThrow(kNotSupportedException, W("NotSupported_OpenType")); + + // Check for Void. + if (elementType.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) + COMPlusThrow(kNotSupportedException, W("NotSupported_Void[]")); + + // That's all the dangerous simple types we know, it must be OK. + return; + } + + // Checks apply recursively for arrays of arrays etc. + if (elementType.IsArray()) + { + CheckElementType(elementType.GetElementType()); + return; + } + + // ByRefs and generic type variables are never allowed. + if (elementType.IsByRef() || elementType.IsGenericVariable()) + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + + // We can create pointers and function pointers, but it requires skip verification permission. + CorElementType etType = elementType.GetSignatureCorElementType(); + if (etType == ELEMENT_TYPE_PTR || etType == ELEMENT_TYPE_FNPTR) + { + Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER); + return; + } + + // We shouldn't get here (it means we've encountered a new type of typehandle if we do). + _ASSERTE(!"Shouldn't get here, unknown type handle type"); + COMPlusThrow(kNotSupportedException); +} + +FCIMPL4(Object*, ArrayNative::CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pLowerBounds) +{ + CONTRACTL { + FCALL_CHECK; + PRECONDITION(rank > 0); + PRECONDITION(CheckPointer(pLengths)); + PRECONDITION(CheckPointer(pLowerBounds, NULL_OK)); + } CONTRACTL_END; + + OBJECTREF pRet = NULL; + TypeHandle elementType = TypeHandle::FromPtr(elementTypeHandle); + + _ASSERTE(!elementType.IsNull()); + + // pLengths and pLowerBounds are pinned buffers. No need to protect them. + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + CheckElementType(elementType); + + CorElementType CorType = elementType.GetSignatureCorElementType(); + + CorElementType kind = ELEMENT_TYPE_ARRAY; + + // Is it ELEMENT_TYPE_SZARRAY array? + if (rank == 1 && (pLowerBounds == NULL || pLowerBounds[0] == 0) +#ifdef FEATURE_64BIT_ALIGNMENT + // On platforms where 64-bit types require 64-bit alignment and don't obtain it naturally force us + // through the slow path where this will be handled. + && (CorType != ELEMENT_TYPE_I8) + && (CorType != ELEMENT_TYPE_U8) + && (CorType != ELEMENT_TYPE_R8) +#endif + ) + { + // Shortcut for common cases + if (CorTypeInfo::IsPrimitiveType(CorType)) + { + pRet = AllocatePrimitiveArray(CorType,pLengths[0]); + goto Done; + } + else + if (CorTypeInfo::IsObjRef(CorType)) + { + pRet = AllocateObjectArray(pLengths[0],elementType); + goto Done; + } + + kind = ELEMENT_TYPE_SZARRAY; + pLowerBounds = NULL; + } + + { + // Find the Array class... + TypeHandle typeHnd = ClassLoader::LoadArrayTypeThrowing(elementType, kind, rank); + + DWORD boundsSize = 0; + INT32* bounds; + if (pLowerBounds != NULL) { + if (!ClrSafeInt<DWORD>::multiply(rank, 2, boundsSize)) + COMPlusThrowOM(); + DWORD dwAllocaSize = 0; + if (!ClrSafeInt<DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize)) + COMPlusThrowOM(); + + bounds = (INT32*) _alloca(dwAllocaSize); + + for (int i=0;i<rank;i++) { + bounds[2*i] = pLowerBounds[i]; + bounds[2*i+1] = pLengths[i]; + } + } + else { + boundsSize = rank; + + DWORD dwAllocaSize = 0; + if (!ClrSafeInt<DWORD>::multiply(boundsSize, sizeof(INT32), dwAllocaSize)) + COMPlusThrowOM(); + + bounds = (INT32*) _alloca(dwAllocaSize); + + // We need to create a private copy of pLengths to avoid holes caused + // by caller mutating the array + for (int i=0;i<rank;i++) + bounds[i] = pLengths[i]; + } + + pRet = AllocateArrayEx(typeHnd, bounds, boundsSize); + } + +Done: ; + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(pRet); +} +FCIMPLEND + + +FCIMPL4(void, ArrayNative::GetReference, ArrayBase* refThisUNSAFE, TypedByRef* elemRef, INT32 rank, INT32* pIndices) +{ + CONTRACTL { + FCALL_CHECK; + PRECONDITION(rank >= 0); + } CONTRACTL_END; + + // FC_GC_POLL not necessary. We poll for GC in Array.Rank that's always called + // right before this function + FC_GC_POLL_NOT_NEEDED(); + + BASEARRAYREF refThis = (BASEARRAYREF) refThisUNSAFE; + + _ASSERTE(rank == (INT32)refThis->GetRank()); + + SIZE_T Offset = 0; + const INT32 *pBoundsPtr = refThis->GetBoundsPtr(); + + if (rank == 1) + { + Offset = pIndices[0] - refThis->GetLowerBoundsPtr()[0]; + + // Bounds check each index + // Casting to unsigned allows us to use one compare for [0..limit-1] + if (((UINT32) Offset) >= ((UINT32) pBoundsPtr[0])) + FCThrowVoid(kIndexOutOfRangeException); + } + else + { + // Avoid redundant computation in GetLowerBoundsPtr + const INT32 *pLowerBoundsPtr = pBoundsPtr + rank; + _ASSERTE(refThis->GetLowerBoundsPtr() == pLowerBoundsPtr); + + SIZE_T Multiplier = 1; + + for (int i = rank; i >= 1; i--) { + INT32 curIndex = pIndices[i-1] - pLowerBoundsPtr[i-1]; + + // Bounds check each index + // Casting to unsigned allows us to use one compare for [0..limit-1] + if (((UINT32) curIndex) >= ((UINT32) pBoundsPtr[i-1])) + FCThrowVoid(kIndexOutOfRangeException); + + Offset += curIndex * Multiplier; + Multiplier *= pBoundsPtr[i-1]; + } + } + + TypeHandle arrayElementType = refThis->GetArrayElementTypeHandle(); + + // Legacy behavior + if (arrayElementType.IsTypeDesc()) + { + CorElementType elemtype = arrayElementType.AsTypeDesc()->GetInternalCorElementType(); + if (elemtype == ELEMENT_TYPE_PTR || elemtype == ELEMENT_TYPE_FNPTR) + FCThrowResVoid(kNotSupportedException, W("NotSupported_Type")); + } +#ifdef _DEBUG + CorElementType elemtype = arrayElementType.GetInternalCorElementType(); + _ASSERTE(elemtype != ELEMENT_TYPE_PTR && elemtype != ELEMENT_TYPE_FNPTR); +#endif + + elemRef->data = refThis->GetDataPtr() + (Offset * refThis->GetComponentSize()); + elemRef->type = arrayElementType; +} +FCIMPLEND + +FCIMPL2(void, ArrayNative::SetValue, TypedByRef * target, Object* objUNSAFE) +{ + FCALL_CONTRACT; + + OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE); + + TypeHandle thTarget(target->type); + + MethodTable* pTargetMT = thTarget.GetMethodTable(); + PREFIX_ASSUME(NULL != pTargetMT); + + if (obj == NULL) + { + // Null is the universal zero... + if (pTargetMT->IsValueType()) + InitValueClass(target->data,pTargetMT); + else + ClearObjectReference((OBJECTREF*)target->data); + } + else + if (thTarget == TypeHandle(g_pObjectClass)) + { + // Everything is compatible with Object + SetObjectReference((OBJECTREF*)target->data,(OBJECTREF)obj,GetAppDomain()); + } + else + if (!pTargetMT->IsValueType()) + { + if (ObjIsInstanceOfNoGC(OBJECTREFToObject(obj), thTarget) != TypeHandle::CanCast) + { + // target->data is protected by the caller + HELPER_METHOD_FRAME_BEGIN_1(obj); + + if (!ObjIsInstanceOf(OBJECTREFToObject(obj), thTarget)) + COMPlusThrow(kInvalidCastException,W("InvalidCast_StoreArrayElement")); + + HELPER_METHOD_FRAME_END(); + } + + SetObjectReference((OBJECTREF*)target->data,obj,GetAppDomain()); + } + else + { + // value class or primitive type + + if (!pTargetMT->UnBoxInto(target->data, obj)) + { + // target->data is protected by the caller + HELPER_METHOD_FRAME_BEGIN_1(obj); + + ARG_SLOT value = 0; + + // Allow enum -> primitive conversion, disallow primitive -> enum conversion + TypeHandle thSrc = obj->GetTypeHandle(); + CorElementType srcType = thSrc.GetVerifierCorElementType(); + CorElementType targetType = thTarget.GetSignatureCorElementType(); + + if (!InvokeUtil::IsPrimitiveType(srcType) || !InvokeUtil::IsPrimitiveType(targetType)) + COMPlusThrow(kInvalidCastException, W("InvalidCast_StoreArrayElement")); + + // Get a properly widened type + InvokeUtil::CreatePrimitiveValue(targetType,srcType,obj,&value); + + UINT cbSize = CorTypeInfo::Size(targetType); + memcpyNoGCRefs(target->data, ArgSlotEndianessFixup(&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); + + // Report the RVA field to the logger. + g_IBCLogger.LogRVADataAccess(pField); + + // 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 |