summaryrefslogtreecommitdiff
path: root/src/vm/array.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/array.cpp')
-rw-r--r--src/vm/array.cpp1434
1 files changed, 1434 insertions, 0 deletions
diff --git a/src/vm/array.cpp b/src/vm/array.cpp
new file mode 100644
index 0000000000..d6792942e7
--- /dev/null
+++ b/src/vm/array.cpp
@@ -0,0 +1,1434 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// File: ARRAY.CPP
+//
+
+//
+// File which contains a bunch of of array related things.
+//
+
+#include "common.h"
+
+#include "clsload.hpp"
+#include "method.hpp"
+#include "class.h"
+#include "object.h"
+#include "field.h"
+#include "util.hpp"
+#include "excep.h"
+#include "siginfo.hpp"
+#include "threads.h"
+#include "stublink.h"
+#include "stubcache.h"
+#include "dllimport.h"
+#include "gcdesc.h"
+#include "jitinterface.h"
+#include "eeconfig.h"
+#include "log.h"
+#include "fieldmarshaler.h"
+#include "cgensys.h"
+#include "array.h"
+#include "typestring.h"
+#include "sigbuilder.h"
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4244)
+#endif // _MSC_VER
+
+#define MAX_SIZE_FOR_VALUECLASS_IN_ARRAY 0xffff
+#define MAX_PTRS_FOR_VALUECLASSS_IN_ARRAY 0xffff
+
+
+/*****************************************************************************************/
+LPCUTF8 ArrayMethodDesc::GetMethodName()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ switch (GetArrayFuncIndex())
+ {
+ case ARRAY_FUNC_GET:
+ return "Get";
+ case ARRAY_FUNC_SET:
+ return "Set";
+ case ARRAY_FUNC_ADDRESS:
+ return "Address";
+ default:
+ return COR_CTOR_METHOD_NAME; // ".ctor"
+ }
+}
+
+/*****************************************************************************************/
+DWORD ArrayMethodDesc::GetAttrs()
+{
+ LIMITED_METHOD_CONTRACT;
+ return (GetArrayFuncIndex() >= ARRAY_FUNC_CTOR) ? (mdPublic | mdRTSpecialName) : mdPublic;
+}
+
+/*****************************************************************************************/
+CorInfoIntrinsics ArrayMethodDesc::GetIntrinsicID()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ switch (GetArrayFuncIndex())
+ {
+ case ARRAY_FUNC_GET:
+ return CORINFO_INTRINSIC_Array_Get;
+ case ARRAY_FUNC_SET:
+ return CORINFO_INTRINSIC_Array_Set;
+ case ARRAY_FUNC_ADDRESS:
+ return CORINFO_INTRINSIC_Array_Address;
+ default:
+ return CORINFO_INTRINSIC_Illegal;
+ }
+}
+
+#ifndef DACCESS_COMPILE
+
+/*****************************************************************************************/
+
+//
+// Generate a short sig (descr) for an array accessors
+//
+
+VOID ArrayClass::GenerateArrayAccessorCallSig(
+ DWORD dwRank,
+ DWORD dwFuncType, // Load, store, or <init>
+ PCCOR_SIGNATURE *ppSig,// Generated signature
+ DWORD * pcSig, // Generated signature size
+ LoaderAllocator *pLoaderAllocator,
+ AllocMemTracker *pamTracker
+#ifdef FEATURE_ARRAYSTUB_AS_IL
+ ,BOOL fForStubAsIL
+#endif
+ )
+{
+ CONTRACTL {
+ STANDARD_VM_CHECK;
+ PRECONDITION(dwRank >= 1 && dwRank < 0x3ffff);
+ } CONTRACTL_END;
+
+ PCOR_SIGNATURE pSig;
+ PCOR_SIGNATURE pSigMemory;
+ DWORD dwCallSigSize = dwRank;
+ DWORD dwArgCount = (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET) ? dwRank+1 : dwRank;
+ DWORD i;
+
+ switch (dwFuncType)
+ {
+ // <callconv> <argcount> VAR 0 I4 , ... , I4
+ case ArrayMethodDesc::ARRAY_FUNC_GET:
+ dwCallSigSize += 4;
+ break;
+
+ // <callconv> <argcount> VOID I4 , ... , I4
+ case ArrayMethodDesc::ARRAY_FUNC_CTOR:
+ dwCallSigSize += 3;
+ break;
+
+ // <callconv> <argcount> VOID I4 , ... , I4 VAR 0
+ case ArrayMethodDesc::ARRAY_FUNC_SET:
+ dwCallSigSize += 5;
+ break;
+
+ // <callconv> <argcount> BYREF VAR 0 I4 , ... , I4
+ case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
+ dwCallSigSize += 5;
+#ifdef FEATURE_ARRAYSTUB_AS_IL
+ if(fForStubAsIL) {dwArgCount++; dwCallSigSize++;}
+#endif
+ break;
+ }
+
+ // If the argument count is larger than 127 then it will require 2 bytes for the encoding
+ if (dwArgCount > 0x7f)
+ dwCallSigSize++;
+
+ pSigMemory = (PCOR_SIGNATURE)pamTracker->Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(dwCallSigSize)));
+
+ pSig = pSigMemory;
+ BYTE callConv = IMAGE_CEE_CS_CALLCONV_DEFAULT + IMAGE_CEE_CS_CALLCONV_HASTHIS;
+
+ if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS
+#ifdef FEATURE_ARRAYSTUB_AS_IL
+ && !fForStubAsIL
+#endif
+ )
+ {
+ callConv |= CORINFO_CALLCONV_PARAMTYPE; // Address routine needs special hidden arg
+ }
+
+ *pSig++ = callConv;
+ pSig += CorSigCompressData(dwArgCount, pSig); // Argument count
+ switch (dwFuncType)
+ {
+ case ArrayMethodDesc::ARRAY_FUNC_GET:
+ *pSig++ = ELEMENT_TYPE_VAR;
+ *pSig++ = 0; // variable 0
+ break;
+ case ArrayMethodDesc::ARRAY_FUNC_CTOR:
+ *pSig++ = (BYTE) ELEMENT_TYPE_VOID; // Return type
+ break;
+ case ArrayMethodDesc::ARRAY_FUNC_SET:
+ *pSig++ = (BYTE) ELEMENT_TYPE_VOID; // Return type
+ break;
+ case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
+ *pSig++ = (BYTE) ELEMENT_TYPE_BYREF; // Return type
+ *pSig++ = ELEMENT_TYPE_VAR;
+ *pSig++ = 0; // variable 0
+ break;
+ }
+
+#if defined(FEATURE_ARRAYSTUB_AS_IL ) && !defined(_TARGET_X86_)
+ if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
+ {
+ *pSig++ = ELEMENT_TYPE_I;
+ }
+#endif
+
+ for (i = 0; i < dwRank; i++)
+ *pSig++ = ELEMENT_TYPE_I4;
+
+ if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET)
+ {
+ *pSig++ = ELEMENT_TYPE_VAR;
+ *pSig++ = 0; // variable 0
+ }
+#if defined(FEATURE_ARRAYSTUB_AS_IL ) && defined(_TARGET_X86_)
+ else if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
+ {
+ *pSig++ = ELEMENT_TYPE_I;
+ }
+#endif
+
+ // Make sure the sig came out exactly as large as we expected
+ _ASSERTE(pSig == pSigMemory + dwCallSigSize);
+
+ *ppSig = pSigMemory;
+ *pcSig = (DWORD)(pSig-pSigMemory);
+}
+
+//
+// Allocate a new MethodDesc for a fake array method.
+//
+// Based on code in class.cpp.
+//
+void ArrayClass::InitArrayMethodDesc(
+ ArrayMethodDesc *pNewMD,
+ PCCOR_SIGNATURE pShortSig,
+ DWORD cShortSig,
+ DWORD dwVtableSlot,
+ LoaderAllocator *pLoaderAllocator,
+ AllocMemTracker *pamTracker)
+{
+ STANDARD_VM_CONTRACT;
+
+ // Note: The method desc memory is zero initialized
+
+ pNewMD->SetMemberDef(0);
+
+ pNewMD->SetSlot((WORD) dwVtableSlot);
+ pNewMD->SetStoredMethodSig(pShortSig, cShortSig);
+
+ _ASSERTE(!pNewMD->MayHaveNativeCode());
+ pNewMD->SetTemporaryEntryPoint(pLoaderAllocator, pamTracker);
+
+#ifdef _DEBUG
+ _ASSERTE(pNewMD->GetMethodName() && GetDebugClassName());
+ pNewMD->m_pszDebugMethodName = pNewMD->GetMethodName();
+ pNewMD->m_pszDebugClassName = GetDebugClassName();
+ pNewMD->m_pDebugMethodTable.SetValue(pNewMD->GetMethodTable());
+#endif // _DEBUG
+}
+
+/*****************************************************************************************/
+MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementType arrayKind, unsigned Rank, AllocMemTracker *pamTracker)
+{
+ CONTRACTL {
+ STANDARD_VM_CHECK;
+ PRECONDITION(Rank > 0);
+ } CONTRACTL_END;
+
+ MethodTable * pElemMT = elemTypeHnd.GetMethodTable();
+
+ CorElementType elemType = elemTypeHnd.GetSignatureCorElementType();
+
+ // Shared EEClass if there is one
+ MethodTable * pCanonMT = NULL;
+
+ // Strictly speaking no method table should be needed for
+ // arrays of the faked up TypeDescs for variable types that are
+ // used when verfifying generic code.
+ // However verification is tied in with some codegen in the JITs, so give these
+ // the shared MT just in case.
+ // This checks match precisely one in ParamTypeDesc::OwnsMethodTable
+ if (CorTypeInfo::IsGenericVariable(elemType)) {
+ // This is loading the canonical version of the array so we can override
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+ return(ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass), arrayKind, Rank).GetMethodTable());
+ }
+
+ // Arrays of reference types all share the same EEClass.
+ //
+ // We can't share nested SZARRAYs because they have different
+ // numbers of constructors.
+ //
+ // Unfortunately, we cannot share more because of it would affect user visible System.RuntimeMethodHandle behavior
+ if (CorTypeInfo::IsObjRef(elemType) && elemType != ELEMENT_TYPE_SZARRAY && pElemMT != g_pObjectClass)
+ {
+ // This is loading the canonical version of the array so we can override
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+ pCanonMT = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass), arrayKind, Rank).GetMethodTable();
+ }
+
+ BOOL containsPointers = CorTypeInfo::IsObjRef(elemType);
+ if (elemType == ELEMENT_TYPE_VALUETYPE && pElemMT->ContainsPointers())
+ containsPointers = TRUE;
+
+ // this is the base for every array type
+ MethodTable *pParentClass = g_pArrayClass;
+ _ASSERTE(pParentClass); // Must have already loaded the System.Array class
+ _ASSERTE(pParentClass->IsFullyLoaded());
+
+ DWORD numCtors = 2; // ELEMENT_TYPE_ARRAY has two ctor functions, one with and one without lower bounds
+ if (arrayKind == ELEMENT_TYPE_SZARRAY)
+ {
+ numCtors = 1;
+ TypeHandle ptr = elemTypeHnd;
+ while (ptr.IsTypeDesc() && ptr.AsTypeDesc()->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY) {
+ numCtors++;
+ ptr = ptr.AsTypeDesc()->GetTypeParam();
+ }
+ }
+
+ /****************************************************************************************/
+
+ // Parent class is the top level array
+ // The vtable will have all of top level class's methods, plus any methods we have for array classes
+ DWORD numVirtuals = pParentClass->GetNumVirtuals();
+ DWORD numNonVirtualSlots = numCtors + 3; // 3 for the proper rank Get, Set, Address
+
+ size_t cbMT = sizeof(MethodTable);
+ cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(PTR_PCODE);
+
+ // GC info
+ size_t cbCGCDescData = 0;
+ if (containsPointers)
+ {
+ cbCGCDescData += CGCDesc::ComputeSize(1);
+ if (elemType == ELEMENT_TYPE_VALUETYPE)
+ {
+ size_t nSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
+ cbCGCDescData += (nSeries - 1)*sizeof (val_serie_item);
+ _ASSERTE(cbCGCDescData == CGCDesc::ComputeSizeRepeating(nSeries));
+ }
+ }
+#ifdef FEATURE_COLLECTIBLE_TYPES
+ else if (this->IsCollectible())
+ {
+ cbCGCDescData = (DWORD)CGCDesc::ComputeSize(1);
+ }
+#endif
+
+ DWORD dwMultipurposeSlotsMask = 0;
+ dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasPerInstInfo;
+ dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasInterfaceMap;
+ if (pCanonMT == NULL)
+ dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasNonVirtualSlots;
+ if (this != elemTypeHnd.GetModule())
+ dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasModuleOverride;
+
+ // Allocate space for optional members
+ // We always have a non-virtual slot array, see assert at end
+ cbMT += MethodTable::GetOptionalMembersAllocationSize(dwMultipurposeSlotsMask,
+ FALSE, // RemotableMethodInfo
+ FALSE, // GenericsStaticsInfo
+ FALSE, // GuidInfo
+ FALSE, // CCWTemplate
+ FALSE, // RCWPerTypeData
+ FALSE, // RemotingVtsInfo
+ FALSE, // ContextStatic
+ FALSE); // TokenOverflow
+
+ // This is the offset of the beginning of the interface map
+ size_t imapOffset = cbMT;
+
+ // This is added after we determine the offset of the interface maps
+ // because the memory appears before the pointer to the method table
+ cbMT += cbCGCDescData;
+
+ // Inherit top level class's interface map
+ cbMT += pParentClass->GetNumInterfaces() * sizeof(InterfaceInfo_t);
+
+#ifdef FEATURE_PREJIT
+ Module* pComputedPZM = Module::ComputePreferredZapModule(NULL, Instantiation(&elemTypeHnd, 1));
+ BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this, pComputedPZM);
+#else
+ BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this);
+#endif // FEATURE_PREJIT
+
+ size_t offsetOfUnsharedVtableChunks = cbMT;
+
+ // We either share all of the parent's virtual slots or none of them
+ // If none, we need to allocate space for the slots
+ if (!canShareVtableChunks)
+ {
+ cbMT += numVirtuals * sizeof(PCODE);
+ }
+
+ // Canonical methodtable has an array of non virtual slots pointed to by the optional member
+ size_t offsetOfNonVirtualSlots = 0;
+ size_t cbArrayClass = 0;
+
+ if (pCanonMT == NULL)
+ {
+ offsetOfNonVirtualSlots = cbMT;
+ cbMT += numNonVirtualSlots * sizeof(PCODE);
+
+ // Allocate ArrayClass (including space for packed fields), MethodTable, and class name in one alloc.
+ // Remember to pad allocation size for ArrayClass portion to ensure MethodTable is pointer aligned.
+ cbArrayClass = ALIGN_UP(sizeof(ArrayClass) + sizeof(EEClassPackedFields), sizeof(void*));
+ }
+
+ // ArrayClass already includes one void*
+ LoaderAllocator* pAllocator= this->GetLoaderAllocator();
+ BYTE* pMemory = (BYTE *)pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbArrayClass) +
+ S_SIZE_T(cbMT)));
+
+ // Note: Memory allocated on loader heap is zero filled
+ // memset(pMemory, 0, sizeof(ArrayClass) + cbMT);
+
+ ArrayClass* pClass = NULL;
+
+ if (pCanonMT == NULL)
+ {
+ pClass = ::new (pMemory) ArrayClass();
+ }
+
+ // Head of MethodTable memory (starts after ArrayClass), this points at the GCDesc stuff in front
+ // of a method table (if needed)
+ BYTE* pMTHead = pMemory + cbArrayClass + cbCGCDescData;
+
+ MethodTable* pMT = (MethodTable *) pMTHead;
+
+ pMT->SetMultipurposeSlotsMask(dwMultipurposeSlotsMask);
+
+ // Allocate the private data block ("private" during runtime in the ngen'ed case).
+ MethodTableWriteableData * pMTWriteableData = (MethodTableWriteableData *) (BYTE *)
+ pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(MethodTableWriteableData))));
+ pMT->SetWriteableData(pMTWriteableData);
+
+ // This also disables IBC logging until the type is sufficiently intitialized so
+ // it needs to be done early
+ pMTWriteableData->SetIsNotFullyLoadedForBuildMethodTable();
+
+ // Fill in pClass
+ if (pClass != NULL)
+ {
+ pClass->SetInternalCorElementType(arrayKind);
+ pClass->SetAttrClass (tdPublic | tdSerializable | tdSealed); // This class is public, serializable, sealed
+ pClass->SetRank (Rank);
+ pClass->SetArrayElementType (elemType);
+ pClass->SetMethodTable (pMT);
+
+#if defined(CHECK_APP_DOMAIN_LEAKS) || defined(_DEBUG)
+ // Non-covariant arrays of agile types are agile
+ if (elemType != ELEMENT_TYPE_CLASS && elemTypeHnd.IsAppDomainAgile())
+ pClass->SetAppDomainAgile();
+ pClass->SetAppDomainAgilityDone();
+#endif
+
+ // Fill In the method table
+ pClass->SetNumMethods(numVirtuals + numNonVirtualSlots);
+
+ pClass->SetNumNonVirtualSlots(numNonVirtualSlots);
+ }
+
+ pMT->SetNumVirtuals(numVirtuals);
+
+ pMT->SetParentMethodTable(pParentClass);
+
+ DWORD dwComponentSize = elemTypeHnd.GetSize();
+
+ if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
+ {
+ // The only way for dwComponentSize to be large is to be part of a value class. If this changes
+ // then the check will need to be moved outside valueclass check.
+ if(dwComponentSize > MAX_SIZE_FOR_VALUECLASS_IN_ARRAY) {
+ StackSString ssElemName;
+ elemTypeHnd.GetName(ssElemName);
+
+ StackScratchBuffer scratch;
+ elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch), IDS_CLASSLOAD_VALUECLASSTOOLARGE);
+ }
+ }
+
+ if (pClass != NULL)
+ {
+ pMT->SetClass(pClass);
+ }
+ else
+ {
+ pMT->SetCanonicalMethodTable(pCanonMT);
+ }
+
+ pMT->SetIsArray(arrayKind, elemType);
+
+ pMT->SetApproxArrayElementTypeHandle(elemTypeHnd);
+
+ _ASSERTE(FitsIn<WORD>(dwComponentSize));
+ pMT->SetComponentSize(static_cast<WORD>(dwComponentSize));
+
+ pMT->SetLoaderModule(this);
+ pMT->SetLoaderAllocator(pAllocator);
+
+ pMT->SetModule(elemTypeHnd.GetModule());
+
+ if (elemTypeHnd.ContainsGenericVariables())
+ pMT->SetContainsGenericVariables();
+
+#ifdef FEATURE_TYPEEQUIVALENCE
+ if (elemTypeHnd.HasTypeEquivalence())
+ {
+ // propagate the type equivalence flag
+ pMT->SetHasTypeEquivalence();
+ }
+#endif // FEATURE_TYPEEQUIVALENCE
+
+ _ASSERTE(pMT->IsClassPreInited());
+
+ // Set BaseSize to be size of non-data portion of the array
+ DWORD baseSize = ObjSizeOf(ArrayBase);
+ if (arrayKind == ELEMENT_TYPE_ARRAY)
+ baseSize += Rank*sizeof(DWORD)*2;
+
+#if !defined(_WIN64) && (DATA_ALIGNMENT > 4)
+ if (dwComponentSize >= DATA_ALIGNMENT)
+ baseSize = (DWORD)ALIGN_UP(baseSize, DATA_ALIGNMENT);
+#endif // !defined(_WIN64) && (DATA_ALIGNMENT > 4)
+ pMT->SetBaseSize(baseSize);
+ // Because of array method table persisting, we need to copy the map
+ memcpy(pMTHead + imapOffset, pParentClass->GetInterfaceMap(),
+ pParentClass->GetNumInterfaces() * sizeof(InterfaceInfo_t));
+ pMT->SetInterfaceMap(pParentClass->GetNumInterfaces(), (InterfaceInfo_t *)(pMTHead + imapOffset));
+
+ // Copy down flags for these interfaces as well. This is simplified a bit since we know that System.Array
+ // only has a few interfaces and the flags will fit inline into the MethodTable's optional members.
+ _ASSERTE(MethodTable::GetExtraInterfaceInfoSize(pParentClass->GetNumInterfaces()) == 0);
+ pMT->InitializeExtraInterfaceInfo(NULL);
+
+ for (UINT32 i = 0; i < pParentClass->GetNumInterfaces(); i++)
+ {
+ if (pParentClass->IsInterfaceDeclaredOnClass(i))
+ pMT->SetInterfaceDeclaredOnClass(i);
+ }
+
+ // The type is sufficiently initialized for most general purpose accessor methods to work.
+ // Mark the type as restored to avoid asserts. Note that this also enables IBC logging.
+ pMTWriteableData->SetIsFullyLoadedForBuildMethodTable();
+
+ {
+ // Fill out the vtable indirection slots
+ MethodTable::VtableIndirectionSlotIterator it = pMT->IterateVtableIndirectionSlots();
+ while (it.Next())
+ {
+ if (canShareVtableChunks)
+ {
+ // Share the parent chunk
+ it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()]);
+ }
+ else
+ {
+ // Use the locally allocated chunk
+ it.SetIndirectionSlot((PTR_PCODE)(pMemory+cbArrayClass+offsetOfUnsharedVtableChunks));
+ offsetOfUnsharedVtableChunks += it.GetSize();
+ }
+ }
+
+ // If we are not sharing parent chunks, copy down the slot contents
+ if (!canShareVtableChunks)
+ {
+ // Copy top level class's vtable - note, vtable is contained within the MethodTable
+ for (UINT32 i = 0; i < numVirtuals; i++)
+ pMT->SetSlot(i, pParentClass->GetSlot(i));
+ }
+
+ if (pClass != NULL)
+ pMT->SetNonVirtualSlotsArray((PTR_PCODE)(pMemory+cbArrayClass+offsetOfNonVirtualSlots));
+ }
+
+#ifdef _DEBUG
+ StackSString debugName;
+ TypeString::AppendType(debugName, TypeHandle(pMT));
+ StackScratchBuffer buff;
+ const char* pDebugNameUTF8 = debugName.GetUTF8(buff);
+ S_SIZE_T safeLen = S_SIZE_T(strlen(pDebugNameUTF8))+S_SIZE_T(1);
+ if(safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
+ size_t len = safeLen.Value();
+ char * name = (char*) pamTracker->Track(pAllocator->
+ GetHighFrequencyHeap()->
+ AllocMem(safeLen));
+ strcpy_s(name, len, pDebugNameUTF8);
+
+ if (pClass != NULL)
+ pClass->SetDebugClassName(name);
+ pMT->SetDebugClassName(name);
+#endif // _DEBUG
+
+ if (pClass != NULL)
+ {
+ // Count the number of method descs we need so we can allocate chunks.
+ DWORD dwMethodDescs = numCtors
+ + 3; // for rank specific Get, Set, Address
+
+ MethodDescChunk * pChunks = MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
+ dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */, FALSE /* fComPlusCallInfo */,
+ pMT, pamTracker);
+ pClass->SetChunks(pChunks);
+
+ MethodTable::IntroducedMethodIterator it(pMT);
+
+ DWORD dwMethodIndex = 0;
+ for (; it.IsValid(); it.Next())
+ {
+ ArrayMethodDesc* pNewMD = (ArrayMethodDesc *) it.GetMethodDesc();
+ _ASSERTE(pNewMD->GetClassification() == mcArray);
+
+ DWORD dwFuncRank;
+ DWORD dwFuncType;
+
+ if (dwMethodIndex < ArrayMethodDesc::ARRAY_FUNC_CTOR)
+ {
+ // Generate a new stand-alone, Rank Specific Get, Set and Address method.
+ dwFuncRank = Rank;
+ dwFuncType = dwMethodIndex;
+ }
+ else
+ {
+ if (arrayKind == ELEMENT_TYPE_SZARRAY)
+ {
+ // For SZARRAY arrays, set up multiple constructors.
+ dwFuncRank = 1 + (dwMethodIndex - ArrayMethodDesc::ARRAY_FUNC_CTOR);
+ }
+ else
+ {
+ // ELEMENT_TYPE_ARRAY has two constructors, one without lower bounds and one with lower bounds
+ _ASSERTE((dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR) || (dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR+1));
+ dwFuncRank = (dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR) ? Rank : 2 * Rank;
+ }
+ dwFuncType = ArrayMethodDesc::ARRAY_FUNC_CTOR;
+ }
+
+ PCCOR_SIGNATURE pSig;
+ DWORD cSig;
+
+ pClass->GenerateArrayAccessorCallSig(dwFuncRank, dwFuncType, &pSig, &cSig, pAllocator, pamTracker
+ #ifdef FEATURE_ARRAYSTUB_AS_IL
+ ,0
+ #endif
+ );
+
+ pClass->InitArrayMethodDesc(pNewMD, pSig, cSig, numVirtuals + dwMethodIndex, pAllocator, pamTracker);
+
+ dwMethodIndex++;
+ }
+ _ASSERTE(dwMethodIndex == dwMethodDescs);
+ }
+
+ // Set up GC information
+ if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
+ {
+ // If it's an array of value classes, there is a different format for the GCDesc if it contains pointers
+ if (pElemMT->ContainsPointers())
+ {
+ CGCDescSeries *pSeries;
+
+ // There must be only one series for value classes
+ CGCDescSeries *pByValueSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetHighestSeries();
+
+ pMT->SetContainsPointers();
+
+ // negative series has a special meaning, indicating a different form of GCDesc
+ SSIZE_T nSeries = (SSIZE_T) CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
+ CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, nSeries);
+
+ pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
+
+ // sort by offset
+ SSIZE_T AllocSizeSeries;
+ if (!ClrSafeInt<SSIZE_T>::multiply(sizeof(CGCDescSeries*), nSeries, AllocSizeSeries))
+ COMPlusThrowOM();
+ CGCDescSeries** sortedSeries = (CGCDescSeries**) _alloca(AllocSizeSeries);
+ int index;
+ for (index = 0; index < nSeries; index++)
+ sortedSeries[index] = &pByValueSeries[-index];
+
+ // section sort
+ for (int i = 0; i < nSeries; i++) {
+ for (int j = i+1; j < nSeries; j++)
+ if (sortedSeries[j]->GetSeriesOffset() < sortedSeries[i]->GetSeriesOffset())
+ {
+ CGCDescSeries* temp = sortedSeries[i];
+ sortedSeries[i] = sortedSeries[j];
+ sortedSeries[j] = temp;
+ }
+ }
+
+ // Offset of the first pointer in the array
+ // This equals the offset of the first pointer if this were an array of entirely pointers, plus the offset of the
+ // first pointer in the value class
+ pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT)
+ + (sortedSeries[0]->GetSeriesOffset()) - sizeof (Object) );
+ for (index = 0; index < nSeries; index ++)
+ {
+ size_t numPtrsInBytes = sortedSeries[index]->GetSeriesSize()
+ + pElemMT->GetBaseSize();
+ size_t currentOffset;
+ size_t skip;
+ currentOffset = sortedSeries[index]->GetSeriesOffset()+numPtrsInBytes;
+ if (index != nSeries-1)
+ {
+ skip = sortedSeries[index+1]->GetSeriesOffset()-currentOffset;
+ }
+ else if (index == 0)
+ {
+ skip = pElemMT->GetAlignedNumInstanceFieldBytes() - numPtrsInBytes;
+ }
+ else
+ {
+ skip = sortedSeries[0]->GetSeriesOffset() + pElemMT->GetBaseSize()
+ - ObjSizeOf(Object) - currentOffset;
+ }
+
+ _ASSERTE(!"Module::CreateArrayMethodTable() - unaligned GC info" || IS_ALIGNED(skip, sizeof(size_t)));
+
+ unsigned short NumPtrs = (unsigned short) (numPtrsInBytes / sizeof(void*));
+ if(skip > MAX_SIZE_FOR_VALUECLASS_IN_ARRAY || numPtrsInBytes > MAX_PTRS_FOR_VALUECLASSS_IN_ARRAY) {
+ StackSString ssElemName;
+ elemTypeHnd.GetName(ssElemName);
+
+ StackScratchBuffer scratch;
+ elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch),
+ IDS_CLASSLOAD_VALUECLASSTOOLARGE);
+ }
+
+ val_serie_item *val_item = &(pSeries->val_serie[-index]);
+
+ val_item->set_val_serie_item (NumPtrs, (unsigned short)skip);
+ }
+ }
+ }
+ else if (CorTypeInfo::IsObjRef(elemType))
+ {
+ CGCDescSeries *pSeries;
+
+ pMT->SetContainsPointers();
+
+ // This array is all GC Pointers
+ CGCDesc::GetCGCDescFromMT(pMT)->Init( pMT, 1 );
+
+ pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
+
+ pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT));
+ // For arrays, the size is the negative of the BaseSize (the GC always adds the total
+ // size of the object, so what you end up with is the size of the data portion of the array)
+ pSeries->SetSeriesSize(-(SSIZE_T)(pMT->GetBaseSize()));
+ }
+
+#ifdef FEATURE_COLLECTIBLE_TYPES
+ if (!pMT->ContainsPointers() && this->IsCollectible())
+ {
+ CGCDescSeries *pSeries;
+
+ // For collectible types, insert empty gc series
+ CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, 1);
+ pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
+ pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT));
+ pSeries->val_serie[0].set_val_serie_item (0, pMT->GetComponentSize());
+ }
+#endif
+
+ // If we get here we are assuming that there was no truncation. If this is not the case then
+ // an array whose base type is not a value class was created and was larger then 0xffff (a word)
+ _ASSERTE(dwComponentSize == pMT->GetComponentSize());
+
+#ifdef FEATURE_PREJIT
+ _ASSERTE(pComputedPZM == Module::GetPreferredZapModuleForMethodTable(pMT));
+#endif
+
+ return(pMT);
+} // Module::CreateArrayMethodTable
+
+#ifndef CROSSGEN_COMPILE
+
+#ifdef FEATURE_ARRAYSTUB_AS_IL
+
+class ArrayOpLinker : public ILStubLinker
+{
+ ILCodeStream * m_pCode;
+ ArrayMethodDesc * m_pMD;
+
+ SigTypeContext m_emptyContext;
+
+public:
+ ArrayOpLinker(ArrayMethodDesc * pMD)
+ : ILStubLinker(pMD->GetModule(), pMD->GetSignature(), &m_emptyContext, pMD, TRUE, TRUE, FALSE)
+ {
+ m_pCode = NewCodeStream(kDispatch);
+ m_pMD = pMD;
+ }
+
+ void EmitStub()
+ {
+ MethodTable *pMT = m_pMD->GetMethodTable();
+ BOOL fHasLowerBounds = pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY;
+
+ DWORD dwTotalLocalNum = NewLocal(ELEMENT_TYPE_I4);
+ DWORD dwFactorLocalNum = NewLocal(ELEMENT_TYPE_I4);
+ DWORD dwLengthLocalNum = NewLocal(ELEMENT_TYPE_I4);
+
+ mdToken tokPinningHelper = GetToken(MscorlibBinder::GetField(FIELD__PINNING_HELPER__M_DATA));
+
+ ILCodeLabel * pRangeExceptionLabel = NewCodeLabel();
+ ILCodeLabel * pRangeExceptionLabel1 = NewCodeLabel();
+ ILCodeLabel * pCheckDone = NewCodeLabel();
+ ILCodeLabel * pNotSZArray = NewCodeLabel();
+ ILCodeLabel * pTypeMismatchExceptionLabel = NULL;
+
+ UINT rank = pMT->GetRank();
+ UINT idx = rank;
+ UINT firstIdx = 0;
+ UINT hiddenArgIdx = rank;
+ _ASSERTE(rank>0);
+
+
+#ifndef _TARGET_X86_
+ if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
+ {
+ idx++;
+ firstIdx = 1;
+ hiddenArgIdx = 0;
+ }
+#endif
+
+ ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
+ if(pcls->GetArrayElementType() == ELEMENT_TYPE_CLASS)
+ {
+ // Type Check
+ if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_SET)
+ {
+ ILCodeLabel * pTypeCheckOK = NewCodeLabel();
+
+ m_pCode->EmitLDARG(rank); // load value to store
+ m_pCode->EmitBRFALSE(pTypeCheckOK); //Storing NULL is OK
+
+ m_pCode->EmitLDARG(rank); // return param
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
+ m_pCode->EmitSUB();
+ m_pCode->EmitLDIND_I(); // TypeHandle
+
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
+ m_pCode->EmitSUB();
+ m_pCode->EmitLDIND_I(); // Array MT
+ m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
+ m_pCode->EmitADD();
+ m_pCode->EmitLDIND_I();
+
+ m_pCode->EmitCEQ();
+ m_pCode->EmitBRTRUE(pTypeCheckOK); // Same type is OK
+
+ // Call type check helper
+ m_pCode->EmitLDARG(rank);
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitCALL(METHOD__STUBHELPERS__ARRAY_TYPE_CHECK,2,0);
+
+ m_pCode->EmitLabel(pTypeCheckOK);
+
+ }
+ else if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
+ {
+ // Check that the hidden param is same type
+ ILCodeLabel *pTypeCheckPassed = NewCodeLabel();
+ pTypeMismatchExceptionLabel = NewCodeLabel();
+
+ m_pCode->EmitLDARG(hiddenArgIdx); // hidden param
+ m_pCode->EmitBRFALSE(pTypeCheckPassed);
+ m_pCode->EmitLDARG(hiddenArgIdx);
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(offsetof(ParamTypeDesc, m_Arg) - (Object::GetOffsetOfFirstField()+2));
+ m_pCode->EmitADD();
+ m_pCode->EmitLDIND_I();
+
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
+ m_pCode->EmitSUB();
+ m_pCode->EmitLDIND_I(); // Array MT
+ m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
+ m_pCode->EmitADD();
+ m_pCode->EmitLDIND_I();
+
+ m_pCode->EmitCEQ();
+ m_pCode->EmitBRFALSE(pTypeMismatchExceptionLabel); // throw exception if not same
+ m_pCode->EmitLabel(pTypeCheckPassed);
+ }
+ }
+
+ if(rank == 1 && fHasLowerBounds)
+ {
+ // check if the array is SZArray.
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
+ m_pCode->EmitSUB();
+ m_pCode->EmitLDIND_I();
+ m_pCode->EmitLDC(MethodTable::GetOffsetOfFlags());
+ m_pCode->EmitADD();
+ m_pCode->EmitLDIND_I4();
+ m_pCode->EmitLDC(MethodTable::GetIfArrayThenSzArrayFlag());
+ m_pCode->EmitAND();
+ m_pCode->EmitBRFALSE(pNotSZArray); // goto multi-dimmArray code if not szarray
+
+ // it is SZArray
+ // bounds check
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(ArrayBase::GetOffsetOfNumComponents() - Object::GetOffsetOfFirstField());
+ m_pCode->EmitADD();
+ m_pCode->EmitLDIND_I4();
+ m_pCode->EmitLDARG(firstIdx);
+ m_pCode->EmitBLE_UN(pRangeExceptionLabel);
+
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField());
+ m_pCode->EmitADD();
+ m_pCode->EmitLDARG(firstIdx);
+ m_pCode->EmitBR(pCheckDone);
+ m_pCode->EmitLabel(pNotSZArray);
+ }
+
+ while(idx-- > firstIdx)
+ {
+ // Cache length
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC((ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + (idx-firstIdx)*sizeof(DWORD));
+ m_pCode->EmitADD();
+ m_pCode->EmitLDIND_I4();
+ m_pCode->EmitSTLOC(dwLengthLocalNum);
+
+ // Fetch index
+ m_pCode->EmitLDARG(idx);
+
+ if (fHasLowerBounds)
+ {
+ // Load lower bound
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC((ArrayBase::GetLowerBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + (idx-firstIdx)*sizeof(DWORD));
+ m_pCode->EmitADD();
+ m_pCode->EmitLDIND_I4();
+
+ // Subtract lower bound
+ m_pCode->EmitSUB();
+ }
+
+ // Compare with length
+ m_pCode->EmitDUP();
+ m_pCode->EmitLDLOC(dwLengthLocalNum);
+ m_pCode->EmitBGE_UN(pRangeExceptionLabel1);
+
+ // Add to the running total if we have one already
+ if ((idx-firstIdx) != (rank - 1))
+ {
+ m_pCode->EmitLDLOC(dwFactorLocalNum);
+ m_pCode->EmitMUL();
+ m_pCode->EmitLDLOC(dwTotalLocalNum);
+ m_pCode->EmitADD();
+ }
+ m_pCode->EmitSTLOC(dwTotalLocalNum);
+
+ // Update factor if this is not the last iteration
+ if ((idx-firstIdx) != 0)
+ {
+ m_pCode->EmitLDLOC(dwLengthLocalNum);
+ if ((idx-firstIdx) != (rank - 1))
+ {
+ m_pCode->EmitLDLOC(dwFactorLocalNum);
+ m_pCode->EmitMUL();
+ }
+ m_pCode->EmitSTLOC(dwFactorLocalNum);
+ }
+ }
+
+ // Compute element address
+ m_pCode->EmitLoadThis();
+ m_pCode->EmitLDFLDA(tokPinningHelper);
+ m_pCode->EmitLDC(ArrayBase::GetDataPtrOffset(pMT) - Object::GetOffsetOfFirstField());
+ m_pCode->EmitADD();
+ m_pCode->EmitLDLOC(dwTotalLocalNum);
+
+ m_pCode->EmitLabel(pCheckDone);
+
+ SIZE_T elemSize = pMT->GetComponentSize();
+ if (elemSize != 1)
+ {
+ m_pCode->EmitLDC(elemSize);
+ m_pCode->EmitMUL();
+ }
+ m_pCode->EmitADD();
+
+ LocalDesc elemType(pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType());
+
+ switch (m_pMD->GetArrayFuncIndex())
+ {
+
+ case ArrayMethodDesc::ARRAY_FUNC_GET:
+ if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
+ {
+ m_pCode->EmitLDOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
+ }
+ else
+ m_pCode->EmitLDIND_T(&elemType);
+ break;
+
+ case ArrayMethodDesc::ARRAY_FUNC_SET:
+ // Value to store into the array
+ m_pCode->EmitLDARG(rank);
+
+ if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
+ {
+ m_pCode->EmitSTOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
+ }
+ else
+ m_pCode->EmitSTIND_T(&elemType);
+ break;
+
+ case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
+ break;
+
+ default:
+ _ASSERTE(!"Unknown ArrayFuncIndex");
+ }
+
+ m_pCode->EmitRET();
+
+ m_pCode->EmitLDC(0);
+ m_pCode->EmitLabel(pRangeExceptionLabel1); // Assumes that there is one "int" pushed on the stack
+ m_pCode->EmitPOP();
+
+ mdToken tokIndexOutOfRangeCtorExcep = GetToken((MscorlibBinder::GetException(kIndexOutOfRangeException))->GetDefaultConstructor());
+ m_pCode->EmitLabel(pRangeExceptionLabel);
+ m_pCode->EmitNEWOBJ(tokIndexOutOfRangeCtorExcep, 0);
+ m_pCode->EmitTHROW();
+
+ if(pTypeMismatchExceptionLabel != NULL)
+ {
+ mdToken tokTypeMismatchExcepCtor = GetToken((MscorlibBinder::GetException(kArrayTypeMismatchException))->GetDefaultConstructor());
+
+ m_pCode->EmitLabel(pTypeMismatchExceptionLabel);
+ m_pCode->EmitNEWOBJ(tokTypeMismatchExcepCtor, 0);
+ m_pCode->EmitTHROW();
+ }
+ }
+};
+
+Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
+{
+ STANDARD_VM_CONTRACT;
+
+ ArrayOpLinker sl(pMD);
+
+ sl.EmitStub();
+
+ PCCOR_SIGNATURE pSig;
+ DWORD cbSig;
+ AllocMemTracker amTracker;
+
+ if (pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
+ {
+ // The stub has to have signature with explicit hidden argument instead of CORINFO_CALLCONV_PARAMTYPE.
+ // Generate a new signature for the stub here.
+ ((ArrayClass*)(pMD->GetMethodTable()->GetClass()))->GenerateArrayAccessorCallSig(pMD->GetMethodTable()->GetRank(),
+ ArrayMethodDesc::ARRAY_FUNC_ADDRESS,
+ &pSig,
+ &cbSig,
+ pMD->GetLoaderAllocator(),
+ &amTracker,
+ 1);
+ }
+ else
+ {
+ pMD->GetSig(&pSig,&cbSig);
+ }
+
+ amTracker.SuppressRelease();
+
+ static const ILStubTypes stubTypes[3] = { ILSTUB_ARRAYOP_GET, ILSTUB_ARRAYOP_SET, ILSTUB_ARRAYOP_ADDRESS };
+
+ _ASSERTE(pMD->GetArrayFuncIndex() <= COUNTOF(stubTypes));
+ NDirectStubFlags arrayOpStubFlag = (NDirectStubFlags)stubTypes[pMD->GetArrayFuncIndex()];
+
+ MethodDesc * pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc(pMD->GetLoaderAllocator(),
+ pMD->GetMethodTable(),
+ arrayOpStubFlag,
+ pMD->GetModule(),
+ pSig, cbSig,
+ NULL,
+ &sl);
+
+ return Stub::NewStub(JitILStub(pStubMD));
+}
+
+#else // FEATURE_ARRAYSTUB_AS_IL
+//========================================================================
+// Generates the platform-independent arrayop stub.
+//========================================================================
+void GenerateArrayOpScript(ArrayMethodDesc *pMD, ArrayOpScript *paos)
+{
+ STANDARD_VM_CONTRACT;
+
+ ArrayOpIndexSpec *pai = NULL;
+ MethodTable *pMT = pMD->GetMethodTable();
+ ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
+
+ // The ArrayOpScript and ArrayOpIndexSpec structs double as hash keys
+ // for the ArrayStubCache. Thus, it's imperative that there be no
+ // unused "pad" fields that contain unstable values.
+ // pMT->GetRank() is bounded so the arithmetics here is safe.
+ memset(paos, 0, sizeof(ArrayOpScript) + sizeof(ArrayOpIndexSpec) * pMT->GetRank());
+
+ paos->m_rank = (BYTE)(pMT->GetRank());
+ paos->m_fHasLowerBounds = (pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY);
+
+ paos->m_ofsoffirst = ArrayBase::GetDataPtrOffset(pMT);
+
+ switch (pMD->GetArrayFuncIndex())
+ {
+ case ArrayMethodDesc::ARRAY_FUNC_GET:
+ paos->m_op = ArrayOpScript::LOAD;
+ break;
+ case ArrayMethodDesc::ARRAY_FUNC_SET:
+ paos->m_op = ArrayOpScript::STORE;
+ break;
+ case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
+ paos->m_op = ArrayOpScript::LOADADDR;
+ break;
+ default:
+ _ASSERTE(!"Unknown array func!");
+ }
+
+ MetaSig msig(pMD);
+ _ASSERTE(!msig.IsVarArg()); // No array signature is varargs, code below does not expect it.
+
+ switch (pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType())
+ {
+ // These are all different because of sign extension
+
+ case ELEMENT_TYPE_I1:
+ paos->m_elemsize = 1;
+ paos->m_signed = TRUE;
+ break;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_U1:
+ paos->m_elemsize = 1;
+ break;
+
+ case ELEMENT_TYPE_I2:
+ paos->m_elemsize = 2;
+ paos->m_signed = TRUE;
+ break;
+
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_U2:
+ paos->m_elemsize = 2;
+ break;
+
+ case ELEMENT_TYPE_I4:
+ IN_WIN32(case ELEMENT_TYPE_I:)
+ paos->m_elemsize = 4;
+ paos->m_signed = TRUE;
+ break;
+
+ case ELEMENT_TYPE_U4:
+ IN_WIN32(case ELEMENT_TYPE_U:)
+ IN_WIN32(case ELEMENT_TYPE_PTR:)
+ paos->m_elemsize = 4;
+ break;
+
+ case ELEMENT_TYPE_I8:
+ IN_WIN64(case ELEMENT_TYPE_I:)
+ paos->m_elemsize = 8;
+ paos->m_signed = TRUE;
+ break;
+
+ case ELEMENT_TYPE_U8:
+ IN_WIN64(case ELEMENT_TYPE_U:)
+ IN_WIN64(case ELEMENT_TYPE_PTR:)
+ paos->m_elemsize = 8;
+ break;
+
+ case ELEMENT_TYPE_R4:
+ paos->m_elemsize = 4;
+ paos->m_flags |= paos->ISFPUTYPE;
+ break;
+
+ case ELEMENT_TYPE_R8:
+ paos->m_elemsize = 8;
+ paos->m_flags |= paos->ISFPUTYPE;
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_ARRAY:
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_OBJECT:
+ paos->m_elemsize = sizeof(LPVOID);
+ paos->m_flags |= paos->NEEDSWRITEBARRIER;
+ if (paos->m_op != ArrayOpScript::LOAD)
+ {
+ paos->m_flags |= paos->NEEDSTYPECHECK;
+ }
+
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ paos->m_elemsize = pMT->GetComponentSize();
+ if (pMT->ContainsPointers())
+ {
+ paos->m_gcDesc = CGCDesc::GetCGCDescFromMT(pMT);
+ paos->m_flags |= paos->NEEDSWRITEBARRIER;
+ }
+ break;
+
+ default:
+ _ASSERTE(!"Unsupported Array Type!");
+ }
+
+ ArgIterator argit(&msig);
+
+#ifdef _TARGET_X86_
+ paos->m_cbretpop = argit.CbStackPop();
+#endif
+
+ if (argit.HasRetBuffArg())
+ {
+ paos->m_flags |= ArrayOpScript::HASRETVALBUFFER;
+ paos->m_fRetBufLoc = argit.GetRetBuffArgOffset();
+ }
+
+ if (paos->m_op == ArrayOpScript::LOADADDR)
+ {
+ paos->m_typeParamOffs = argit.GetParamTypeArgOffset();
+ }
+
+ for (UINT idx = 0; idx < paos->m_rank; idx++)
+ {
+ pai = (ArrayOpIndexSpec*)(paos->GetArrayOpIndexSpecs() + idx);
+
+ pai->m_idxloc = argit.GetNextOffset();
+ pai->m_lboundofs = paos->m_fHasLowerBounds ? (UINT32) (ArrayBase::GetLowerBoundsOffset(pMT) + idx*sizeof(DWORD)) : 0;
+ pai->m_lengthofs = ArrayBase::GetBoundsOffset(pMT) + idx*sizeof(DWORD);
+ }
+
+ if (paos->m_op == paos->STORE)
+ {
+ paos->m_fValLoc = argit.GetNextOffset();
+ }
+}
+
+//---------------------------------------------------------
+// Cache for array stubs
+//---------------------------------------------------------
+class ArrayStubCache : public StubCacheBase
+{
+ virtual void CompileStub(const BYTE *pRawStub,
+ StubLinker *psl);
+ virtual UINT Length(const BYTE *pRawStub);
+
+public:
+ static ArrayStubCache * GetArrayStubCache()
+ {
+ STANDARD_VM_CONTRACT;
+
+ static ArrayStubCache * s_pArrayStubCache = NULL;
+
+ if (s_pArrayStubCache == NULL)
+ {
+ ArrayStubCache * pArrayStubCache = new ArrayStubCache();
+ if (FastInterlockCompareExchangePointer(&s_pArrayStubCache, pArrayStubCache, NULL) != NULL)
+ delete pArrayStubCache;
+ }
+
+ return s_pArrayStubCache;
+ }
+};
+
+Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
+{
+ STANDARD_VM_CONTRACT;
+
+ MethodTable *pMT = pMD->GetMethodTable();
+
+ ArrayOpScript *paos = (ArrayOpScript*)_alloca(sizeof(ArrayOpScript) + sizeof(ArrayOpIndexSpec) * pMT->GetRank());
+
+ GenerateArrayOpScript(pMD, paos);
+
+ Stub *pArrayOpStub;
+ pArrayOpStub = ArrayStubCache::GetArrayStubCache()->Canonicalize((const BYTE *)paos);
+ if (pArrayOpStub == NULL)
+ COMPlusThrowOM();
+
+ return pArrayOpStub;
+}
+
+void ArrayStubCache::CompileStub(const BYTE *pRawStub,
+ StubLinker *psl)
+{
+ STANDARD_VM_CONTRACT;
+
+ ((CPUSTUBLINKER*)psl)->EmitArrayOpStub((ArrayOpScript*)pRawStub);
+}
+
+UINT ArrayStubCache::Length(const BYTE *pRawStub)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ return ((ArrayOpScript*)pRawStub)->Length();
+}
+
+#endif // FEATURE_ARRAYSTUB_AS_IL
+
+#endif // CROSSGEN_COMPILE
+
+//---------------------------------------------------------------------
+// This method returns TRUE if pInterfaceMT could be one of the interfaces
+// that are implicitly implemented by SZArrays
+
+BOOL IsImplicitInterfaceOfSZArray(MethodTable *pInterfaceMT)
+{
+ LIMITED_METHOD_CONTRACT;
+ PRECONDITION(pInterfaceMT->IsInterface());
+
+ // Is target interface Anything<T> in mscorlib?
+ if (!pInterfaceMT->HasInstantiation() || !pInterfaceMT->GetModule()->IsSystem())
+ return FALSE;
+
+ unsigned rid = pInterfaceMT->GetTypeDefRid();
+
+ // Is target interface IList<T> or one of its ancestors, or IReadOnlyList<T>?
+ return (rid == MscorlibBinder::GetExistingClass(CLASS__ILISTGENERIC)->GetTypeDefRid() ||
+ rid == MscorlibBinder::GetExistingClass(CLASS__ICOLLECTIONGENERIC)->GetTypeDefRid() ||
+ rid == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)->GetTypeDefRid() ||
+ rid == MscorlibBinder::GetExistingClass(CLASS__IREADONLYCOLLECTIONGENERIC)->GetTypeDefRid() ||
+ rid == MscorlibBinder::GetExistingClass(CLASS__IREADONLYLISTGENERIC)->GetTypeDefRid());
+}
+
+//---------------------------------------------------------------------
+// Check if arrays supports certain interfaces that don't appear in the base interface
+// list. It does not check the base interfaces themselves - you must do that
+// separately.
+//---------------------------------------------------------------------
+BOOL ArraySupportsBizarreInterface(ArrayTypeDesc *pArrayTypeDesc, MethodTable *pInterfaceMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+
+ PRECONDITION(pInterfaceMT->IsInterface());
+ PRECONDITION(pArrayTypeDesc->IsArray());
+ }
+ CONTRACTL_END
+
+#ifdef _DEBUG
+ MethodTable *pArrayMT = pArrayTypeDesc->GetMethodTable();
+ _ASSERTE(pArrayMT->IsArray());
+ _ASSERTE(pArrayMT->IsRestored());
+#endif
+
+ // IList<T> & IReadOnlyList<T> only supported for SZ_ARRAYS
+ if (pArrayTypeDesc->GetInternalCorElementType() != ELEMENT_TYPE_SZARRAY)
+ return FALSE;
+
+ ClassLoader::EnsureLoaded(pInterfaceMT, CLASS_DEPENDENCIES_LOADED);
+
+ if (!IsImplicitInterfaceOfSZArray(pInterfaceMT))
+ return FALSE;
+
+ return TypeDesc::CanCastParam(pArrayTypeDesc->GetTypeParam(), pInterfaceMT->GetInstantiation()[0], NULL);
+}
+
+//----------------------------------------------------------------------------------
+// Calls to (IList<T>)(array).Meth are actually implemented by SZArrayHelper.Meth<T>
+// This workaround exists for two reasons:
+//
+// - For working set reasons, we don't want insert these methods in the array hierachy
+// in the normal way.
+// - For platform and devtime reasons, we still want to use the C# compiler to generate
+// the method bodies.
+//
+// (Though it's questionable whether any devtime was saved.)
+//
+// This method takes care of the mapping between the two. Give it a method
+// IList<T>.Meth, and it will return SZArrayHelper.Meth<T>.
+//----------------------------------------------------------------------------------
+MethodDesc* GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod(MethodDesc *pItfcMeth, TypeHandle theT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END
+
+ int slot = pItfcMeth->GetSlot();
+
+ // We need to pick the right starting method depending on the depth of the inheritance chain
+ static const BinderMethodID startingMethod[] = {
+ METHOD__SZARRAYHELPER__GETENUMERATOR, // First method of IEnumerable`1
+ METHOD__SZARRAYHELPER__GET_COUNT, // First method of ICollection`1/IReadOnlyCollection`1
+ METHOD__SZARRAYHELPER__GET_ITEM // First method of IList`1/IReadOnlyList`1
+ };
+
+ // Subtract one for the non-generic IEnumerable that the generic enumerable inherits from
+ unsigned int inheritanceDepth = pItfcMeth->GetMethodTable()->GetNumInterfaces() - 1;
+ PREFIX_ASSUME(0 <= inheritanceDepth && inheritanceDepth < NumItems(startingMethod));
+
+ MethodDesc *pGenericImplementor = MscorlibBinder::GetMethod((BinderMethodID)(startingMethod[inheritanceDepth] + slot));
+
+ // The most common reason for this assert is that the order of the SZArrayHelper methods in
+ // mscorlib.h does not match the order they are implemented on the generic interfaces.
+ _ASSERTE(pGenericImplementor == MemberLoader::FindMethodByName(g_pSZArrayHelperClass, pItfcMeth->GetName()));
+
+ // OPTIMIZATION: For any method other than GetEnumerator(), we can safely substitute
+ // "Object" for reference-type theT's. This causes fewer methods to be instantiated.
+ if (startingMethod[inheritanceDepth] != METHOD__SZARRAYHELPER__GETENUMERATOR &&
+ !theT.IsValueType())
+ {
+ theT = TypeHandle(g_pObjectClass);
+ }
+
+ MethodDesc *pActualImplementor = MethodDesc::FindOrCreateAssociatedMethodDesc(pGenericImplementor,
+ g_pSZArrayHelperClass,
+ FALSE,
+ Instantiation(&theT, 1),
+ FALSE // allowInstParam
+ );
+ _ASSERTE(pActualImplementor);
+ return pActualImplementor;
+}
+#endif // DACCESS_COMPILE
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#pragma warning(disable:4244)
+#endif // _MSC_VER: warning C4244