diff options
author | Tomáš Rylek <trylek@microsoft.com> | 2020-12-08 05:19:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-08 05:19:44 +0300 |
commit | 69e114c1abf91241a0eeecf1ecceab4711b8aa62 (patch) | |
tree | b81a0b35748f5e598412bcc504335cdbd322cd43 /src/coreclr/classlibnative | |
parent | 0ec07945a9759a72a689edbb01e69b232e26e05a (diff) |
December infra rollout - remove duplicated 'src' from coreclr subrepo (src/coreclr/src becomes src/coreclr) (#44973)
* Move src/coreclr/src/Directory.Build.targets to src/coreclr
Merge src/coreclr/src/CMakeLists.txt into src/coreclr/CMakeLists.txt
* Mechanical move of src/coreclr/src to src/coreclr
* Scripts adjustments to reflect the changed paths
Diffstat (limited to 'src/coreclr/classlibnative')
23 files changed, 5102 insertions, 0 deletions
diff --git a/src/coreclr/classlibnative/CMakeLists.txt b/src/coreclr/classlibnative/CMakeLists.txt new file mode 100644 index 00000000000..aeaba89b82b --- /dev/null +++ b/src/coreclr/classlibnative/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(BEFORE "../vm") +include_directories("../inc") +include_directories(BEFORE "../vm/${ARCH_SOURCES_DIR}") +include_directories("../debug/inc") +include_directories("../debug/inc/dump") + +add_subdirectory(bcltype) +add_subdirectory(float) diff --git a/src/coreclr/classlibnative/bcltype/CMakeLists.txt b/src/coreclr/classlibnative/bcltype/CMakeLists.txt new file mode 100644 index 00000000000..fdcf344c16a --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/CMakeLists.txt @@ -0,0 +1,20 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(BCLTYPE_SOURCES + arraynative.cpp + oavariant.cpp + objectnative.cpp + stringnative.cpp + system.cpp + varargsnative.cpp + variant.cpp +) + +add_library_clr(bcltype_obj + OBJECT + ${BCLTYPE_SOURCES} +) + +add_dependencies(bcltype_obj eventing_headers) +add_library(bcltype INTERFACE) +target_sources(bcltype INTERFACE $<TARGET_OBJECTS:bcltype_obj>) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp new file mode 100644 index 00000000000..50e652b5b66 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -0,0 +1,1168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// 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 "invokeutil.h" + +#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 + +// array is GC protected by caller +void ArrayInitializeWorker(ARRAYBASEREF * arrayRef, + MethodTable* pArrayMT, + MethodTable* pElemMT) +{ + STATIC_CONTRACT_MODE_COOPERATIVE; + + // Ensure that the array element type is fully loaded before executing its code + pElemMT->EnsureInstanceActive(); + + //can not use contract here because of SEH + _ASSERTE(IsProtectedByGCFrame (arrayRef)); + + SIZE_T offset = ArrayBase::GetDataPtrOffset(pArrayMT); + SIZE_T size = pArrayMT->GetComponentSize(); + SIZE_T cElements = (*arrayRef)->GetNumComponents(); + + MethodTable * pCanonMT = pElemMT->GetCanonicalMethodTable(); + WORD slot = pCanonMT->GetDefaultConstructorSlot(); + + PCODE ctorFtn = pCanonMT->GetSlot(slot); + +#if defined(TARGET_X86) && !defined(TARGET_UNIX) + BEGIN_CALL_TO_MANAGED(); + + + for (SIZE_T i = 0; i < cElements; i++) + { + // Since GetSlot() is not idempotent and may have returned + // a non-optimal entry-point the first time round. + if (i == 1) + { + ctorFtn = pCanonMT->GetSlot(slot); + } + + BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset); + +#ifdef _DEBUG + __asm { + mov ECX, thisPtr + mov EDX, pElemMT // Instantiation argument if the type is generic + call [ctorFtn] + nop // Mark the fact that we can call managed code + } +#else // _DEBUG + typedef void (__fastcall * CtorFtnType)(BYTE*, BYTE*); + (*(CtorFtnType)ctorFtn)(thisPtr, (BYTE*)pElemMT); +#endif // _DEBUG + + offset += size; + } + + END_CALL_TO_MANAGED(); +#else // TARGET_X86 && !TARGET_UNIX + // + // This is quite a bit slower, but it is portable. + // + + for (SIZE_T i =0; i < cElements; i++) + { + // Since GetSlot() is not idempotent and may have returned + // a non-optimal entry-point the first time round. + if (i == 1) + { + ctorFtn = pCanonMT->GetSlot(slot); + } + + BYTE* thisPtr = (((BYTE*) OBJECTREFToObject (*arrayRef)) + offset); + + PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(ctorFtn); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = PTR_TO_ARGHOLDER(thisPtr); + args[ARGNUM_1] = PTR_TO_ARGHOLDER(pElemMT); // Instantiation argument if the type is generic + CALL_MANAGED_METHOD_NORET(args); + + offset += size; + } +#endif // !TARGET_X86 || TARGET_UNIX +} + + +FCIMPL1(void, ArrayNative::Initialize, ArrayBase* array) +{ + FCALL_CONTRACT; + + if (array == NULL) + { + FCThrowVoid(kNullReferenceException); + } + + + MethodTable* pArrayMT = array->GetMethodTable(); + + TypeHandle thElem = pArrayMT->GetArrayElementTypeHandle(); + 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 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 + + +// 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; + + // 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(); + _ASSERTE(pSrcMT != pDestMT); // Handled by fast path + + TypeHandle srcTH = pSrcMT->GetArrayElementTypeHandle(); + TypeHandle destTH = pDestMT->GetArrayElementTypeHandle(); + _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; +} + + +// 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); + } + + 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.AsMethodTable(); + 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); + } + 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.AsMethodTable(); + PREFIX_ASSUME(pDestMT != NULL); + + SIZE_T destSize = pDest->GetComponentSize(); + 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.GetVerifierCorElementType(); + const CorElementType destElType = destTH.GetVerifierCorElementType(); + 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; + } + CONTRACTL_END; + + _ASSERTE(dest != nullptr); + _ASSERTE(src != nullptr); + + // 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))); + + _ASSERTE(CheckPointer(dest)); + _ASSERTE(CheckPointer(src)); + + if (len != 0 && dest != src) + { + InlinedMemmoveGCRefsHelper(dest, src, len); + } +} + +FCIMPL5(void, ArrayNative::CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength) +{ + FCALL_CONTRACT; + + struct _gc + { + BASEARRAYREF pSrc; + BASEARRAYREF pDst; + } gc; + + gc.pSrc = (BASEARRAYREF)pSrc; + gc.pDst = (BASEARRAYREF)pDst; + + // cannot pass null for source or destination + _ASSERTE(gc.pSrc != NULL && gc.pDst != NULL); + + // source and destination must be arrays + _ASSERTE(gc.pSrc->GetMethodTable()->IsArray()); + _ASSERTE(gc.pDst->GetMethodTable()->IsArray()); + + _ASSERTE(gc.pSrc->GetRank() == gc.pDst->GetRank()); + + // array bounds checking + _ASSERTE(iLength >= 0); + _ASSERTE(iSrcIndex >= 0); + _ASSERTE(iDstIndex >= 0); + _ASSERTE((DWORD)(iSrcIndex + iLength) <= gc.pSrc->GetNumComponents()); + _ASSERTE((DWORD)(iDstIndex + iLength) <= gc.pDst->GetNumComponents()); + + HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); + + int r = CanAssignArrayType(gc.pSrc, gc.pDst); + + if (r == AssignWrongType) + COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType")); + + if (iLength > 0) + { + switch (r) + { + case AssignUnboxValueClass: + UnBoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); + break; + + case AssignBoxValueClassOrPrimitive: + BoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); + break; + + case AssignMustCast: + CastCheckEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); + break; + + case AssignPrimitiveWiden: + PrimitiveWiden(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); + break; + + default: + _ASSERTE(!"Fell through switch in Array.Copy!"); + } + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + +// Check we're allowed to create an array with the given element type. +void ArrayNative::CheckElementType(TypeHandle elementType) +{ + // Checks apply recursively for arrays of arrays etc. + if (elementType.IsArray()) + { + CheckElementType(elementType.GetArrayElementTypeHandle()); + return; + } + + // Check for simple types first. + if (!elementType.IsTypeDesc()) + { + MethodTable *pMT = elementType.AsMethodTable(); + + // Check for byref-like types. + if (pMT->IsByRefLike()) + COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLikeArray")); + + // 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_VoidArray")); + + // That's all the dangerous simple types we know, it must be OK. + 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) + { + 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.AsMethodTable(); + 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); + } + else + if (!pTargetMT->IsValueType()) + { + if (ObjIsInstanceOfCached(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); + } + 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 diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h new file mode 100644 index 00000000000..c5be8403a8e --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: ArrayNative.h +// + +// +// ArrayNative +// This file defines the native methods for the Array +// + + +#ifndef _ARRAYNATIVE_H_ +#define _ARRAYNATIVE_H_ + +#include "fcall.h" + +struct FCALLRuntimeFieldHandle +{ + ReflectFieldObject *pFieldDONOTUSEDIRECTLY; +}; +#define FCALL_RFH_TO_REFLECTFIELD(x) (x).pFieldDONOTUSEDIRECTLY + +class ArrayNative +{ +public: + static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); + + static FCDECL1(void, Initialize, ArrayBase* pArray); + + static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); + static FCDECL5(void, CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength); + + // This method will create a new array of type type, with zero lower + // bounds and rank. + static FCDECL4(Object*, CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pBounds); + + // This method will return a TypedReference to the array element + static FCDECL4(void, GetReference, ArrayBase* refThisUNSAFE, TypedByRef* elemRef, INT32 rank, INT32* pIndices); + + // This set of methods will set a value in an array + static FCDECL2(void, SetValue, TypedByRef* target, Object* objUNSAFE); + + // This method will initialize an array from a TypeHandle + // to a field. + static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); + +private: + // Helper for CreateInstance + static void CheckElementType(TypeHandle elementType); + + // Return values for CanAssignArrayType + enum AssignArrayEnum + { + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, + }; + + // The following functions are all helpers for ArrayCopy + static AssignArrayEnum CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest); + static void CastCheckEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); + static void BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); + static void UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); + static void PrimitiveWiden(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); + +}; + +#endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/classlibnative/bcltype/arraynative.inl b/src/coreclr/classlibnative/bcltype/arraynative.inl new file mode 100644 index 00000000000..913b8c64939 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/arraynative.inl @@ -0,0 +1,325 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: ArrayNative.cpp +// + +// +// This file contains the native methods that support the Array class +// + +#ifndef _ARRAYNATIVE_INL_ +#define _ARRAYNATIVE_INL_ + +#include "gchelpers.inl" + +FORCEINLINE void InlinedForwardGCSafeCopyHelper(void *dest, const void *src, size_t len) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + _ASSERTE(dest != nullptr); + _ASSERTE(src != nullptr); + _ASSERTE(dest != src); + _ASSERTE(len != 0); + + // To be able to copy forwards, the destination buffer cannot start inside the source buffer + _ASSERTE((SIZE_T)dest - (SIZE_T)src >= len); + + // 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))); + + _ASSERTE(CheckPointer(dest)); + _ASSERTE(CheckPointer(src)); + + SIZE_T *dptr = (SIZE_T *)dest; + SIZE_T *sptr = (SIZE_T *)src; + + while (true) + { + if ((len & sizeof(SIZE_T)) != 0) + { + *dptr = *sptr; + + len ^= sizeof(SIZE_T); + if (len == 0) + { + return; + } + ++sptr; + ++dptr; + } + +#if defined(HOST_AMD64) && (defined(_MSC_VER) || defined(__GNUC__)) + if ((len & (2 * sizeof(SIZE_T))) != 0) + { + __m128 v = _mm_loadu_ps((float *)sptr); + _mm_storeu_ps((float *)dptr, v); + + len ^= 2 * sizeof(SIZE_T); + if (len == 0) + { + return; + } + sptr += 2; + dptr += 2; + } + + // Align the destination pointer to 16 bytes for the next set of 16-byte copies + if (((SIZE_T)dptr & sizeof(SIZE_T)) != 0) + { + *dptr = *sptr; + + ++sptr; + ++dptr; + len -= sizeof(SIZE_T); + if (len < 4 * sizeof(SIZE_T)) + { + continue; + } + } + + // Copy 32 bytes at a time + _ASSERTE(len >= 4 * sizeof(SIZE_T)); + do + { + __m128 v = _mm_loadu_ps((float *)sptr); + _mm_store_ps((float *)dptr, v); + v = _mm_loadu_ps((float *)(sptr + 2)); + _mm_store_ps((float *)(dptr + 2), v); + + sptr += 4; + dptr += 4; + len -= 4 * sizeof(SIZE_T); + } while (len >= 4 * sizeof(SIZE_T)); + if (len == 0) + { + return; + } +#else // !(defined(HOST_AMD64) && (defined(_MSC_VER) || defined(__GNUC__))) + if ((len & (2 * sizeof(SIZE_T))) != 0) + { + // Read two values and write two values to hint the use of wide loads and stores + SIZE_T p0 = sptr[0]; + SIZE_T p1 = sptr[1]; + dptr[0] = p0; + dptr[1] = p1; + + len ^= 2 * sizeof(SIZE_T); + if (len == 0) + { + return; + } + sptr += 2; + dptr += 2; + } + + // Copy 16 (on 32-bit systems) or 32 (on 64-bit systems) bytes at a time + _ASSERTE(len >= 4 * sizeof(SIZE_T)); + while (true) + { + // Read two values and write two values to hint the use of wide loads and stores + SIZE_T p0 = sptr[0]; + SIZE_T p1 = sptr[1]; + dptr[0] = p0; + dptr[1] = p1; + p0 = sptr[2]; + p1 = sptr[3]; + dptr[2] = p0; + dptr[3] = p1; + + len -= 4 * sizeof(SIZE_T); + if (len == 0) + { + return; + } + sptr += 4; + dptr += 4; + } +#endif // defined(HOST_AMD64) && (defined(_MSC_VER) || defined(__GNUC__)) + } +} + +FORCEINLINE void InlinedBackwardGCSafeCopyHelper(void *dest, const void *src, size_t len) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + _ASSERTE(dest != nullptr); + _ASSERTE(src != nullptr); + _ASSERTE(dest != src); + _ASSERTE(len != 0); + + // To be able to copy backwards, the source buffer cannot start inside the destination buffer + _ASSERTE((SIZE_T)src - (SIZE_T)dest >= len); + + // 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))); + + _ASSERTE(CheckPointer(dest)); + _ASSERTE(CheckPointer(src)); + + SIZE_T *dptr = (SIZE_T *)((BYTE *)dest + len); + SIZE_T *sptr = (SIZE_T *)((BYTE *)src + len); + + while (true) + { + if ((len & sizeof(SIZE_T)) != 0) + { + --sptr; + --dptr; + + *dptr = *sptr; + + len ^= sizeof(SIZE_T); + if (len == 0) + { + return; + } + } + +#if defined(HOST_AMD64) && (defined(_MSC_VER) || defined(__GNUC__)) + if ((len & (2 * sizeof(SIZE_T))) != 0) + { + sptr -= 2; + dptr -= 2; + + __m128 v = _mm_loadu_ps((float *)sptr); + _mm_storeu_ps((float *)dptr, v); + + len ^= 2 * sizeof(SIZE_T); + if (len == 0) + { + return; + } + } + + // Align the destination pointer to 16 bytes for the next set of 16-byte copies + if (((SIZE_T)dptr & sizeof(SIZE_T)) != 0) + { + --sptr; + --dptr; + + *dptr = *sptr; + + len -= sizeof(SIZE_T); + if (len < 4 * sizeof(SIZE_T)) + { + continue; + } + } + + // Copy 32 bytes at a time + _ASSERTE(len >= 4 * sizeof(SIZE_T)); + do + { + sptr -= 4; + dptr -= 4; + + __m128 v = _mm_loadu_ps((float *)(sptr + 2)); + _mm_store_ps((float *)(dptr + 2), v); + v = _mm_loadu_ps((float *)sptr); + _mm_store_ps((float *)dptr, v); + + len -= 4 * sizeof(SIZE_T); + } while (len >= 4 * sizeof(SIZE_T)); + if (len == 0) + { + return; + } +#else // !(defined(HOST_AMD64) && (defined(_MSC_VER) || defined(__GNUC__))) + if ((len & (2 * sizeof(SIZE_T))) != 0) + { + sptr -= 2; + dptr -= 2; + + // Read two values and write two values to hint the use of wide loads and stores + SIZE_T p1 = sptr[1]; + SIZE_T p0 = sptr[0]; + dptr[1] = p1; + dptr[0] = p0; + + len ^= 2 * sizeof(SIZE_T); + if (len == 0) + { + return; + } + } + + // Copy 16 (on 32-bit systems) or 32 (on 64-bit systems) bytes at a time + _ASSERTE(len >= 4 * sizeof(SIZE_T)); + do + { + sptr -= 4; + dptr -= 4; + + // Read two values and write two values to hint the use of wide loads and stores + SIZE_T p0 = sptr[2]; + SIZE_T p1 = sptr[3]; + dptr[2] = p0; + dptr[3] = p1; + p0 = sptr[0]; + p1 = sptr[1]; + dptr[0] = p0; + dptr[1] = p1; + + len -= 4 * sizeof(SIZE_T); + } while (len != 0); + return; +#endif // defined(HOST_AMD64) && (defined(_MSC_VER) || defined(__GNUC__)) + } +} + +FORCEINLINE void InlinedMemmoveGCRefsHelper(void *dest, const void *src, size_t len) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + _ASSERTE(dest != nullptr); + _ASSERTE(src != nullptr); + _ASSERTE(dest != src); + _ASSERTE(len != 0); + + // 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))); + + _ASSERTE(CheckPointer(dest)); + _ASSERTE(CheckPointer(src)); + + GCHeapMemoryBarrier(); + + // To be able to copy forwards, the destination buffer cannot start inside the source buffer + if ((size_t)dest - (size_t)src >= len) + { + InlinedForwardGCSafeCopyHelper(dest, src, len); + } + else + { + InlinedBackwardGCSafeCopyHelper(dest, src, len); + } + + InlinedSetCardsAfterBulkCopyHelper((Object**)dest, len); +} + +#endif // !_ARRAYNATIVE_INL_ diff --git a/src/coreclr/classlibnative/bcltype/oavariant.cpp b/src/coreclr/classlibnative/bcltype/oavariant.cpp new file mode 100644 index 00000000000..fd8a2948820 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/oavariant.cpp @@ -0,0 +1,429 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: OAVariant.cpp +// + +// +// Purpose: Wrapper for Ole Automation compatable math ops. +// Calls through to OleAut.dll +// + +// + +#include <common.h> + +#ifdef FEATURE_COMINTEROP + +#include <oleauto.h> +#include "excep.h" +#include "oavariant.h" +#include "comdatetime.h" // DateTime <-> OleAut date conversions +#include "interoputil.h" +#include "interopconverter.h" +#include "excep.h" +#include "string.h" +#include "comutilnative.h" // for COMDate + +#define INVALID_MAPPING (BYTE)(-1) + +static const BYTE CVtoVTTable [] = +{ + VT_EMPTY, // CV_EMPTY + VT_VOID, // CV_VOID + VT_BOOL, // CV_BOOLEAN + VT_UI2, // CV_CHAR + VT_I1, // CV_I1 + VT_UI1, // CV_U1 + VT_I2, // CV_I2 + VT_UI2, // CV_U2 + VT_I4, // CV_I4 + VT_UI4, // CV_U4 + VT_I8, // CV_I8 + VT_UI8, // CV_U8 + VT_R4, // CV_R4 + VT_R8, // CV_R8 + VT_BSTR, // CV_STRING + INVALID_MAPPING, // CV_PTR + VT_DATE, // CV_DATETIME + INVALID_MAPPING, // CV_TIMESPAN + VT_UNKNOWN, // CV_OBJECT + VT_DECIMAL, // CV_DECIMAL + VT_CY, // CV_CURRENCY + INVALID_MAPPING, // CV_ENUM + INVALID_MAPPING, // CV_MISSING + VT_NULL, // CV_NULL + INVALID_MAPPING // CV_LAST +}; + +static const BYTE VTtoCVTable[] = +{ + CV_EMPTY, // VT_EMPTY + CV_NULL, // VT_NULL + CV_I2, // VT_I2 + CV_I4, // VT_I4 + CV_R4, // VT_R4 + CV_R8, // VT_R8 + CV_CURRENCY,// VT_CY + CV_DATETIME,// VT_DATE + CV_STRING, // VT_BSTR + INVALID_MAPPING, // VT_DISPATCH + INVALID_MAPPING, // VT_ERROR + CV_BOOLEAN, // VT_BOOL + CV_OBJECT, // VT_VARIANT + CV_OBJECT, // VT_UNKNOWN + CV_DECIMAL, // VT_DECIMAL + INVALID_MAPPING, // An unused enum table entry + CV_I1, // VT_I1 + CV_U1, // VT_UI1 + CV_U2, // VT_UI2 + CV_U4, // VT_UI4 + CV_I8, // VT_I8 + CV_U8, // VT_UI8 + CV_I4, // VT_INT + CV_U4, // VT_UINT + CV_VOID // VT_VOID +}; + +// Need translations from CVType to VARENUM and vice versa. CVTypes +// is defined in COMVariant.h. VARENUM is defined in OleAut's variant.h +// Assumption here is we will only deal with VARIANTs and not other OLE +// constructs such as property sets or safe arrays. +VARENUM COMOAVariant::CVtoVT(const CVTypes cv) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(cv >= 0 && cv < CV_LAST); + } + CONTRACTL_END; + + if (CVtoVTTable[cv] == INVALID_MAPPING) + COMPlusThrow(kNotSupportedException, W("NotSupported_ChangeType")); + + return (VARENUM) CVtoVTTable[cv]; +} + +// Need translations from CVType to VARENUM and vice versa. CVTypes +// is defined in COMVariant.h. VARENUM is defined in OleAut's variant.h +CVTypes COMOAVariant::VTtoCV(const VARENUM vt) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(vt < VT_VOID); + } + CONTRACTL_END; + + if (vt <0 || vt > VT_VOID || VTtoCVTable[vt]==INVALID_MAPPING) + COMPlusThrow(kNotSupportedException, W("NotSupported_ChangeType")); + + return (CVTypes) VTtoCVTable[vt]; +} + + +// Converts a COM+ Variant to an OleAut Variant. Returns true if +// there was a native object allocated by this method that must be freed, +// else false. +bool COMOAVariant::ToOAVariant(const VariantData * const var, VARIANT * oa) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(var)); + PRECONDITION(CheckPointer(oa)); + } + CONTRACTL_END; + + SafeVariantInit(oa); + UINT64 * dest = (UINT64*) &V_UI1(oa); + *dest = 0; + + WCHAR * chars; + int strLen; + + // Set the data field of the OA Variant to be either the object reference + // or the data (ie int) that it needs. + + switch (var->GetType()) + { + case CV_STRING: + if (var->GetObjRef() == NULL) + { + V_BSTR(oa) = NULL; + V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType())); + + // OA perf feature: VarClear calls SysFreeString(null), which access violates. + return false; + } + + ((STRINGREF) (var->GetObjRef()))->RefInterpretGetStringValuesDangerousForGC(&chars, &strLen); + V_BSTR(oa) = SysAllocStringLen(chars, strLen); + if (V_BSTR(oa) == NULL) + COMPlusThrowOM(); + + V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType())); + + return true; + + case CV_CHAR: + chars = (WCHAR*) var->GetData(); + V_BSTR(oa) = SysAllocStringLen(chars, 1); + if (V_BSTR(oa) == NULL) + COMPlusThrowOM(); + + // We should override the VTtoVT default of VT_UI2 for this case. + V_VT(oa) = VT_BSTR; + + return true; + + case CV_DATETIME: + V_DATE(oa) = COMDateTime::TicksToDoubleDate(var->GetDataAsInt64()); + V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType())); + return false; + + case CV_BOOLEAN: + V_BOOL(oa) = (var->GetDataAsInt64()==0 ? VARIANT_FALSE : VARIANT_TRUE); + V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType())); + return false; + + case CV_DECIMAL: + { + OBJECTREF obj = var->GetObjRef(); + DECIMAL * d = (DECIMAL*) obj->GetData(); + // DECIMALs and Variants are the same size. Variants are a union between + // all the normal Variant fields (vt, bval, etc) and a Decimal. Decimals + // also have the first 2 bytes reserved, for a VT field. + + V_DECIMAL(oa) = *d; + V_VT(oa) = VT_DECIMAL; + return false; + } + + case CV_OBJECT: + { + OBJECTREF obj = var->GetObjRef(); + GCPROTECT_BEGIN(obj) + { + IUnknown *pUnk = NULL; + + // Convert the object to an IDispatch/IUnknown pointer. + ComIpType FetchedIpType = ComIpType_None; + pUnk = GetComIPFromObjectRef(&obj, ComIpType_Both, &FetchedIpType); + V_UNKNOWN(oa) = pUnk; + V_VT(oa) = static_cast<VARTYPE>(FetchedIpType == ComIpType_Dispatch ? VT_DISPATCH : VT_UNKNOWN); + } + GCPROTECT_END(); + return true; + } + + default: + *dest = var->GetDataAsInt64(); + V_VT(oa) = static_cast<VARTYPE>(CVtoVT(var->GetType())); + return false; + } +} + +// Converts an OleAut Variant into a COM+ Variant. +// NOte that we pass the VariantData Byref so that if GC happens, 'var' gets updated +void COMOAVariant::FromOAVariant(const VARIANT * const oa, VariantData * const& var) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(oa)); + } + CONTRACTL_END; + + // Clear the return variant value. It's allocated on + // the stack and we only want valid state data in there. + memset(var, 0, sizeof(VariantData)); + + CVTypes type = VTtoCV((VARENUM) V_VT(oa)); + var->SetType(type); + + switch (type) + { + case CV_STRING: + { + // BSTRs have an int with the string buffer length (not the string length) + // followed by the data. The pointer to the BSTR points to the start of the + // characters, NOT the start of the BSTR. + WCHAR * chars = V_BSTR(oa); + int strLen = SysStringLen(V_BSTR(oa)); + STRINGREF str = StringObject::NewString(chars, strLen); + var->SetObjRef((OBJECTREF)str); + break; + } + case CV_DATETIME: + var->SetDataAsInt64(COMDateTime::DoubleDateToTicks(V_DATE(oa))); + break; + + case CV_BOOLEAN: + var->SetDataAsInt64(V_BOOL(oa)==VARIANT_FALSE ? 0 : 1); + break; + + case CV_DECIMAL: + { + MethodTable * pDecimalMT = GetTypeHandleForCVType(CV_DECIMAL).GetMethodTable(); + _ASSERTE(pDecimalMT); + OBJECTREF pDecimalRef = AllocateObject(pDecimalMT); + + *(DECIMAL *) pDecimalRef->GetData() = V_DECIMAL(oa); + var->SetObjRef(pDecimalRef); + break; + } + + + // All types less than 4 bytes need an explicit cast from their original + // type to be sign extended to 8 bytes. This makes Variant's ToInt32 + // function simpler for these types. + case CV_I1: + var->SetDataAsInt64(V_I1(oa)); + break; + + case CV_U1: + var->SetDataAsInt64(V_UI1(oa)); + break; + + case CV_I2: + var->SetDataAsInt64(V_I2(oa)); + break; + + case CV_U2: + var->SetDataAsInt64(V_UI2(oa)); + break; + + case CV_EMPTY: + case CV_NULL: + // Must set up the Variant's m_or to the appropriate classes. + // Note that OleAut doesn't have any VT_MISSING. + VariantData::NewVariant(var, type, NULL + DEBUG_ARG(TRUE)); + break; + + case CV_OBJECT: + { + // Convert the IUnknown pointer to an OBJECTREF. + OBJECTREF oref = NULL; + GCPROTECT_BEGIN(oref); + GetObjectRefFromComIP(&oref, V_UNKNOWN(oa)); + var->SetObjRef(oref); + GCPROTECT_END(); + break; + } + default: + // Copy all the bits there, and make sure we don't do any float to int conversions. + void * src = (void*) &(V_UI1(oa)); + var->SetData(src); + } +} + +void COMOAVariant::OAFailed(const HRESULT hr) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(FAILED(hr)); + } + CONTRACTL_END; + + switch (hr) + { + case E_OUTOFMEMORY: + COMPlusThrowOM(); + + case DISP_E_BADVARTYPE: + COMPlusThrow(kNotSupportedException, W("NotSupported_OleAutBadVarType")); + + case DISP_E_DIVBYZERO: + COMPlusThrow(kDivideByZeroException); + + case DISP_E_OVERFLOW: + COMPlusThrow(kOverflowException); + + case DISP_E_TYPEMISMATCH: + COMPlusThrow(kInvalidCastException, W("InvalidCast_OATypeMismatch")); + + case E_INVALIDARG: + COMPlusThrow(kArgumentException); + break; + + default: + _ASSERTE(!"Unrecognized HResult - OAVariantLib routine failed in an unexpected way!"); + COMPlusThrowHR(hr); + } +} + +FCIMPL6(void, COMOAVariant::ChangeTypeEx, VariantData *result, VariantData *op, LCID lcid, void *targetType, int cvType, INT16 flags) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(result)); + } + CONTRACTL_END; + + HELPER_METHOD_FRAME_BEGIN_0(); + GCPROTECT_BEGININTERIOR (result); + + BOOL fConverted = FALSE; + + TypeHandle thTarget = TypeHandle::FromPtr(targetType); + if (cvType == CV_OBJECT && IsTypeRefOrDef(g_ColorClassName, thTarget.GetModule(), thTarget.GetCl())) + { + if (op->GetType() == CV_I4 || op->GetType() == CV_U4) + { + // Int32/UInt32 can be converted to System.Drawing.Color + SYSTEMCOLOR SystemColor; + ConvertOleColorToSystemColor(op->GetDataAsUInt32(), &SystemColor); + + result->SetObjRef(thTarget.AsMethodTable()->Box(&SystemColor)); + result->SetType(CV_OBJECT); + + fConverted = TRUE; + } + } + + if (!fConverted) + { + VariantHolder ret; + VariantHolder vOp; + + VARENUM vt = CVtoVT((CVTypes) cvType); + ToOAVariant(op, &vOp); + + HRESULT hr = SafeVariantChangeTypeEx(&ret, &vOp, lcid, flags, static_cast<VARTYPE>(vt)); + + if (FAILED(hr)) + OAFailed(hr); + + if ((CVTypes) cvType == CV_CHAR) + { + result->SetType(CV_CHAR); + result->SetDataAsUInt16(V_UI2(&ret)); + } + else + { + FromOAVariant(&ret, result); + } + } + + GCPROTECT_END (); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +#endif // FEATURE_COMINTEROP diff --git a/src/coreclr/classlibnative/bcltype/oavariant.h b/src/coreclr/classlibnative/bcltype/oavariant.h new file mode 100644 index 00000000000..de09ae7f873 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/oavariant.h @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: OAVariant.h +// + +// +// Purpose: Wrapper for Ole Automation compatable math ops. +// Calls through to OleAut.dll +// + +// + +#ifndef _OAVARIANT_H_ +#define _OAVARIANT_H_ + +#ifndef FEATURE_COMINTEROP +#error FEATURE_COMINTEROP is required for this file +#endif // FEATURE_COMINTEROP + +#include "variant.h" + +class COMOAVariant +{ +public: + + // Utility Functions + // Conversion between COM+ variant type field & OleAut Variant enumeration + // WinCE doesn't support Variants entirely. + static VARENUM CVtoVT(const CVTypes cv); + static CVTypes VTtoCV(const VARENUM vt); + + // Conversion between COM+ Variant & OleAut Variant. ToOAVariant + // returns true if the conversion process allocated an object (like a BSTR). + static bool ToOAVariant(const VariantData * const var, VARIANT * oa); + static void FromOAVariant(const VARIANT * const oa, VariantData * const& var); + + // Throw a specific exception for a failure, specified by a given HRESULT. + static void OAFailed(const HRESULT hr); + + static FCDECL6(void, ChangeTypeEx, VariantData *result, VariantData *op, LCID lcid, void *targetType, int cvType, INT16 flags); +}; + + +#endif // _OAVARIANT_H_ diff --git a/src/coreclr/classlibnative/bcltype/objectnative.cpp b/src/coreclr/classlibnative/bcltype/objectnative.cpp new file mode 100644 index 00000000000..80023e27a9d --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/objectnative.cpp @@ -0,0 +1,336 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: ObjectNative.cpp +// + +// +// Purpose: Native methods on System.Object +// +// + +#include "common.h" + +#include "objectnative.h" +#include "excep.h" +#include "vars.hpp" +#include "field.h" +#include "object.h" +#include "comsynchronizable.h" +#include "eeconfig.h" + + +/********************************************************************/ +/* gets an object's 'value'. For normal classes, with reference + based semantics, this means the object's pointer. For boxed + primitive types, it also means just returning the pointer (because + they are immutable), for other value class, it means returning + a boxed copy. */ + +FCIMPL1(Object*, ObjectNative::GetObjectValue, Object* obj) +{ + CONTRACTL + { + FCALL_CHECK; + INJECT_FAULT(FCThrow(kOutOfMemoryException);); + } + CONTRACTL_END; + + VALIDATEOBJECT(obj); + + if (obj == 0) + return(obj); + + MethodTable* pMT = obj->GetMethodTable(); + // optimize for primitive types since GetVerifierCorElementType is slow. + if (pMT->IsTruePrimitive() || TypeHandle(pMT).GetVerifierCorElementType() != ELEMENT_TYPE_VALUETYPE) { + return(obj); + } + + Object* retVal = NULL; + OBJECTREF objRef(obj); + HELPER_METHOD_FRAME_BEGIN_RET_1(objRef); // Set up a frame + + // Technically we could return boxed DateTimes and Decimals without + // copying them here, but VB realized that this would be a breaking change + // for their customers. So copy them. + // + // MethodTable::Box is a cleaner way to copy value class, but it is slower than following code. + // + retVal = OBJECTREFToObject(AllocateObject(pMT)); + CopyValueClass(retVal->GetData(), objRef->GetData(), pMT); + HELPER_METHOD_FRAME_END(); + + return(retVal); +} +FCIMPLEND + + +NOINLINE static INT32 GetHashCodeHelper(OBJECTREF objRef) +{ + DWORD idx = 0; + + FC_INNER_PROLOG(ObjectNative::GetHashCode); + + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef); + + idx = objRef->GetHashCodeEx(); + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); + return idx; +} + +// Note that we obtain a sync block index without actually building a sync block. +// That's because a lot of objects are hashed, without requiring support for +FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) { + + CONTRACTL + { + FCALL_CHECK; + INJECT_FAULT(FCThrow(kOutOfMemoryException);); + } + CONTRACTL_END; + + VALIDATEOBJECT(obj); + + if (obj == 0) + return 0; + + OBJECTREF objRef(obj); + + { + DWORD bits = objRef->GetHeader()->GetBits(); + + if (bits & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) + { + if (bits & BIT_SBLK_IS_HASHCODE) + { + // Common case: the object already has a hash code + return bits & MASK_HASHCODE; + } + else + { + // We have a sync block index. This means if we already have a hash code, + // it is in the sync block, otherwise we generate a new one and store it there + SyncBlock *psb = objRef->PassiveGetSyncBlock(); + if (psb != NULL) + { + DWORD hashCode = psb->GetHashCode(); + if (hashCode != 0) + return hashCode; + } + } + } + } + + FC_INNER_RETURN(INT32, GetHashCodeHelper(objRef)); +} +FCIMPLEND + +// +// Compare by ref for normal classes, by value for value types. +// +// <TODO>@todo: it would be nice to customize this method based on the +// defining class rather than doing a runtime check whether it is +// a value type.</TODO> +// + +FCIMPL2(FC_BOOL_RET, ObjectNative::Equals, Object *pThisRef, Object *pCompareRef) +{ + CONTRACTL + { + FCALL_CHECK; + INJECT_FAULT(FCThrow(kOutOfMemoryException);); + } + CONTRACTL_END; + + if (pThisRef == pCompareRef) + FC_RETURN_BOOL(TRUE); + + // Since we are in FCALL, we must handle NULL specially. + if (pThisRef == NULL || pCompareRef == NULL) + FC_RETURN_BOOL(FALSE); + + MethodTable *pThisMT = pThisRef->GetMethodTable(); + + // If it's not a value class, don't compare by value + if (!pThisMT->IsValueType()) + FC_RETURN_BOOL(FALSE); + + // Make sure they are the same type. + if (pThisMT != pCompareRef->GetMethodTable()) + FC_RETURN_BOOL(FALSE); + + // Compare the contents (size - vtable - sync block index). + DWORD dwBaseSize = pThisRef->GetMethodTable()->GetBaseSize(); + if(pThisRef->GetMethodTable() == g_pStringClass) + dwBaseSize -= sizeof(WCHAR); + BOOL ret = memcmp( + (void *) (pThisRef+1), + (void *) (pCompareRef+1), + dwBaseSize - sizeof(Object) - sizeof(int)) == 0; + + FC_GC_POLL_RET(); + + FC_RETURN_BOOL(ret); +} +FCIMPLEND + +NOINLINE static Object* GetClassHelper(OBJECTREF objRef) +{ + FC_INNER_PROLOG(ObjectNative::GetClass); + _ASSERTE(objRef != NULL); + TypeHandle typeHandle = objRef->GetTypeHandle(); + OBJECTREF refType = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refType); + + refType = typeHandle.GetManagedClassObject(); + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); + return OBJECTREFToObject(refType); +} + +// This routine is called by the Object.GetType() routine. It is a major way to get the Sytem.Type +FCIMPL1(Object*, ObjectNative::GetClass, Object* pThis) +{ + CONTRACTL + { + FCALL_CHECK; + INJECT_FAULT(FCThrow(kOutOfMemoryException);); + } + CONTRACTL_END; + + OBJECTREF objRef = ObjectToOBJECTREF(pThis); + if (objRef != NULL) + { + MethodTable* pMT = objRef->GetMethodTable(); + OBJECTREF typePtr = pMT->GetManagedClassObjectIfExists(); + if (typePtr != NULL) + { + return OBJECTREFToObject(typePtr); + } + } + else + FCThrow(kNullReferenceException); + + FC_INNER_RETURN(Object*, GetClassHelper(objRef)); +} +FCIMPLEND + +FCIMPL1(Object*, ObjectNative::AllocateUninitializedClone, Object* pObjUNSAFE) +{ + FCALL_CONTRACT; + + // Delegate error handling to managed side (it will throw NullRefenceException) + if (pObjUNSAFE == NULL) + return NULL; + + OBJECTREF refClone = ObjectToOBJECTREF(pObjUNSAFE); + + HELPER_METHOD_FRAME_BEGIN_RET_1(refClone); + + MethodTable* pMT = refClone->GetMethodTable(); + + // assert that String has overloaded the Clone() method + _ASSERTE(pMT != g_pStringClass); + + if (pMT->IsArray()) { + refClone = DupArrayForCloning((BASEARRAYREF)refClone); + } else { + // We don't need to call the <cinit> because we know + // that it has been called....(It was called before this was created) + refClone = AllocateObject(pMT); + } + + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(refClone); +} +FCIMPLEND + +FCIMPL2(FC_BOOL_RET, ObjectNative::WaitTimeout, INT32 Timeout, Object* pThisUNSAFE) +{ + FCALL_CONTRACT; + + BOOL retVal = FALSE; + OBJECTREF pThis = (OBJECTREF) pThisUNSAFE; + HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); + + // Arguments validated on managed side + _ASSERTE(pThis != NULL); + _ASSERTE(Timeout >= INFINITE_TIMEOUT); + + retVal = pThis->Wait(Timeout); + + HELPER_METHOD_FRAME_END(); + FC_RETURN_BOOL(retVal); +} +FCIMPLEND + +FCIMPL1(void, ObjectNative::Pulse, Object* pThisUNSAFE) +{ + FCALL_CONTRACT; + + OBJECTREF pThis = (OBJECTREF) pThisUNSAFE; + HELPER_METHOD_FRAME_BEGIN_1(pThis); + + if (pThis == NULL) + COMPlusThrow(kNullReferenceException, W("NullReference_This")); + + pThis->Pulse(); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL1(void, ObjectNative::PulseAll, Object* pThisUNSAFE) +{ + FCALL_CONTRACT; + + OBJECTREF pThis = (OBJECTREF) pThisUNSAFE; + HELPER_METHOD_FRAME_BEGIN_1(pThis); + + if (pThis == NULL) + COMPlusThrow(kNullReferenceException, W("NullReference_This")); + + pThis->PulseAll(); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL1(FC_BOOL_RET, ObjectNative::IsLockHeld, Object* pThisUNSAFE) +{ + FCALL_CONTRACT; + + BOOL retVal; + DWORD owningThreadId; + DWORD acquisitionCount; + + // + // If the lock is held, check if it's held by the current thread. + // + retVal = pThisUNSAFE->GetThreadOwningMonitorLock(&owningThreadId, &acquisitionCount); + if (retVal) + retVal = GetThread()->GetThreadId() == owningThreadId; + + FC_RETURN_BOOL(retVal); +} +FCIMPLEND + +INT64 QCALLTYPE ObjectNative::GetMonitorLockContentionCount() +{ + QCALL_CONTRACT; + + INT64 result = 0; + + BEGIN_QCALL; + + result = (INT64)Thread::GetTotalMonitorLockContentionCount(); + + END_QCALL; + return result; +} diff --git a/src/coreclr/classlibnative/bcltype/objectnative.h b/src/coreclr/classlibnative/bcltype/objectnative.h new file mode 100644 index 00000000000..cc084e4d3b5 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/objectnative.h @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: ObjectNative.h +// + +// +// Purpose: Native methods on System.Object +// +// + +#ifndef _OBJECTNATIVE_H_ +#define _OBJECTNATIVE_H_ + +#include "fcall.h" + + +// +// Each function that we call through native only gets one argument, +// which is actually a pointer to it's stack of arguments. Our structs +// for accessing these are defined below. +// + +class ObjectNative +{ +public: + + // This method will return a Class object for the object + // iff the Class object has already been created. + // If the Class object doesn't exist then you must call the GetClass() method. + static FCDECL1(Object*, GetObjectValue, Object* vThisRef); + static FCDECL1(INT32, GetHashCode, Object* vThisRef); + static FCDECL2(FC_BOOL_RET, Equals, Object *pThisRef, Object *pCompareRef); + static FCDECL1(Object*, AllocateUninitializedClone, Object* pObjUNSAFE); + static FCDECL1(Object*, GetClass, Object* pThis); + static FCDECL2(FC_BOOL_RET, WaitTimeout, INT32 Timeout, Object* pThisUNSAFE); + static FCDECL1(void, Pulse, Object* pThisUNSAFE); + static FCDECL1(void, PulseAll, Object* pThisUNSAFE); + static FCDECL1(FC_BOOL_RET, IsLockHeld, Object* pThisUNSAFE); + static INT64 QCALLTYPE GetMonitorLockContentionCount(); +}; + +#endif // _OBJECTNATIVE_H_ diff --git a/src/coreclr/classlibnative/bcltype/stringnative.cpp b/src/coreclr/classlibnative/bcltype/stringnative.cpp new file mode 100644 index 00000000000..0c3ec4f8dc7 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/stringnative.cpp @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: StringNative.cpp +// + +// +// Purpose: The implementation of the String class. +// + +// + +#include "common.h" + +#include "object.h" +#include "utilcode.h" +#include "excep.h" +#include "frames.h" +#include "field.h" +#include "vars.hpp" +#include "stringnative.h" +#include "comutilnative.h" +#include "metasig.h" +#include "excep.h" + +// Compile the string functionality with these pragma flags (equivalent of the command line /Ox flag) +// Compiling this functionality differently gives us significant throughout gain in some cases. +#if defined(_MSC_VER) && defined(TARGET_X86) +#pragma optimize("tgy", on) +#endif + + +/*==================================GETCHARAT=================================== +**Returns the character at position index. Thows IndexOutOfRangeException as +**appropriate. +**This method is not actually used. JIT will generate code for indexer method on string class. +** +==============================================================================*/ +FCIMPL2(FC_CHAR_RET, COMString::GetCharAt, StringObject* str, INT32 index) { + FCALL_CONTRACT; + + FC_GC_POLL_NOT_NEEDED(); + VALIDATEOBJECT(str); + if (str == NULL) { + FCThrow(kNullReferenceException); + } + _ASSERTE(str->GetMethodTable() == g_pStringClass); + + if (index >=0 && index < (INT32)str->GetStringLength()) { + //Return the appropriate character. + return str->GetBuffer()[index]; + } + + FCThrow(kIndexOutOfRangeException); +} +FCIMPLEND + + +/*==================================LENGTH=================================== */ + +FCIMPL1(INT32, COMString::Length, StringObject* str) { + FCALL_CONTRACT; + + FC_GC_POLL_NOT_NEEDED(); + if (str == NULL) + FCThrow(kNullReferenceException); + + FCUnique(0x11); + return str->GetStringLength(); +} +FCIMPLEND + + +FCIMPL2(FC_BOOL_RET, COMString::FCTryGetTrailByte, StringObject* thisRefUNSAFE, UINT8 *pbData) +{ + FCALL_CONTRACT; + + STRINGREF thisRef = ObjectToSTRINGREF(thisRefUNSAFE); + FC_RETURN_BOOL(thisRef->GetTrailByte(pbData)); +} +FCIMPLEND + +FCIMPL2(VOID, COMString::FCSetTrailByte, StringObject* thisRefUNSAFE, UINT8 bData) +{ + FCALL_CONTRACT; + + STRINGREF thisRef = ObjectToSTRINGREF(thisRefUNSAFE); + HELPER_METHOD_FRAME_BEGIN_1(thisRef); + + thisRef->SetTrailByte(bData); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +// Revert to command line compilation flags +#if defined(_MSC_VER) && defined(TARGET_X86) +#pragma optimize ("", on) +#endif diff --git a/src/coreclr/classlibnative/bcltype/stringnative.h b/src/coreclr/classlibnative/bcltype/stringnative.h new file mode 100644 index 00000000000..1396564aef9 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/stringnative.h @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: StringNative.h +// + +// +// Purpose: Contains types and method signatures for the String class +// + +// + +#include "fcall.h" +#include "qcall.h" +#include "excep.h" + +#ifndef _STRINGNATIVE_H_ +#define _STRINGNATIVE_H_ +// +// Each function that we call through native only gets one argument, +// which is actually a pointer to it's stack of arguments. Our structs +// for accessing these are defined below. +// + +// +//These are the type signatures for String +// +// +// The method signatures for each of the methods we define. +// N.B.: There's a one-to-one mapping between the method signatures and the +// type definitions given above. +// + + +// Compile the string functionality with these pragma flags (equivalent of the command line /Ox flag) +// Compiling this functionality differently gives us significant throughout gain in some cases. +#if defined(_MSC_VER) && defined(TARGET_X86) +#pragma optimize("tgy", on) +#endif + +class COMString { +public: + // + // Query Methods + // + static FCDECL2(FC_CHAR_RET, GetCharAt, StringObject* pThisRef, INT32 index); + static FCDECL1(INT32, Length, StringObject* pThisRef); + + // + // Interop + // + static FCDECL2(FC_BOOL_RET, FCTryGetTrailByte, StringObject* thisRefUNSAFE, UINT8 *pbData); + static FCDECL2(VOID, FCSetTrailByte, StringObject* thisRefUNSAFE, UINT8 bData); +}; + +// Revert to command line compilation flags +#if defined(_MSC_VER) && defined(TARGET_X86) +#pragma optimize ("", on) +#endif + +#endif // _STRINGNATIVE_H_ + + + + + + diff --git a/src/coreclr/classlibnative/bcltype/system.cpp b/src/coreclr/classlibnative/bcltype/system.cpp new file mode 100644 index 00000000000..3660c14963d --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/system.cpp @@ -0,0 +1,606 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: System.cpp +// + +// +// Purpose: Native methods on System.Environment & Array +// + +// + +#include "common.h" + +#include <object.h> + +#include "ceeload.h" + +#include "excep.h" +#include "frames.h" +#include "vars.hpp" +#include "classnames.h" +#include "system.h" +#include "string.h" +#include "sstring.h" +#include "eeconfig.h" +#include "assemblynative.hpp" +#include "generics.h" +#include "invokeutil.h" +#include "array.h" +#include "eepolicy.h" + +#ifndef TARGET_UNIX +typedef void(WINAPI *pfnGetSystemTimeAsFileTime)(LPFILETIME lpSystemTimeAsFileTime); +extern pfnGetSystemTimeAsFileTime g_pfnGetSystemTimeAsFileTime; + +void WINAPI InitializeGetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) +{ + pfnGetSystemTimeAsFileTime func = NULL; + + HMODULE hKernel32 = WszLoadLibrary(W("kernel32.dll")); + if (hKernel32 != NULL) + { + func = (pfnGetSystemTimeAsFileTime)GetProcAddress(hKernel32, "GetSystemTimePreciseAsFileTime"); + if (func != NULL) + { + // GetSystemTimePreciseAsFileTime exists and we'd like to use it. However, on + // misconfigured systems, it's possible for the "precise" time to be inaccurate: + // https://github.com/dotnet/runtime/issues/9014 + // If it's inaccurate, though, we expect it to be wildly inaccurate, so as a + // workaround/heuristic, we get both the "normal" and "precise" times, and as + // long as they're close, we use the precise one. This workaround can be removed + // when we better understand what's causing the drift and the issue is no longer + // a problem or can be better worked around on all targeted OSes. + + FILETIME systemTimeResult; + ::GetSystemTimeAsFileTime(&systemTimeResult); + + FILETIME preciseSystemTimeResult; + func(&preciseSystemTimeResult); + + LONG64 systemTimeLong100ns = (LONG64)((((ULONG64)systemTimeResult.dwHighDateTime) << 32) | (ULONG64)systemTimeResult.dwLowDateTime); + LONG64 preciseSystemTimeLong100ns = (LONG64)((((ULONG64)preciseSystemTimeResult.dwHighDateTime) << 32) | (ULONG64)preciseSystemTimeResult.dwLowDateTime); + + const INT32 THRESHOLD_100NS = 1000000; // 100ms + if (abs(preciseSystemTimeLong100ns - systemTimeLong100ns) > THRESHOLD_100NS) + { + // Too much difference. Don't use GetSystemTimePreciseAsFileTime. + func = NULL; + } + } + } + if (func == NULL) + { + func = &::GetSystemTimeAsFileTime; + } + + InterlockedCompareExchangeT(&g_pfnGetSystemTimeAsFileTime, func, &InitializeGetSystemTimeAsFileTime); + + g_pfnGetSystemTimeAsFileTime(lpSystemTimeAsFileTime); +} + +pfnGetSystemTimeAsFileTime g_pfnGetSystemTimeAsFileTime = &InitializeGetSystemTimeAsFileTime; +#endif // TARGET_UNIX + +FCIMPL0(INT64, SystemNative::__GetSystemTimeAsFileTime) +{ + FCALL_CONTRACT; + + INT64 timestamp; +#ifndef TARGET_UNIX + g_pfnGetSystemTimeAsFileTime((FILETIME*)×tamp); +#else + GetSystemTimeAsFileTime((FILETIME*)×tamp); +#endif + +#if BIGENDIAN + timestamp = (INT64)(((UINT64)timestamp >> 32) | ((UINT64)timestamp << 32)); +#endif + + return timestamp; +} +FCIMPLEND; + + +#ifndef TARGET_UNIX + +FCIMPL1(VOID, SystemNative::GetSystemTimeWithLeapSecondsHandling, FullSystemTime *time) +{ + FCALL_CONTRACT; + INT64 timestamp; + + g_pfnGetSystemTimeAsFileTime((FILETIME*)×tamp); + + if (::FileTimeToSystemTime((FILETIME*)×tamp, &(time->systemTime))) + { + // to keep the time precision + time->hundredNanoSecond = timestamp % 10000; // 10000 is the number of 100-nano seconds per Millisecond + } + else + { + ::GetSystemTime(&(time->systemTime)); + time->hundredNanoSecond = 0; + } + + if (time->systemTime.wSecond > 59) + { + // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. + // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds + time->systemTime.wSecond = 59; + time->systemTime.wMilliseconds = 999; + time->hundredNanoSecond = 9999; + } +} +FCIMPLEND; + +FCIMPL2(FC_BOOL_RET, SystemNative::FileTimeToSystemTime, INT64 fileTime, FullSystemTime *time) +{ + FCALL_CONTRACT; + if (::FileTimeToSystemTime((FILETIME*)&fileTime, (LPSYSTEMTIME) time)) + { + // to keep the time precision + time->hundredNanoSecond = fileTime % 10000; // 10000 is the number of 100-nano seconds per Millisecond + if (time->systemTime.wSecond > 59) + { + // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. + // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds + time->systemTime.wSecond = 59; + time->systemTime.wMilliseconds = 999; + time->hundredNanoSecond = 9999; + } + FC_RETURN_BOOL(TRUE); + } + FC_RETURN_BOOL(FALSE); +} +FCIMPLEND; + +FCIMPL2(FC_BOOL_RET, SystemNative::ValidateSystemTime, SYSTEMTIME *time, CLR_BOOL localTime) +{ + FCALL_CONTRACT; + + if (localTime) + { + SYSTEMTIME st; + FC_RETURN_BOOL(::TzSpecificLocalTimeToSystemTime(NULL, time, &st)); + } + else + { + FILETIME timestamp; + FC_RETURN_BOOL(::SystemTimeToFileTime(time, ×tamp)); + } +} +FCIMPLEND; + +FCIMPL2(FC_BOOL_RET, SystemNative::SystemTimeToFileTime, SYSTEMTIME *time, INT64 *pFileTime) +{ + FCALL_CONTRACT; + + BOOL ret = ::SystemTimeToFileTime(time, (LPFILETIME) pFileTime); + FC_RETURN_BOOL(ret); +} +FCIMPLEND; +#endif // TARGET_UNIX + + +FCIMPL0(UINT32, SystemNative::GetTickCount) +{ + FCALL_CONTRACT; + + return ::GetTickCount(); +} +FCIMPLEND; + +FCIMPL0(UINT64, SystemNative::GetTickCount64) +{ + FCALL_CONTRACT; + + return ::GetTickCount64(); +} +FCIMPLEND; + + + + +VOID QCALLTYPE SystemNative::Exit(INT32 exitcode) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + // The exit code for the process is communicated in one of two ways. If the + // entrypoint returns an 'int' we take that. Otherwise we take a latched + // process exit code. This can be modified by the app via setting + // Environment's ExitCode property. + SetLatchedExitCode(exitcode); + + ForceEEShutdown(); + + END_QCALL; +} + +FCIMPL1(VOID,SystemNative::SetExitCode,INT32 exitcode) +{ + FCALL_CONTRACT; + + // The exit code for the process is communicated in one of two ways. If the + // entrypoint returns an 'int' we take that. Otherwise we take a latched + // process exit code. This can be modified by the app via setting + // Environment's ExitCode property. + SetLatchedExitCode(exitcode); +} +FCIMPLEND + +FCIMPL0(INT32, SystemNative::GetExitCode) +{ + FCALL_CONTRACT; + + // Return whatever has been latched so far. This is uninitialized to 0. + return GetLatchedExitCode(); +} +FCIMPLEND + +void QCALLTYPE SystemNative::_GetCommandLine(QCall::StringHandleOnStack retString) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + LPCWSTR commandLine; + + commandLine = WszGetCommandLine(); + if (commandLine==NULL) + COMPlusThrowOM(); + + retString.Set(commandLine); + + END_QCALL; +} + +FCIMPL0(Object*, SystemNative::GetCommandLineArgs) +{ + FCALL_CONTRACT; + + PTRARRAYREF strArray = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(strArray); + + LPWSTR commandLine; + + commandLine = WszGetCommandLine(); + if (commandLine==NULL) + COMPlusThrowOM(); + + DWORD numArgs = 0; + LPWSTR* argv = SegmentCommandLine(commandLine, &numArgs); + if (!argv) + COMPlusThrowOM(); + + _ASSERTE(numArgs > 0); + + strArray = (PTRARRAYREF) AllocateObjectArray(numArgs, g_pStringClass); + // Copy each argument into new Strings. + for(unsigned int i=0; i<numArgs; i++) + { + STRINGREF str = StringObject::NewString(argv[i]); + STRINGREF * destData = ((STRINGREF*)(strArray->GetDataPtr())) + i; + SetObjectReference((OBJECTREF*)destData, (OBJECTREF)str); + } + delete [] argv; + + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(strArray); +} +FCIMPLEND + +// Return a method info for the method were the exception was thrown +FCIMPL1(ReflectMethodObject*, SystemNative::GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE) +{ + FCALL_CONTRACT; + + I1ARRAYREF pArray(static_cast<I1Array *>(pStackTraceUNSAFE)); + StackTraceArray stackArray(pArray); + + if (!stackArray.Size()) + return NULL; + + // The managed stacktrace classes always returns typical method definition, so we don't need to bother providing exact instantiation. + // Generics::GetExactInstantiationsOfMethodAndItsClassFromCallInformation(pElements[0].pFunc, pElements[0].pExactGenericArgsToken, pTypeHandle, &pMD); + + MethodDesc* pFunc = stackArray[0].pFunc; + + // Strip the instantiation to make sure that the reflection never gets a bad method desc back. + REFLECTMETHODREF refRet = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_0() + pFunc = pFunc->LoadTypicalMethodDefinition(); + refRet = pFunc->GetStubMethodInfo(); + _ASSERTE(pFunc->IsRuntimeMethodHandle()); + + HELPER_METHOD_FRAME_END(); + + return (ReflectMethodObject*)OBJECTREFToObject(refRet); +} +FCIMPLEND + +INT32 QCALLTYPE SystemNative::GetProcessorCount() +{ + QCALL_CONTRACT; + + INT32 processorCount = 0; + + BEGIN_QCALL; + +#ifndef TARGET_UNIX + CPUGroupInfo::EnsureInitialized(); + + if(CPUGroupInfo::CanEnableThreadUseAllCpuGroups()) + { + processorCount = CPUGroupInfo::GetNumActiveProcessors(); + } +#endif // !TARGET_UNIX + // Processor count will be 0 if CPU groups are disabled/not supported + if(processorCount == 0) + { + SYSTEM_INFO systemInfo; + ZeroMemory(&systemInfo, sizeof(systemInfo)); + + GetSystemInfo(&systemInfo); + + processorCount = systemInfo.dwNumberOfProcessors; + } + +#ifdef TARGET_UNIX + uint32_t cpuLimit; + + if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < (uint32_t)processorCount) + processorCount = cpuLimit; +#endif + + END_QCALL; + + return processorCount; +} + +// FailFast is supported in BCL.small as internal to support failing fast in places where EEE used to be thrown. +// +// Static message buffer used by SystemNative::FailFast to avoid reliance on a +// managed string object buffer. This buffer is not always used, see comments in +// the method below. +WCHAR g_szFailFastBuffer[256]; +#define FAIL_FAST_STATIC_BUFFER_LENGTH (sizeof(g_szFailFastBuffer) / sizeof(WCHAR)) + +// This is the common code for FailFast processing that is wrapped by the two +// FailFast FCalls below. +void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode, STRINGREF refErrorSourceString) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + }CONTRACTL_END; + + struct + { + STRINGREF refMesgString; + EXCEPTIONREF refExceptionForWatsonBucketing; + STRINGREF refErrorSourceString; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + + gc.refMesgString = refMesgString; + gc.refExceptionForWatsonBucketing = refExceptionForWatsonBucketing; + gc.refErrorSourceString = refErrorSourceString; + + // Managed code injected FailFast maps onto the unmanaged version + // (EEPolicy::HandleFatalError) in the following manner: the exit code is + // always set to COR_E_FAILFAST and the address passed (usually a failing + // EIP) is in fact the address of a unicode message buffer (explaining the + // reason for the fault). + // The message string comes from a managed string object so we can't rely on + // the buffer remaining in place below our feet. But equally we don't want + // to inject failure points (by, for example, allocating a heap buffer or a + // pinning handle) when we have a much higher chance than usual of actually + // tripping those failure points and eradicating useful debugging info. + // We employ various strategies to deal with this: + // o If the message is small enough we copy it into a static buffer + // (g_szFailFastBuffer). + // o Otherwise we try to allocate a buffer of the required size on the + // heap. This buffer will be leaked. + // o If the allocation above fails we return to the static buffer and + // truncate the message. + // + // Another option would seem to be to implement a new frame type that + // protects object references as pinned, but that seems like overkill for + // just this problem. + WCHAR *pszMessage = NULL; + DWORD cchMessage = (gc.refMesgString == NULL) ? 0 : gc.refMesgString->GetStringLength(); + + WCHAR * errorSourceString = NULL; + + if (gc.refErrorSourceString != NULL) + { + DWORD cchErrorSource = gc.refErrorSourceString->GetStringLength(); + errorSourceString = new (nothrow) WCHAR[cchErrorSource + 1]; + + if (errorSourceString != NULL) + { + memcpyNoGCRefs(errorSourceString, gc.refErrorSourceString->GetBuffer(), cchErrorSource * sizeof(WCHAR)); + errorSourceString[cchErrorSource] = W('\0'); + } + } + + if (cchMessage < FAIL_FAST_STATIC_BUFFER_LENGTH) + { + pszMessage = g_szFailFastBuffer; + } + else + { + // We can fail here, but we can handle the fault. + CONTRACT_VIOLATION(FaultViolation); + pszMessage = new (nothrow) WCHAR[cchMessage + 1]; + if (pszMessage == NULL) + { + // Truncate the message to what will fit in the static buffer. + cchMessage = FAIL_FAST_STATIC_BUFFER_LENGTH - 1; + pszMessage = g_szFailFastBuffer; + } + } + + if (cchMessage > 0) + memcpyNoGCRefs(pszMessage, gc.refMesgString->GetBuffer(), cchMessage * sizeof(WCHAR)); + pszMessage[cchMessage] = W('\0'); + + if (cchMessage == 0) { + WszOutputDebugString(W("CLR: Managed code called FailFast without specifying a reason.\r\n")); + } + else { + WszOutputDebugString(W("CLR: Managed code called FailFast, saying \"")); + WszOutputDebugString(pszMessage); + WszOutputDebugString(W("\"\r\n")); + } + + LPCWSTR argExceptionString = NULL; + StackSString msg; + if (gc.refExceptionForWatsonBucketing != NULL) + { + GetExceptionMessage(gc.refExceptionForWatsonBucketing, msg); + argExceptionString = msg.GetUnicode(); + } + + Thread *pThread = GetThread(); + +#ifndef TARGET_UNIX + // If we have the exception object, then try to setup + // the watson bucket if it has any details. + // On CoreCLR, Watson may not be enabled. Thus, we should + // skip this, if required. + if (IsWatsonEnabled()) + { + if ((gc.refExceptionForWatsonBucketing == NULL) || !SetupWatsonBucketsForFailFast(gc.refExceptionForWatsonBucketing)) + { + PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker(); + _ASSERTE(pUEWatsonBucketTracker != NULL); + pUEWatsonBucketTracker->SaveIpForWatsonBucket(retAddress); + pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::FatalError, pThread, NULL); + if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL) + { + pUEWatsonBucketTracker->ClearWatsonBucketDetails(); + } + } + } +#endif // !TARGET_UNIX + + // stash the user-provided exception object. this will be used as + // the inner exception object to the FatalExecutionEngineException. + if (gc.refExceptionForWatsonBucketing != NULL) + pThread->SetLastThrownObject(gc.refExceptionForWatsonBucketing); + + EEPolicy::HandleFatalError(exitCode, retAddress, pszMessage, NULL, errorSourceString, argExceptionString); + + GCPROTECT_END(); +} + +// Note: Do not merge this FCALL method with any other FailFast overloads. +// Watson uses the managed FailFast method with one String for crash dump bucketization. +FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE) +{ + FCALL_CONTRACT; + + STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_1(refMessage); + + // The HelperMethodFrame knows how to get the return address. + UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); + + // Call the actual worker to perform failfast + GenericFailFast(refMessage, NULL, retaddr, COR_E_FAILFAST, NULL); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL2(VOID, SystemNative::FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode) +{ + FCALL_CONTRACT; + + STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_1(refMessage); + + // The HelperMethodFrame knows how to get the return address. + UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); + + // Call the actual worker to perform failfast + GenericFailFast(refMessage, NULL, retaddr, exitCode, NULL); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE) +{ + FCALL_CONTRACT; + + STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; + EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_2(refMessage, refException); + + // The HelperMethodFrame knows how to get the return address. + UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); + + // Call the actual worker to perform failfast + GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST, NULL); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE) +{ + FCALL_CONTRACT; + + STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; + EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE; + STRINGREF errorSource = (STRINGREF)errorSourceUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_3(refMessage, refException, errorSource); + + // The HelperMethodFrame knows how to get the return address. + UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); + + // Call the actual worker to perform failfast + GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST, errorSource); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL0(FC_BOOL_RET, SystemNative::IsServerGC) +{ + FCALL_CONTRACT; + + FC_RETURN_BOOL(GCHeapUtilities::IsServerHeap()); +} +FCIMPLEND + +#if defined(TARGET_X86) || defined(TARGET_AMD64) + +void QCALLTYPE SystemNative::X86BaseCpuId(int cpuInfo[4], int functionId, int subFunctionId) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + __cpuidex(cpuInfo, functionId, subFunctionId); + + END_QCALL; +} + +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) diff --git a/src/coreclr/classlibnative/bcltype/system.h b/src/coreclr/classlibnative/bcltype/system.h new file mode 100644 index 00000000000..5bbf73d4a10 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/system.h @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: System.h +// + +// +// Purpose: Native methods on System.System +// + +#ifndef _SYSTEM_H_ +#define _SYSTEM_H_ + +#include "fcall.h" +#include "qcall.h" + +struct FullSystemTime +{ + SYSTEMTIME systemTime; + INT64 hundredNanoSecond; +}; + +class SystemNative +{ + friend class DebugStackTrace; + +private: + struct CaptureStackTraceData + { + // Used for the integer-skip version + INT32 skip; + + INT32 cElementsAllocated; + INT32 cElements; + StackTraceElement* pElements; + void* pStopStack; // use to limit the crawl + + CaptureStackTraceData() : skip(0), cElementsAllocated(0), cElements(0), pElements(NULL), pStopStack((void*)-1) + { + LIMITED_METHOD_CONTRACT; + } + }; + +public: + // Functions on the System.Environment class +#ifndef TARGET_UNIX + static FCDECL1(VOID, GetSystemTimeWithLeapSecondsHandling, FullSystemTime *time); + static FCDECL2(FC_BOOL_RET, ValidateSystemTime, SYSTEMTIME *time, CLR_BOOL localTime); + static FCDECL2(FC_BOOL_RET, FileTimeToSystemTime, INT64 fileTime, FullSystemTime *time); + static FCDECL2(FC_BOOL_RET, SystemTimeToFileTime, SYSTEMTIME *time, INT64 *pFileTime); +#endif // TARGET_UNIX + static FCDECL0(INT64, __GetSystemTimeAsFileTime); + static FCDECL0(UINT32, GetTickCount); + static FCDECL0(UINT64, GetTickCount64); + + static + void QCALLTYPE Exit(INT32 exitcode); + + static FCDECL1(VOID,SetExitCode,INT32 exitcode); + static FCDECL0(INT32, GetExitCode); + + static + void QCALLTYPE _GetCommandLine(QCall::StringHandleOnStack retString); + + static FCDECL0(Object*, GetCommandLineArgs); + static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE); + static FCDECL2(VOID, FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode); + static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE); + static FCDECL3(VOID, FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE); + + // Returns the number of logical processors that can be used by managed code + static INT32 QCALLTYPE GetProcessorCount(); + + static FCDECL0(FC_BOOL_RET, IsServerGC); + + // Return a method info for the method were the exception was thrown + static FCDECL1(ReflectMethodObject*, GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE); + +#if defined(TARGET_X86) || defined(TARGET_AMD64) + static void QCALLTYPE X86BaseCpuId(int cpuInfo[4], int functionId, int subFunctionId); +#endif // defined(TARGET_X86) || defined(TARGET_AMD64) + +private: + // Common processing code for FailFast + static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode, STRINGREF errorSource); +}; + +/* static */ +void QCALLTYPE GetTypeLoadExceptionMessage(UINT32 resId, QCall::StringHandleOnStack retString); + +/* static */ +void QCALLTYPE GetFileLoadExceptionMessage(UINT32 hr, QCall::StringHandleOnStack retString); + +/* static */ +void QCALLTYPE FileLoadException_GetMessageForHR(UINT32 hresult, QCall::StringHandleOnStack retString); + +#endif // _SYSTEM_H_ + diff --git a/src/coreclr/classlibnative/bcltype/varargsnative.cpp b/src/coreclr/classlibnative/bcltype/varargsnative.cpp new file mode 100644 index 00000000000..55dd48bf280 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/varargsnative.cpp @@ -0,0 +1,638 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: VarArgsNative.cpp +// + +// +// This module contains the implementation of the native methods for the +// varargs class(es).. +// + + +#include "common.h" +#include "object.h" +#include "excep.h" +#include "frames.h" +#include "vars.hpp" +#include "varargsnative.h" + +// Some platforms have additional alignment requirements for arguments. This function adjusts the given arg +// pointer to achieve such an alignment for the next argument on those platforms (otherwise it is a no-op). +// NOTE: the debugger has its own implementation of this algorithm in Debug\DI\RsType.cpp, CordbType::RequiresAlign8() +// so if you change this implementation be sure to update the debugger's version as well. +static void AdjustArgPtrForAlignment(VARARGS *pData, size_t cbArg) +{ +#ifdef TARGET_ARM + // Only 64-bit primitives or value types with embedded 64-bit primitives are aligned on 64-bit boundaries. + if (cbArg < 8) + return; + + // For the value type case we have to dig deeper and ask the typeloader whether the type requires + // alignment. + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic. + CorElementType et = pData->SigPtr.PeekElemTypeClosed(pData->ArgCookie->pModule, &typeContext); + if (et == ELEMENT_TYPE_TYPEDBYREF) + { + return; + } + else + if (et == ELEMENT_TYPE_VALUETYPE) + { + SigPointer tempSig(pData->SigPtr); + TypeHandle valueType = tempSig.GetTypeHandleThrowing(pData->ArgCookie->pModule, &typeContext); + if (!valueType.AsMethodTable()->RequiresAlign8()) + return; + } + else + { + // One of the primitive 64-bit types + } + pData->ArgPtr = (BYTE*)ALIGN_UP(pData->ArgPtr, 8); +#endif // TARGET_ARM +} + +//////////////////////////////////////////////////////////////////////////////// +// Initialize the basic info for processing a varargs parameter list. +//////////////////////////////////////////////////////////////////////////////// +static void InitCommon(VARARGS *data, VASigCookie** cookie) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(data)); + PRECONDITION(CheckPointer(cookie)); + } CONTRACTL_END; + + // Save the cookie and a copy of the signature. + data->ArgCookie = *cookie; + data->SigPtr = data->ArgCookie->signature.CreateSigPointer(); + + // Skip the calling convention, get the # of args and skip the return type. + ULONG callConv; + IfFailThrow(data->SigPtr.GetCallingConvInfo(&callConv)); + + ULONG sigData; + IfFailThrow(data->SigPtr.GetData(&sigData)); + data->RemainingArgs = sigData; + + IfFailThrow(data->SigPtr.SkipExactlyOne()); + + // Get a pointer to the cookie arg. + data->ArgPtr = (BYTE *) cookie; + +#if defined(TARGET_X86) + // STACK_GROWS_DOWN_ON_ARGS_WALK + + // <return address> ;; lower memory + // <varargs_cookie> '\' + // <argN> '\' + // += sizeOfArgs + // / + // <arg1> / + // * <this> ;; if an instance method (note: <this> is usally passed in + // ;; a register and wouldn't appear on the stack frame) + // ;; higher memory + // + // When the stack grows down, ArgPtr always points to the end of the next + // argument passed. So we initialize it to the address that is the + // end of the first fixed arg (arg1) (marked with a '*'). + + data->ArgPtr += data->ArgCookie->sizeOfArgs; +#else + // STACK_GROWS_UP_ON_ARGS_WALK + + // <this> ;; lower memory + // <varargs_cookie> ;; if an instance method + // * <arg1> + // + // <argN> ;; higher memory + // + // When the stack grows up, ArgPtr always points to the start of the next + // argument passed. So we initialize it to the address marked with a '*', + // which is the start of the first fixed arg (arg1). + + // Always skip over the varargs_cookie. + data->ArgPtr += StackElemSize(sizeof(LPVOID)); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// After initialization advance the next argument pointer to the first optional +// argument. +//////////////////////////////////////////////////////////////////////////////// +void AdvanceArgPtr(VARARGS *data) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(data)); + } CONTRACTL_END; + + // Advance to the first optional arg. + while (data->RemainingArgs > 0) + { + if (data->SigPtr.AtSentinel()) + break; + + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic + SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule, &typeContext); + SIZE_T cbArg = StackElemSize(cbRaw); + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(cbRaw)) + cbArg = sizeof(void*); +#endif + + // Adjust the frame pointer and the signature info. + AdjustArgPtrForAlignment(data, cbArg); +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr -= cbArg; +#else // STACK_GROWS_UP_ON_ARGS_WALK + data->ArgPtr += cbArg; +#endif // STACK_GROWS_**_ON_ARGS_WALK + + IfFailThrow(data->SigPtr.SkipExactlyOne()); + --data->RemainingArgs; + } +} // AdvanceArgPtr + +//////////////////////////////////////////////////////////////////////////////// +// ArgIterator constructor that initializes the state to support iteration +// of the args starting at the first optional argument. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL2(void, VarArgsNative::Init, VARARGS* _this, LPVOID cookie) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + + _ASSERTE(_this != NULL); + VARARGS* data = _this; + if (cookie == 0) + COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized")); + + VASigCookie* pCookie = *(VASigCookie**)(cookie); + + if (pCookie->signature.IsEmpty()) + { + data->SigPtr = SigPointer(NULL, 0); + data->ArgCookie = NULL; + data->ArgPtr = (BYTE*)((VASigCookieEx*)pCookie)->m_pArgs; + } + else + { + // Use common code to pick the cookie apart and advance to the ... + InitCommon(data, (VASigCookie**)cookie); + AdvanceArgPtr(data); + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +//////////////////////////////////////////////////////////////////////////////// +// ArgIterator constructor that initializes the state to support iteration +// of the args starting at the argument following the supplied argument pointer. +// Specifying NULL as the firstArg parameter causes it to start at the first +// argument to the call. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL3( +void, +VarArgsNative::Init2, + VARARGS * _this, + LPVOID cookie, + LPVOID firstArg) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + + _ASSERTE(_this != NULL); + VARARGS* data = _this; + if (cookie == 0) + COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized")); + + // Init most of the structure. + InitCommon(data, (VASigCookie**)cookie); + + // If it is NULL, start at the first arg. + if (firstArg != NULL) + { + // + // The expectation made by VarArgsNative is that: +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + // data->ArgPtr points to the end of the next argument. + // <varargs_cookie> + // <argN> <-- data->ArgPtr after InitCommon + // + // <argM 1st optional arg> + // *@ <arg2> <-- firstArg, data->ArgPtr leaving Init2() + // <arg1> + // <this> ;; if an instance method + // ;; higher memory + // +#else // STACK_GROWS_UP_ON_ARGS_WALK + // data->ArgPtr points to the beginning of the next argument + // <this> ;; if an instance method + // <varargs_cookie> + // <arg1> <-- data->ArgPtr after InitCommon + // * <arg2> <-- firstArg + // @ <argM - 1st optional arg> <-- data->ArgPtr leaving Init2() + // + // <argN> + // ;; higher memory +#endif // STACK_GROWS_**_ON_ARGS_WALK + // where * indicates the place on the stack that firstArg points upon + // entry to Init2. We need to correct in the STACK_GROWS_UP... case since + // we actually want to point at the argument after firstArg. This confusion + // comes from the difference in expectation of whether ArgPtr is pointing + // at the beginning or end of the argument on the stack. + // + // @ indicates where we want data->ArgPtr to point to after we're done with Init2 + // + + // Advance to the specified arg. + while (data->RemainingArgs > 0) + { + if (data->SigPtr.AtSentinel()) + { + COMPlusThrow(kArgumentException); + } + + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic + SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule,&typeContext); + SIZE_T cbArg = StackElemSize(cbRaw); + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(cbRaw)) + cbArg = sizeof(void*); +#endif + + // Adjust the frame pointer and the signature info. + AdjustArgPtrForAlignment(data, cbArg); + IfFailThrow(data->SigPtr.SkipExactlyOne()); + data->RemainingArgs--; + +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr -= cbArg; + bool atFirstArg = (data->ArgPtr == firstArg); +#else // STACK_GROWS_UP_ON_ARGS_WALK + bool atFirstArg = (data->ArgPtr == firstArg); + data->ArgPtr += cbArg; +#endif // STACK_GROWS_**_ON_ARGS_WALK + + if (atFirstArg) + break; + } + } + HELPER_METHOD_FRAME_END(); +} // VarArgsNative::Init2 +FCIMPLEND + + +//////////////////////////////////////////////////////////////////////////////// +// Return the number of unprocessed args in the argument iterator. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL1(int, VarArgsNative::GetRemainingCount, VARARGS* _this) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + _ASSERTE(_this != NULL); + if (!(_this->ArgCookie)) + { + // this argiterator was created by marshaling from an unmanaged va_list - + // can't do this operation + COMPlusThrow(kNotSupportedException); + } + HELPER_METHOD_FRAME_END(); + return (_this->RemainingArgs); +} +FCIMPLEND + + +//////////////////////////////////////////////////////////////////////////////// +// Retrieve the type of the next argument without consuming it. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL1(void*, VarArgsNative::GetNextArgType, VARARGS* _this) +{ + FCALL_CONTRACT; + + TypedByRef value; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + PREFIX_ASSUME(_this != NULL); + VARARGS data = *_this; + + if (!(_this->ArgCookie)) + { + // this argiterator was created by marshaling from an unmanaged va_list - + // can't do this operation + COMPlusThrow(kNotSupportedException); + } + + + // Make sure there are remaining args. + if (data.RemainingArgs == 0) + COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EnumEnded")); + + GetNextArgHelper(&data, &value, FALSE); + HELPER_METHOD_FRAME_END(); + return value.type.AsPtr(); +} +FCIMPLEND + +//////////////////////////////////////////////////////////////////////////////// +// Retrieve the next argument and return it in a TypedByRef and advance the +// next argument pointer. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL2(void, VarArgsNative::DoGetNextArg, VARARGS* _this, void * value) +{ + FCALL_CONTRACT; + + TypedByRef * result = (TypedByRef *)value; + HELPER_METHOD_FRAME_BEGIN_0(); + GCPROTECT_BEGININTERIOR (result); + + _ASSERTE(_this != NULL); + if (!(_this->ArgCookie)) + { + // this argiterator was created by marshaling from an unmanaged va_list - + // can't do this operation + COMPlusThrow(kInvalidOperationException); + } + + // Make sure there are remaining args. + if (_this->RemainingArgs == 0) + COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EnumEnded")); + + GetNextArgHelper(_this, result, TRUE); + GCPROTECT_END (); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + + +//////////////////////////////////////////////////////////////////////////////// +// Retrieve the next argument and return it in a TypedByRef and advance the +// next argument pointer. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL3(void, VarArgsNative::GetNextArg2, VARARGS* _this, void * value, ReflectClassBaseObject *pTypeUNSAFE) +{ + FCALL_CONTRACT; + + TypedByRef * result = (TypedByRef *)value; + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); + + if (refType == NULL) + FCThrowResVoid(kArgumentNullException, W("Arg_InvalidHandle")); + + HELPER_METHOD_FRAME_BEGIN_1(refType); + GCPROTECT_BEGININTERIOR (result); + + // IJW + + TypeHandle typehandle = refType->GetType(); + + _ASSERTE(_this != NULL); + UINT size = 0; + + CorElementType typ = typehandle.GetInternalCorElementType(); + if (CorTypeInfo::IsPrimitiveType(typ)) + { + size = CorTypeInfo::Size(typ); + } + else if (typ == ELEMENT_TYPE_PTR) + { + size = sizeof(LPVOID); + } + else if (typ == ELEMENT_TYPE_VALUETYPE) + { + size = typehandle.AsMethodTable()->GetNativeSize(); + } + else + { + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + size = StackElemSize(size); + + AdjustArgPtrForAlignment(_this, size); + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(size)) + { + result->data = *(void**)_this->ArgPtr; + size = sizeof(void*); + } + else +#endif + { + result->data = (void*)_this->ArgPtr; + } + + result->type = typehandle; + _this->ArgPtr += size; + + GCPROTECT_END (); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + + +//////////////////////////////////////////////////////////////////////////////// +// This is a helper that uses a VARARGS tracking data structure to retrieve +// the next argument out of a varargs function call. This does not check if +// there are any args remaining (it assumes it has been checked). +//////////////////////////////////////////////////////////////////////////////// +void +VarArgsNative::GetNextArgHelper( + VARARGS * data, + TypedByRef * value, + BOOL fData) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(data)); + PRECONDITION(CheckPointer(value)); + } CONTRACTL_END; + + GCPROTECT_BEGININTERIOR (value); + CorElementType elemType; + + _ASSERTE(data->RemainingArgs != 0); + + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic + SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule,&typeContext); + SIZE_T cbArg = StackElemSize(cbRaw); + + AdjustArgPtrForAlignment(data, cbArg); + + // Get a pointer to the beginning of the argument. +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr -= cbArg; +#endif + + // Assume the ref pointer points directly at the arg on the stack. + void* origArgPtr = data->ArgPtr; + value->data = origArgPtr; + +#ifndef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr += cbArg; +#endif // STACK_GROWS_**_ON_ARGS_WALK + + +TryAgain: + switch (elemType = data->SigPtr.PeekElemTypeClosed(data->ArgCookie->pModule, &typeContext)) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: +#if BIGENDIAN + if ( origArgPtr == value->data ) { + value->data = (BYTE*)origArgPtr + (sizeof(void*)-1); + } +#endif + value->type = CoreLibBinder::GetElementType(elemType); + break; + + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: +#if BIGENDIAN + if ( origArgPtr == value->data ) { + value->data = (BYTE*)origArgPtr + (sizeof(void*)-2); + } +#endif + value->type = CoreLibBinder::GetElementType(elemType); + break; + + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: + value->type = CoreLibBinder::GetElementType(elemType); + break; + + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R8: + value->type = CoreLibBinder::GetElementType(elemType); +#if !defined(HOST_64BIT) && (DATA_ALIGNMENT > 4) + if ( fData && origArgPtr == value->data ) { + // allocate an aligned copy of the value + value->data = value->type.AsMethodTable()->Box(origArgPtr, FALSE)->UnBox(); + } +#endif + break; + + case ELEMENT_TYPE_PTR: + value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule, &typeContext); + break; + + case ELEMENT_TYPE_BYREF: + // Check if we have already processed a by-ref. + if (value->data != origArgPtr) + { + _ASSERTE(!"Can't have a ByRef of a ByRef"); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + // Dereference the argument to remove the indirection of the ByRef. + value->data = *((void **) value->data); + + // Consume and discard the element type. + IfFailThrow(data->SigPtr.GetElemType(NULL)); + goto TryAgain; + + case ELEMENT_TYPE_VALUETYPE: + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (origArgPtr == value->data && ArgIterator::IsVarArgPassedByRef(cbRaw)) + { + // Adjust the arg pointer so only one word has been skipped + data->ArgPtr = (BYTE *)origArgPtr + sizeof(void*); + // Dereference the argument because the valuetype is passed by reference + value->data = *((void **) origArgPtr); + } +#endif + +#if BIGENDIAN + // Adjust the pointer for small valuetypes + if (origArgPtr == value->data) { + value->data = StackElemEndianessFixup(origArgPtr, cbRaw); + } +#endif + + FALLTHROUGH; + + case ELEMENT_TYPE_CLASS: { + value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule, &typeContext); + + if (value->type.AsMethodTable()->IsByRefLike()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + if (elemType == ELEMENT_TYPE_CLASS && value->type.AsMethodTable()->IsValueType()) + value->type = g_pObjectClass; + } break; + + case ELEMENT_TYPE_TYPEDBYREF: + if (value->data != origArgPtr) + { + //<TODO>@todo: Is this really an error?</TODO> + _ASSERTE(!"Can't have a ByRef of a TypedByRef"); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(sizeof(TypedByRef))) + { + // Adjust the arg pointer so only one word has been skipped + data->ArgPtr = (BYTE *)origArgPtr + sizeof(void *); + // Dereference the argument because the valuetypes are always passed by reference + value->data = *((void **)origArgPtr); + } +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE + // Load the TypedByRef + value->type = ((TypedByRef*)value->data)->type; + value->data = ((TypedByRef*)value->data)->data; + break; + + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_ARRAY: + { + value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule,&typeContext); + + break; + } + + case ELEMENT_TYPE_FNPTR: + case ELEMENT_TYPE_OBJECT: + _ASSERTE(!"Not implemented"); + COMPlusThrow(kNotSupportedException); + break; + + case ELEMENT_TYPE_SENTINEL: + default: + _ASSERTE(!"Should be unreachable"); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + break; + } + + // Update the tracking stuff to move past the argument. + --data->RemainingArgs; + IfFailThrow(data->SigPtr.SkipExactlyOne()); + GCPROTECT_END (); +} // VarArgsNative::GetNextArgHelper diff --git a/src/coreclr/classlibnative/bcltype/varargsnative.h b/src/coreclr/classlibnative/bcltype/varargsnative.h new file mode 100644 index 00000000000..b3692015035 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/varargsnative.h @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: VarArgsNative.h +// + +// +// This module contains the implementation of the native methods for the +// varargs class(es).. +// + +#ifndef _VARARGSNATIVE_H_ +#define _VARARGSNATIVE_H_ + +#include "clrvarargs.h" + +class VarArgsNative +{ +public: + static FCDECL3(void, Init2, VARARGS* _this, LPVOID cookie, LPVOID firstArg); + static FCDECL2(void, Init, VARARGS* _this, LPVOID cookie); + static FCDECL1(int, GetRemainingCount, VARARGS* _this); + static FCDECL1(void*, GetNextArgType, VARARGS* _this); + //TypedByRef can not be passed by ref, so has to pass it as void pointer + static FCDECL2(void, DoGetNextArg, VARARGS* _this, void * value); + //TypedByRef can not be passed by ref, so has to pass it as void pointer + static FCDECL3(void, GetNextArg2, VARARGS* _this, void * value, ReflectClassBaseObject *pTypeUNSAFE); + + static void GetNextArgHelper(VARARGS *data, TypedByRef *value, BOOL fData); +}; + +#endif // _VARARGSNATIVE_H_ diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp new file mode 100644 index 00000000000..a269644d797 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -0,0 +1,289 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: Variant.cpp +// + +// +// Purpose: Native Implementation of the Variant Class +// + +// + +#include "common.h" + +#ifdef FEATURE_COMINTEROP + +#include "object.h" +#include "excep.h" +#include "frames.h" +#include "vars.hpp" +#include "variant.h" +#include "string.h" +#include "field.h" + +// The following values are used to represent underlying +// type of the Enum.. +#define EnumI1 0x100000 +#define EnumU1 0x200000 +#define EnumI2 0x300000 +#define EnumU2 0x400000 +#define EnumI4 0x500000 +#define EnumU4 0x600000 +#define EnumI8 0x700000 +#define EnumU8 0x800000 +#define EnumMask 0xF00000 + + +/*===============================SetFieldsObject================================ +** +==============================================================================*/ +FCIMPL2(void, COMVariant::SetFieldsObject, VariantData* var, Object* vVal) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(var)); + PRECONDITION(CheckPointer(vVal)); + } + CONTRACTL_END; + + OBJECTREF val = ObjectToOBJECTREF(vVal); + + HELPER_METHOD_FRAME_BEGIN_1(val); + GCPROTECT_BEGININTERIOR(var) + + CVTypes cvt = CV_EMPTY; + TypeHandle typeHandle; + + MethodTable *valMT = val->GetMethodTable(); + + //If this isn't a value class, we should just skip out because we're not going + //to do anything special with it. + if (!valMT->IsValueType()) + { + var->SetObjRef(val); + typeHandle = TypeHandle(valMT); + + if (typeHandle==GetTypeHandleForCVType(CV_MISSING)) + { + var->SetType(CV_MISSING); + } + else if (typeHandle==GetTypeHandleForCVType(CV_NULL)) + { + var->SetType(CV_NULL); + } + else if (typeHandle==GetTypeHandleForCVType(CV_EMPTY)) + { + var->SetType(CV_EMPTY); + var->SetObjRef(NULL); + } + else + { + var->SetType(CV_OBJECT); + } + } + else if (IsTypeRefOrDef(g_ColorClassName, valMT->GetModule(), valMT->GetCl())) + { + // System.Drawing.Color is converted to UInt32 + var->SetDataAsUInt32(ConvertSystemColorToOleColor(&val)); + var->SetType(CV_U4); + } + else + { + //If this is a primitive type, we need to unbox it, get the value and create a variant + //with just those values. + void *UnboxData = val->UnBox(); + + ClearObjectReference(var->GetObjRefPtr()); + typeHandle = TypeHandle(valMT); + CorElementType cet = typeHandle.GetSignatureCorElementType(); + + if (cet>=ELEMENT_TYPE_BOOLEAN && cet<=ELEMENT_TYPE_STRING) + { + cvt = (CVTypes)cet; + } + else + { + cvt = GetCVTypeFromClass(valMT); + } + var->SetType(cvt); + + + //copy all of the data. + // Copies must be done based on the exact number of bytes to copy. + // We don't want to read garbage from other blocks of memory. + //CV_I8 --> CV_R8, CV_DATETIME, CV_TIMESPAN, & CV_CURRENCY are all of the 8 byte quantities + //If we don't find one of those ranges, we've found a value class + //of which we don't have inherent knowledge, so just slam that into an + //ObjectRef. + if (cvt>=CV_BOOLEAN && cvt<=CV_U1 && cvt != CV_CHAR) + { + var->SetDataAsInt64(*((UINT8 *)UnboxData)); + } + else if (cvt==CV_CHAR || cvt>=CV_I2 && cvt<=CV_U2) + { + var->SetDataAsInt64(*((UINT16 *)UnboxData)); + } + else if (cvt>=CV_I4 && cvt<=CV_U4 || cvt==CV_R4) + { + var->SetDataAsInt64(*((UINT32 *)UnboxData)); + } + else if ((cvt>=CV_I8 && cvt<=CV_R8) || (cvt==CV_DATETIME) || (cvt==CV_TIMESPAN) || (cvt==CV_CURRENCY)) + { + var->SetDataAsInt64(*((INT64 *)UnboxData)); + } + else if (cvt==CV_EMPTY || cvt==CV_NULL || cvt==CV_MISSING) + { + var->SetType(cvt); + } + else if (cvt==CV_ENUM) + { + var->SetDataAsInt64(*((INT32 *)UnboxData)); + var->SetObjRef(typeHandle.GetManagedClassObject()); + var->SetType(GetEnumFlags(typeHandle)); + } + else + { + // Decimals and other boxed value classes get handled here. + var->SetObjRef(val); + } + } + + GCPROTECT_END(); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + +FCIMPL1(Object*, COMVariant::BoxEnum, VariantData* var) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(var)); + PRECONDITION(var->GetObjRef() != NULL); + } + CONTRACTL_END; + + OBJECTREF retO = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(retO); + +#ifdef _DEBUG + CVTypes vType = (CVTypes) var->GetType(); +#endif + + _ASSERTE(vType == CV_ENUM); + + MethodTable* mt = ((REFLECTCLASSBASEREF) var->GetObjRef())->GetType().GetMethodTable(); + _ASSERTE(mt); + + retO = mt->Box(var->GetData()); + + HELPER_METHOD_FRAME_END(); + return OBJECTREFToObject(retO); +} +FCIMPLEND + + +/*===============================GetTypeFromClass=============================== +**Action: Takes an MethodTable * and returns the associated CVType. +**Arguments: MethodTable * -- a pointer to the class for which we want the CVType. +**Returns: The CVType associated with the MethodTable or CV_OBJECT if this can't be +** determined. +**Exceptions: None +==============================================================================*/ + +CVTypes COMVariant::GetCVTypeFromClass(TypeHandle th) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + if (th.IsNull()) + return CV_EMPTY; + + //We'll start looking from Variant. Empty and Void are handled below. + for (int i=CV_EMPTY; i<CV_LAST; i++) + { + if (th == GetTypeHandleForCVType((CVTypes)i)) + return (CVTypes)i; + } + + if (th.IsEnum()) + return CV_ENUM; + + return CV_OBJECT; +} + + +int COMVariant::GetEnumFlags(TypeHandle th) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(!th.IsNull()); + PRECONDITION(th.IsEnum()); + } + CONTRACTL_END; + + // <TODO> check this approximation - we may be losing exact type information </TODO> + ApproxFieldDescIterator fdIterator(th.GetMethodTable(), ApproxFieldDescIterator::INSTANCE_FIELDS); + FieldDesc* p = fdIterator.Next(); + if (NULL == p) + { + _ASSERTE(!"NULL FieldDesc returned"); + return 0; + } + +#ifdef _DEBUG + WORD fldCnt = th.GetMethodTable()->GetNumInstanceFields(); +#endif + + _ASSERTE(fldCnt == 1); + + CorElementType cet = p[0].GetFieldType(); + switch (cet) + { + case ELEMENT_TYPE_I1: + return (CV_ENUM | EnumI1); + + case ELEMENT_TYPE_U1: + return (CV_ENUM | EnumU1); + + case ELEMENT_TYPE_I2: + return (CV_ENUM | EnumI2); + + case ELEMENT_TYPE_U2: + return (CV_ENUM | EnumU2); + + IN_TARGET_32BIT(case ELEMENT_TYPE_I:) + case ELEMENT_TYPE_I4: + return (CV_ENUM | EnumI4); + + IN_TARGET_32BIT(case ELEMENT_TYPE_U:) + case ELEMENT_TYPE_U4: + return (CV_ENUM | EnumU4); + + IN_TARGET_64BIT(case ELEMENT_TYPE_I:) + case ELEMENT_TYPE_I8: + return (CV_ENUM | EnumI8); + + IN_TARGET_64BIT(case ELEMENT_TYPE_U:) + case ELEMENT_TYPE_U8: + return (CV_ENUM | EnumU8); + + default: + _ASSERTE(!"UNknown Type"); + return 0; + } +} + +#endif // FEATURE_COMINTEROP diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h new file mode 100644 index 00000000000..aae0a5f9741 --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/variant.h @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: Variant.h +// + +// +// Purpose: Headers for the Variant class. +// + +// + +#ifndef _VARIANT_H_ +#define _VARIANT_H_ + +#ifndef FEATURE_COMINTEROP +#error FEATURE_COMINTEROP is required for this file +#endif // FEATURE_COMINTEROP + +#include <cor.h> +#include "fcall.h" +#include "olevariant.h" + +class COMVariant +{ + friend class OleVariant; + +public: + // + // Helper Routines + // + + static FCDECL2(void, SetFieldsObject, VariantData* vThisRef, Object* vVal); + static FCDECL1(Object*, BoxEnum, VariantData* var); + +private: + // GetCVTypeFromClass + // This method will return the CVTypes from the Variant instance + static CVTypes GetCVTypeFromClass(TypeHandle th); + static int GetEnumFlags(TypeHandle th); +}; + +#endif // _VARIANT_H_ + diff --git a/src/coreclr/classlibnative/float/CMakeLists.txt b/src/coreclr/classlibnative/float/CMakeLists.txt new file mode 100644 index 00000000000..b2c47ea39b6 --- /dev/null +++ b/src/coreclr/classlibnative/float/CMakeLists.txt @@ -0,0 +1,18 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +include_directories("../inc") + +set(FLOAT_SOURCES + floatdouble.cpp + floatsingle.cpp +) + +add_library_clr(comfloat_wks_obj + OBJECT + ${FLOAT_SOURCES} +) + +add_dependencies(comfloat_wks_obj eventing_headers) + +add_library(comfloat_wks INTERFACE) +target_sources(comfloat_wks INTERFACE $<TARGET_OBJECTS:comfloat_wks_obj>)
\ No newline at end of file diff --git a/src/coreclr/classlibnative/float/floatdouble.cpp b/src/coreclr/classlibnative/float/floatdouble.cpp new file mode 100644 index 00000000000..a2a00b0628b --- /dev/null +++ b/src/coreclr/classlibnative/float/floatdouble.cpp @@ -0,0 +1,331 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: FloatDouble.cpp +// + +#include <common.h> + +#include "floatdouble.h" + +// The default compilation mode is /fp:precise, which disables floating-point intrinsics. This +// default compilation mode has previously caused performance regressions in floating-point code. +// We enable /fp:fast semantics for the majority of the math functions, as it will speed up performance +// and is really unlikely to cause any other code regressions. + +// Sin, Cos, and Tan on AMD64 Windows were previously implemented in vm\amd64\JitHelpers_Fast.asm +// by calling x87 floating point code (fsin, fcos, fptan) because the CRT helpers were too slow. This +// is no longer the case and the CRT call is used on all platforms. + +// Log, Log10 and Exp were previously slower with /fp:fast on SSE2 enabled hardware (see #500373). +// This is no longer the case and they now consume use the /fp:fast versions. + +// Exp(+/-INFINITY) did not previously return the expected results of +0.0 (for -INFINITY) +// and +INFINITY (for +INFINITY) so these cases were handled specially. As this is no longer +// the case and the expected results are now returned, the special handling has been removed. + +// Previously there was more special handling for the x86 Windows version of Pow. +// This additional handling was unnecessary and has since been removed. + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +/// +/// beginning of /fp:fast scope +/// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +#pragma float_control(push) +#pragma float_control(precise, off) +#endif + +/*=====================================Abs====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Abs, double x) + FCALL_CONTRACT; + + return (double)fabs(x); +FCIMPLEND + +/*=====================================Acos===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Acos, double x) + FCALL_CONTRACT; + + return (double)acos(x); +FCIMPLEND + +/*=====================================Acosh==================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Acosh, double x) + FCALL_CONTRACT; + + return (double)acosh(x); +FCIMPLEND + +/*=====================================Asin===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Asin, double x) + FCALL_CONTRACT; + + return (double)asin(x); +FCIMPLEND + +/*=====================================Asinh==================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Asinh, double x) + FCALL_CONTRACT; + + return (double)asinh(x); +FCIMPLEND + +/*=====================================Atan===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Atan, double x) + FCALL_CONTRACT; + + return (double)atan(x); +FCIMPLEND + +/*=====================================Atanh==================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Atanh, double x) + FCALL_CONTRACT; + + return (double)atanh(x); +FCIMPLEND + +/*=====================================Atan2==================================== +** +==============================================================================*/ +FCIMPL2_VV(double, COMDouble::Atan2, double y, double x) + FCALL_CONTRACT; + + return (double)atan2(y, x); +FCIMPLEND + +/*====================================Cbrt====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Cbrt, double x) + FCALL_CONTRACT; + + return (double)cbrt(x); +FCIMPLEND + +#if defined(_MSC_VER) && defined(TARGET_AMD64) +// The /fp:fast form of `ceil` for AMD64 does not correctly handle: `-1.0 < value <= -0.0` +// https://github.com/dotnet/runtime/issues/11003 +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +/*====================================Ceil====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Ceil, double x) + FCALL_CONTRACT; + + return (double)ceil(x); +FCIMPLEND + +#if defined(_MSC_VER) && defined(TARGET_AMD64) +#pragma float_control(pop) +#endif + +/*=====================================Cos====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Cos, double x) + FCALL_CONTRACT; + + return (double)cos(x); +FCIMPLEND + +/*=====================================Cosh===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Cosh, double x) + FCALL_CONTRACT; + + return (double)cosh(x); +FCIMPLEND + +/*=====================================Exp====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Exp, double x) + FCALL_CONTRACT; + + return (double)exp(x); +FCIMPLEND + +#if defined(_MSC_VER) && defined(TARGET_X86) +// The /fp:fast form of `floor` for x86 does not correctly handle: `-0.0` +// https://github.com/dotnet/runtime/issues/11003 +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +/*====================================Floor===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Floor, double x) + FCALL_CONTRACT; + + return (double)floor(x); +FCIMPLEND + +#if defined(_MSC_VER) && defined(TARGET_X86) +#pragma float_control(pop) +#endif + +/*=====================================FMod===================================== +** +==============================================================================*/ +FCIMPL2_VV(double, COMDouble::FMod, double x, double y) + FCALL_CONTRACT; + + return (double)fmod(x, y); +FCIMPLEND + +/*=====================================FusedMultiplyAdd========================== +** +==============================================================================*/ +FCIMPL3_VVV(double, COMDouble::FusedMultiplyAdd, double x, double y, double z) + FCALL_CONTRACT; + + return (double)fma(x, y, z); +FCIMPLEND + +/*=====================================Ilog2==================================== +** +==============================================================================*/ +FCIMPL1_V(int, COMDouble::ILogB, double x) + FCALL_CONTRACT; + + return (int)ilogb(x); +FCIMPLEND + +/*=====================================Log====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Log, double x) + FCALL_CONTRACT; + + return (double)log(x); +FCIMPLEND + +/*=====================================Log2===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Log2, double x) + FCALL_CONTRACT; + + return (double)log2(x); +FCIMPLEND + +/*====================================Log10===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Log10, double x) + FCALL_CONTRACT; + + return (double)log10(x); +FCIMPLEND + +/*=====================================ModF===================================== +** +==============================================================================*/ +FCIMPL2_VI(double, COMDouble::ModF, double x, double* intptr) + FCALL_CONTRACT; + + return (double)modf(x, intptr); +FCIMPLEND + +/*=====================================Pow====================================== +** +==============================================================================*/ +FCIMPL2_VV(double, COMDouble::Pow, double x, double y) + FCALL_CONTRACT; + + return (double)pow(x, y); +FCIMPLEND + +/*=====================================ScaleB=================================== +** +==============================================================================*/ +FCIMPL2_VI(double, COMDouble::ScaleB, double x, int n) + FCALL_CONTRACT; + + return (double)scalbn(x, n); +FCIMPLEND + +/*=====================================Sin====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Sin, double x) + FCALL_CONTRACT; + + return (double)sin(x); +FCIMPLEND + +/*=====================================Sinh===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Sinh, double x) + FCALL_CONTRACT; + + return (double)sinh(x); +FCIMPLEND + +/*=====================================Sqrt===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Sqrt, double x) + FCALL_CONTRACT; + + return (double)sqrt(x); +FCIMPLEND + +/*=====================================Tan====================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Tan, double x) + FCALL_CONTRACT; + + return (double)tan(x); +FCIMPLEND + +/*=====================================Tanh===================================== +** +==============================================================================*/ +FCIMPL1_V(double, COMDouble::Tanh, double x) + FCALL_CONTRACT; + + return (double)tanh(x); +FCIMPLEND + +#ifdef _MSC_VER +#pragma float_control(pop) +#endif + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +/// +/// End of /fp:fast scope +/// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/classlibnative/float/floatsingle.cpp b/src/coreclr/classlibnative/float/floatsingle.cpp new file mode 100644 index 00000000000..9972e17c690 --- /dev/null +++ b/src/coreclr/classlibnative/float/floatsingle.cpp @@ -0,0 +1,318 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: FloatSingle.cpp +// + +#include <common.h> + +#include "floatsingle.h" + +// Windows x86 and Windows ARM/ARM64 may not define _isnanf() or _copysignf() but they do +// define _isnan() and _copysign(). We will redirect the macros to these other functions if +// the macro is not defined for the platform. This has the side effect of a possible implicit +// upcasting for arguments passed in and an explicit downcasting for the _copysign() call. +#if (defined(TARGET_X86) || defined(TARGET_ARM) || defined(TARGET_ARM64)) && !defined(TARGET_UNIX) + +#if !defined(_copysignf) +#define _copysignf (float)_copysign +#endif + +#endif + +// The default compilation mode is /fp:precise, which disables floating-point intrinsics. This +// default compilation mode has previously caused performance regressions in floating-point code. +// We enable /fp:fast semantics for the majority of the math functions, as it will speed up performance +// and is really unlikely to cause any other code regressions. + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +/// +/// beginning of /fp:fast scope +/// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +#pragma float_control(push) +#pragma float_control(precise, off) +#endif + +/*=====================================Abs===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Abs, float x) + FCALL_CONTRACT; + + return (float)fabsf(x); +FCIMPLEND + +/*=====================================Acos===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Acos, float x) + FCALL_CONTRACT; + + return (float)acosf(x); +FCIMPLEND + +/*=====================================Acosh==================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Acosh, float x) + FCALL_CONTRACT; + + return (float)acoshf(x); +FCIMPLEND + +/*=====================================Asin===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Asin, float x) + FCALL_CONTRACT; + + return (float)asinf(x); +FCIMPLEND + +/*=====================================Asinh==================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Asinh, float x) + FCALL_CONTRACT; + + return (float)asinhf(x); +FCIMPLEND + +/*=====================================Atan===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Atan, float x) + FCALL_CONTRACT; + + return (float)atanf(x); +FCIMPLEND + +/*=====================================Atanh==================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Atanh, float x) + FCALL_CONTRACT; + + return (float)atanhf(x); +FCIMPLEND + +/*=====================================Atan2==================================== +** +==============================================================================*/ +FCIMPL2_VV(float, COMSingle::Atan2, float y, float x) + FCALL_CONTRACT; + + return (float)atan2f(y, x); +FCIMPLEND + +/*====================================Cbrt====================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Cbrt, float x) + FCALL_CONTRACT; + + return (float)cbrtf(x); +FCIMPLEND + +#if defined(_MSC_VER) && defined(TARGET_AMD64) +// The /fp:fast form of `ceilf` for AMD64 does not correctly handle: `-1.0 < value <= -0.0` +// https://github.com/dotnet/runtime/issues/11003 +#pragma float_control(push) +#pragma float_control(precise, on) +#endif + +/*====================================Ceil====================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Ceil, float x) + FCALL_CONTRACT; + + return (float)ceilf(x); +FCIMPLEND + +#if defined(_MSC_VER) && defined(TARGET_AMD64) +#pragma float_control(pop) +#endif + +/*=====================================Cos====================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Cos, float x) + FCALL_CONTRACT; + + return (float)cosf(x); +FCIMPLEND + +/*=====================================Cosh===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Cosh, float x) + FCALL_CONTRACT; + + return (float)coshf(x); +FCIMPLEND + +/*=====================================Exp====================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Exp, float x) + FCALL_CONTRACT; + + return (float)expf(x); +FCIMPLEND + +/*====================================Floor===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Floor, float x) + FCALL_CONTRACT; + + return (float)floorf(x); +FCIMPLEND + +/*=====================================FMod===================================== +** +==============================================================================*/ +FCIMPL2_VV(float, COMSingle::FMod, float x, float y) + FCALL_CONTRACT; + + return (float)fmodf(x, y); +FCIMPLEND + +/*=====================================FusedMultiplyAdd========================== +** +==============================================================================*/ +FCIMPL3_VVV(float, COMSingle::FusedMultiplyAdd, float x, float y, float z) + FCALL_CONTRACT; + + return (float)fmaf(x, y, z); +FCIMPLEND + +/*=====================================Ilog2==================================== +** +==============================================================================*/ +FCIMPL1_V(int, COMSingle::ILogB, float x) + FCALL_CONTRACT; + + return (int)ilogbf(x); +FCIMPLEND + +/*=====================================Log====================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Log, float x) + FCALL_CONTRACT; + + return (float)logf(x); +FCIMPLEND + +/*=====================================Log2===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Log2, float x) + FCALL_CONTRACT; + + return (float)log2f(x); +FCIMPLEND + +/*====================================Log10===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Log10, float x) + FCALL_CONTRACT; + + return (float)log10f(x); +FCIMPLEND + +/*=====================================ModF===================================== +** +==============================================================================*/ +FCIMPL2_VI(float, COMSingle::ModF, float x, float* intptr) + FCALL_CONTRACT; + + return (float)modff(x, intptr); +FCIMPLEND + +/*=====================================Pow====================================== +** +==============================================================================*/ +FCIMPL2_VV(float, COMSingle::Pow, float x, float y) + FCALL_CONTRACT; + + return (float)powf(x, y); +FCIMPLEND + +/*=====================================ScaleB=================================== +** +==============================================================================*/ +FCIMPL2_VI(float, COMSingle::ScaleB, float x, int n) + FCALL_CONTRACT; + + return (float)scalbnf(x, n); +FCIMPLEND + +/*=====================================Sin====================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Sin, float x) + FCALL_CONTRACT; + + return (float)sinf(x); +FCIMPLEND + +/*=====================================Sinh===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Sinh, float x) + FCALL_CONTRACT; + + return (float)sinhf(x); +FCIMPLEND + +/*=====================================Sqrt===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Sqrt, float x) + FCALL_CONTRACT; + + return (float)sqrtf(x); +FCIMPLEND + +/*=====================================Tan====================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Tan, float x) + FCALL_CONTRACT; + + return (float)tanf(x); +FCIMPLEND + +/*=====================================Tanh===================================== +** +==============================================================================*/ +FCIMPL1_V(float, COMSingle::Tanh, float x) + FCALL_CONTRACT; + + return (float)tanhf(x); +FCIMPLEND + +#ifdef _MSC_VER +#pragma float_control(pop) +#endif + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +/// +/// End of /fp:fast scope +/// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/classlibnative/inc/floatdouble.h b/src/coreclr/classlibnative/inc/floatdouble.h new file mode 100644 index 00000000000..eb430409b6f --- /dev/null +++ b/src/coreclr/classlibnative/inc/floatdouble.h @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _FLOATDOUBLE_H_ +#define _FLOATDOUBLE_H_ + +#include <object.h> +#include <fcall.h> + +class COMDouble { +public: + FCDECL1_V(static double, Abs, double x); + FCDECL1_V(static double, Acos, double x); + FCDECL1_V(static double, Acosh, double x); + FCDECL1_V(static double, Asin, double x); + FCDECL1_V(static double, Asinh, double x); + FCDECL1_V(static double, Atan, double x); + FCDECL1_V(static double, Atanh, double x); + FCDECL2_VV(static double, Atan2, double y, double x); + FCDECL1_V(static double, Cbrt, double x); + FCDECL1_V(static double, Ceil, double x); + FCDECL1_V(static double, Cos, double x); + FCDECL1_V(static double, Cosh, double x); + FCDECL1_V(static double, Exp, double x); + FCDECL1_V(static double, Floor, double x); + FCDECL2_VV(static double, FMod, double x, double y); + FCDECL3_VVV(static double, FusedMultiplyAdd, double x, double y, double z); + FCDECL1_V(static int, ILogB, double x); + FCDECL1_V(static double, Log, double x); + FCDECL1_V(static double, Log2, double x); + FCDECL1_V(static double, Log10, double x); + FCDECL2_VI(static double, ModF, double x, double* intptr); + FCDECL2_VV(static double, Pow, double x, double y); + FCDECL2_VI(static double, ScaleB, double x, int n); + FCDECL1_V(static double, Sin, double x); + FCDECL1_V(static double, Sinh, double x); + FCDECL1_V(static double, Sqrt, double x); + FCDECL1_V(static double, Tan, double x); + FCDECL1_V(static double, Tanh, double x); +}; + +#endif // _FLOATDOUBLE_H_ diff --git a/src/coreclr/classlibnative/inc/floatsingle.h b/src/coreclr/classlibnative/inc/floatsingle.h new file mode 100644 index 00000000000..2658cb08edd --- /dev/null +++ b/src/coreclr/classlibnative/inc/floatsingle.h @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _FLOATSINGLE_H_ +#define _FLOATSINGLE_H_ + +#include <object.h> +#include <fcall.h> + +class COMSingle { +public: + FCDECL1_V(static float, Abs, float x); + FCDECL1_V(static float, Acos, float x); + FCDECL1_V(static float, Acosh, float x); + FCDECL1_V(static float, Asin, float x); + FCDECL1_V(static float, Asinh, float x); + FCDECL1_V(static float, Atan, float x); + FCDECL1_V(static float, Atanh, float x); + FCDECL2_VV(static float, Atan2, float y, float x); + FCDECL1_V(static float, Cbrt, float x); + FCDECL1_V(static float, Ceil, float x); + FCDECL1_V(static float, Cos, float x); + FCDECL1_V(static float, Cosh, float x); + FCDECL1_V(static float, Exp, float x); + FCDECL1_V(static float, Floor, float x); + FCDECL2_VV(static float, FMod, float x, float y); + FCDECL3_VVV(static float, FusedMultiplyAdd, float x, float y, float z); + FCDECL1_V(static int, ILogB, float x); + FCDECL1_V(static float, Log, float x); + FCDECL1_V(static float, Log2, float x); + FCDECL1_V(static float, Log10, float x); + FCDECL2_VI(static float, ModF, float x, float* intptr); + FCDECL2_VV(static float, Pow, float x, float y); + FCDECL2_VI(static float, ScaleB, float x, int n); + FCDECL1_V(static float, Sin, float x); + FCDECL1_V(static float, Sinh, float x); + FCDECL1_V(static float, Sqrt, float x); + FCDECL1_V(static float, Tan, float x); + FCDECL1_V(static float, Tanh, float x); +}; + +#endif // _FLOATSINGLE_H_ diff --git a/src/coreclr/classlibnative/inc/nls.h b/src/coreclr/classlibnative/inc/nls.h new file mode 100644 index 00000000000..b453aa843fc --- /dev/null +++ b/src/coreclr/classlibnative/inc/nls.h @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +//////////////////////////////////////////////////////////////////////////// +// +// Module: NLS +// + +// +// Purpose: This module defines the common header information for +// the Globalization classes. +// +// Date: August 12, 1998 +// +//////////////////////////////////////////////////////////////////////////// + + +#ifndef _NLS_H_ +#define _NLS_H_ + + +// +// Constant Declarations. +// + +#define HIGH_SURROGATE_START 0xd800 +#define HIGH_SURROGATE_END 0xdbff +#define LOW_SURROGATE_START 0xdc00 +#define LOW_SURROGATE_END 0xdfff + +#define PRIVATE_USE_BEGIN 0xe000 +#define PRIVATE_USE_END 0xf8ff + +#endif // _NLS_H_ |