Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1431 lines (1178 sloc) 52 KB
// 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(MethodTable::VTableIndir_t);
// 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(MethodTable::VTableIndir2_t);
}
// 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);
// 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 = ARRAYBASE_BASESIZE;
if (arrayKind == ELEMENT_TYPE_ARRAY)
baseSize += Rank*sizeof(DWORD)*2;
#if !defined(_TARGET_64BIT_) && (DATA_ALIGNMENT > 4)
if (dwComponentSize >= DATA_ALIGNMENT)
baseSize = (DWORD)ALIGN_UP(baseSize, DATA_ALIGNMENT);
#endif // !defined(_TARGET_64BIT_) && (DATA_ALIGNMENT > 4)
pMT->SetBaseSize(baseSize);
// Because of array method table persisting, we need to copy the map
for (unsigned index = 0; index < pParentClass->GetNumInterfaces(); ++index)
{
InterfaceInfo_t *pIntInfo = (InterfaceInfo_t *) (pMTHead + imapOffset + index * sizeof(InterfaceInfo_t));
pIntInfo->SetMethodTable((pParentClass->GetInterfaceMap() + index)->GetMethodTable());
}
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()].GetValueMaybeNull());
}
else
{
// Use the locally allocated chunk
it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(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()) - OBJECT_SIZE);
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()
- OBJECT_BASESIZE - currentOffset;
}
_ASSERTE(!"Module::CreateArrayMethodTable() - unaligned GC info" || IS_ALIGNED(skip, TARGET_POINTER_SIZE));
unsigned short NumPtrs = (unsigned short) (numPtrsInBytes / TARGET_POINTER_SIZE);
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