1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
8 // File which contains a bunch of of array related things.
13 #include "clsload.hpp"
20 #include "siginfo.hpp"
23 #include "stubcache.h"
24 #include "dllimport.h"
26 #include "jitinterface.h"
29 #include "fieldmarshaler.h"
32 #include "typestring.h"
33 #include "sigbuilder.h"
35 #define MAX_SIZE_FOR_VALUECLASS_IN_ARRAY 0xffff
36 #define MAX_PTRS_FOR_VALUECLASSS_IN_ARRAY 0xffff
38 /*****************************************************************************************/
39 LPCUTF8 ArrayMethodDesc::GetMethodName()
41 LIMITED_METHOD_DAC_CONTRACT;
43 switch (GetArrayFuncIndex())
49 case ARRAY_FUNC_ADDRESS:
52 return COR_CTOR_METHOD_NAME; // ".ctor"
56 /*****************************************************************************************/
57 DWORD ArrayMethodDesc::GetAttrs()
59 LIMITED_METHOD_CONTRACT;
60 return (GetArrayFuncIndex() >= ARRAY_FUNC_CTOR) ? (mdPublic | mdRTSpecialName) : mdPublic;
63 /*****************************************************************************************/
64 CorInfoIntrinsics ArrayMethodDesc::GetIntrinsicID()
66 LIMITED_METHOD_CONTRACT;
68 switch (GetArrayFuncIndex())
71 return CORINFO_INTRINSIC_Array_Get;
73 return CORINFO_INTRINSIC_Array_Set;
74 case ARRAY_FUNC_ADDRESS:
75 return CORINFO_INTRINSIC_Array_Address;
77 return CORINFO_INTRINSIC_Illegal;
81 #ifndef DACCESS_COMPILE
83 /*****************************************************************************************/
86 // Generate a short sig (descr) for an array accessors
89 VOID ArrayClass::GenerateArrayAccessorCallSig(
91 DWORD dwFuncType, // Load, store, or <init>
92 PCCOR_SIGNATURE *ppSig,// Generated signature
93 DWORD * pcSig, // Generated signature size
94 LoaderAllocator *pLoaderAllocator,
95 AllocMemTracker *pamTracker
96 #ifdef FEATURE_ARRAYSTUB_AS_IL
103 PRECONDITION(dwRank >= 1 && dwRank < 0x3ffff);
107 PCOR_SIGNATURE pSigMemory;
108 DWORD dwCallSigSize = dwRank;
109 DWORD dwArgCount = (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET) ? dwRank+1 : dwRank;
114 // <callconv> <argcount> VAR 0 I4 , ... , I4
115 case ArrayMethodDesc::ARRAY_FUNC_GET:
119 // <callconv> <argcount> VOID I4 , ... , I4
120 case ArrayMethodDesc::ARRAY_FUNC_CTOR:
124 // <callconv> <argcount> VOID I4 , ... , I4 VAR 0
125 case ArrayMethodDesc::ARRAY_FUNC_SET:
129 // <callconv> <argcount> BYREF VAR 0 I4 , ... , I4
130 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
132 #ifdef FEATURE_ARRAYSTUB_AS_IL
133 if(fForStubAsIL) {dwArgCount++; dwCallSigSize++;}
138 // If the argument count is larger than 127 then it will require 2 bytes for the encoding
139 if (dwArgCount > 0x7f)
142 pSigMemory = (PCOR_SIGNATURE)pamTracker->Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(dwCallSigSize)));
145 BYTE callConv = IMAGE_CEE_CS_CALLCONV_DEFAULT + IMAGE_CEE_CS_CALLCONV_HASTHIS;
147 if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS
148 #ifdef FEATURE_ARRAYSTUB_AS_IL
153 callConv |= CORINFO_CALLCONV_PARAMTYPE; // Address routine needs special hidden arg
157 pSig += CorSigCompressData(dwArgCount, pSig); // Argument count
160 case ArrayMethodDesc::ARRAY_FUNC_GET:
161 *pSig++ = ELEMENT_TYPE_VAR;
162 *pSig++ = 0; // variable 0
164 case ArrayMethodDesc::ARRAY_FUNC_CTOR:
165 *pSig++ = (BYTE) ELEMENT_TYPE_VOID; // Return type
167 case ArrayMethodDesc::ARRAY_FUNC_SET:
168 *pSig++ = (BYTE) ELEMENT_TYPE_VOID; // Return type
170 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
171 *pSig++ = (BYTE) ELEMENT_TYPE_BYREF; // Return type
172 *pSig++ = ELEMENT_TYPE_VAR;
173 *pSig++ = 0; // variable 0
177 #if defined(FEATURE_ARRAYSTUB_AS_IL ) && !defined(_TARGET_X86_)
178 if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
180 *pSig++ = ELEMENT_TYPE_I;
184 for (i = 0; i < dwRank; i++)
185 *pSig++ = ELEMENT_TYPE_I4;
187 if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET)
189 *pSig++ = ELEMENT_TYPE_VAR;
190 *pSig++ = 0; // variable 0
192 #if defined(FEATURE_ARRAYSTUB_AS_IL ) && defined(_TARGET_X86_)
193 else if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
195 *pSig++ = ELEMENT_TYPE_I;
199 // Make sure the sig came out exactly as large as we expected
200 _ASSERTE(pSig == pSigMemory + dwCallSigSize);
203 *pcSig = (DWORD)(pSig-pSigMemory);
207 // Allocate a new MethodDesc for a fake array method.
209 // Based on code in class.cpp.
211 void ArrayClass::InitArrayMethodDesc(
212 ArrayMethodDesc *pNewMD,
213 PCCOR_SIGNATURE pShortSig,
216 LoaderAllocator *pLoaderAllocator,
217 AllocMemTracker *pamTracker)
219 STANDARD_VM_CONTRACT;
221 // Note: The method desc memory is zero initialized
223 pNewMD->SetMemberDef(0);
225 pNewMD->SetSlot((WORD) dwVtableSlot);
226 pNewMD->SetStoredMethodSig(pShortSig, cShortSig);
228 _ASSERTE(!pNewMD->MayHaveNativeCode());
229 pNewMD->SetTemporaryEntryPoint(pLoaderAllocator, pamTracker);
232 _ASSERTE(pNewMD->GetMethodName() && GetDebugClassName());
233 pNewMD->m_pszDebugMethodName = pNewMD->GetMethodName();
234 pNewMD->m_pszDebugClassName = GetDebugClassName();
235 pNewMD->m_pDebugMethodTable.SetValue(pNewMD->GetMethodTable());
239 /*****************************************************************************************/
240 MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementType arrayKind, unsigned Rank, AllocMemTracker *pamTracker)
244 PRECONDITION(Rank > 0);
247 MethodTable * pElemMT = elemTypeHnd.GetMethodTable();
249 CorElementType elemType = elemTypeHnd.GetSignatureCorElementType();
251 // Shared EEClass if there is one
252 MethodTable * pCanonMT = NULL;
254 // Strictly speaking no method table should be needed for
255 // arrays of the faked up TypeDescs for variable types that are
256 // used when verfifying generic code.
257 // However verification is tied in with some codegen in the JITs, so give these
258 // the shared MT just in case.
259 // This checks match precisely one in ParamTypeDesc::OwnsMethodTable
260 if (CorTypeInfo::IsGenericVariable(elemType)) {
261 // This is loading the canonical version of the array so we can override
262 OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
263 return(ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass), arrayKind, Rank).GetMethodTable());
266 // Arrays of reference types all share the same EEClass.
268 // We can't share nested SZARRAYs because they have different
269 // numbers of constructors.
271 // Unfortunately, we cannot share more because of it would affect user visible System.RuntimeMethodHandle behavior
272 if (CorTypeInfo::IsObjRef(elemType) && elemType != ELEMENT_TYPE_SZARRAY && pElemMT != g_pObjectClass)
274 // This is loading the canonical version of the array so we can override
275 OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
276 pCanonMT = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass), arrayKind, Rank).GetMethodTable();
279 BOOL containsPointers = CorTypeInfo::IsObjRef(elemType);
280 if (elemType == ELEMENT_TYPE_VALUETYPE && pElemMT->ContainsPointers())
281 containsPointers = TRUE;
283 // this is the base for every array type
284 MethodTable *pParentClass = g_pArrayClass;
285 _ASSERTE(pParentClass); // Must have already loaded the System.Array class
286 _ASSERTE(pParentClass->IsFullyLoaded());
288 DWORD numCtors = 2; // ELEMENT_TYPE_ARRAY has two ctor functions, one with and one without lower bounds
289 if (arrayKind == ELEMENT_TYPE_SZARRAY)
292 TypeHandle ptr = elemTypeHnd;
293 while (ptr.IsTypeDesc() && ptr.AsTypeDesc()->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY) {
295 ptr = ptr.AsTypeDesc()->GetTypeParam();
299 /****************************************************************************************/
301 // Parent class is the top level array
302 // The vtable will have all of top level class's methods, plus any methods we have for array classes
303 DWORD numVirtuals = pParentClass->GetNumVirtuals();
304 DWORD numNonVirtualSlots = numCtors + 3; // 3 for the proper rank Get, Set, Address
306 size_t cbMT = sizeof(MethodTable);
307 cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(MethodTable::VTableIndir_t);
310 size_t cbCGCDescData = 0;
311 if (containsPointers)
313 cbCGCDescData += CGCDesc::ComputeSize(1);
314 if (elemType == ELEMENT_TYPE_VALUETYPE)
316 size_t nSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
317 cbCGCDescData += (nSeries - 1)*sizeof (val_serie_item);
318 _ASSERTE(cbCGCDescData == CGCDesc::ComputeSizeRepeating(nSeries));
321 #ifdef FEATURE_COLLECTIBLE_TYPES
322 else if (this->IsCollectible())
324 cbCGCDescData = (DWORD)CGCDesc::ComputeSize(1);
328 DWORD dwMultipurposeSlotsMask = 0;
329 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasPerInstInfo;
330 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasInterfaceMap;
331 if (pCanonMT == NULL)
332 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasNonVirtualSlots;
333 if (this != elemTypeHnd.GetModule())
334 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasModuleOverride;
336 // Allocate space for optional members
337 // We always have a non-virtual slot array, see assert at end
338 cbMT += MethodTable::GetOptionalMembersAllocationSize(dwMultipurposeSlotsMask,
339 FALSE, // GenericsStaticsInfo
341 FALSE, // CCWTemplate
342 FALSE, // RCWPerTypeData
343 FALSE); // TokenOverflow
345 // This is the offset of the beginning of the interface map
346 size_t imapOffset = cbMT;
348 // This is added after we determine the offset of the interface maps
349 // because the memory appears before the pointer to the method table
350 cbMT += cbCGCDescData;
352 // Inherit top level class's interface map
353 cbMT += pParentClass->GetNumInterfaces() * sizeof(InterfaceInfo_t);
355 #ifdef FEATURE_PREJIT
356 Module* pComputedPZM = Module::ComputePreferredZapModule(NULL, Instantiation(&elemTypeHnd, 1));
357 BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this, pComputedPZM);
359 BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this);
360 #endif // FEATURE_PREJIT
362 size_t offsetOfUnsharedVtableChunks = cbMT;
364 // We either share all of the parent's virtual slots or none of them
365 // If none, we need to allocate space for the slots
366 if (!canShareVtableChunks)
368 cbMT += numVirtuals * sizeof(MethodTable::VTableIndir2_t);
371 // Canonical methodtable has an array of non virtual slots pointed to by the optional member
372 size_t offsetOfNonVirtualSlots = 0;
373 size_t cbArrayClass = 0;
375 if (pCanonMT == NULL)
377 offsetOfNonVirtualSlots = cbMT;
378 cbMT += numNonVirtualSlots * sizeof(PCODE);
380 // Allocate ArrayClass (including space for packed fields), MethodTable, and class name in one alloc.
381 // Remember to pad allocation size for ArrayClass portion to ensure MethodTable is pointer aligned.
382 cbArrayClass = ALIGN_UP(sizeof(ArrayClass) + sizeof(EEClassPackedFields), sizeof(void*));
385 // ArrayClass already includes one void*
386 LoaderAllocator* pAllocator= this->GetLoaderAllocator();
387 BYTE* pMemory = (BYTE *)pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbArrayClass) +
390 // Note: Memory allocated on loader heap is zero filled
391 // memset(pMemory, 0, sizeof(ArrayClass) + cbMT);
393 ArrayClass* pClass = NULL;
395 if (pCanonMT == NULL)
397 pClass = ::new (pMemory) ArrayClass();
400 // Head of MethodTable memory (starts after ArrayClass), this points at the GCDesc stuff in front
401 // of a method table (if needed)
402 BYTE* pMTHead = pMemory + cbArrayClass + cbCGCDescData;
404 MethodTable* pMT = (MethodTable *) pMTHead;
406 pMT->SetMultipurposeSlotsMask(dwMultipurposeSlotsMask);
408 // Allocate the private data block ("private" during runtime in the ngen'ed case).
409 MethodTableWriteableData * pMTWriteableData = (MethodTableWriteableData *) (BYTE *)
410 pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(MethodTableWriteableData))));
411 pMT->SetWriteableData(pMTWriteableData);
413 // This also disables IBC logging until the type is sufficiently intitialized so
414 // it needs to be done early
415 pMTWriteableData->SetIsNotFullyLoadedForBuildMethodTable();
420 pClass->SetInternalCorElementType(arrayKind);
421 pClass->SetAttrClass (tdPublic | tdSerializable | tdSealed); // This class is public, serializable, sealed
422 pClass->SetRank (Rank);
423 pClass->SetArrayElementType (elemType);
424 pClass->SetMethodTable (pMT);
426 // Fill In the method table
427 pClass->SetNumMethods(static_cast<WORD>(numVirtuals + numNonVirtualSlots));
429 pClass->SetNumNonVirtualSlots(static_cast<WORD>(numNonVirtualSlots));
432 pMT->SetNumVirtuals(static_cast<WORD>(numVirtuals));
434 pMT->SetParentMethodTable(pParentClass);
436 DWORD dwComponentSize = elemTypeHnd.GetSize();
438 if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
440 // The only way for dwComponentSize to be large is to be part of a value class. If this changes
441 // then the check will need to be moved outside valueclass check.
442 if(dwComponentSize > MAX_SIZE_FOR_VALUECLASS_IN_ARRAY) {
443 StackSString ssElemName;
444 elemTypeHnd.GetName(ssElemName);
446 StackScratchBuffer scratch;
447 elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch), IDS_CLASSLOAD_VALUECLASSTOOLARGE);
453 pMT->SetClass(pClass);
457 pMT->SetCanonicalMethodTable(pCanonMT);
460 pMT->SetIsArray(arrayKind, elemType);
462 pMT->SetApproxArrayElementTypeHandle(elemTypeHnd);
464 _ASSERTE(FitsIn<WORD>(dwComponentSize));
465 pMT->SetComponentSize(static_cast<WORD>(dwComponentSize));
467 pMT->SetLoaderModule(this);
468 pMT->SetLoaderAllocator(pAllocator);
470 pMT->SetModule(elemTypeHnd.GetModule());
472 if (elemTypeHnd.ContainsGenericVariables())
473 pMT->SetContainsGenericVariables();
475 #ifdef FEATURE_TYPEEQUIVALENCE
476 if (elemTypeHnd.HasTypeEquivalence())
478 // propagate the type equivalence flag
479 pMT->SetHasTypeEquivalence();
481 #endif // FEATURE_TYPEEQUIVALENCE
483 _ASSERTE(pMT->IsClassPreInited());
485 // Set BaseSize to be size of non-data portion of the array
486 DWORD baseSize = ARRAYBASE_BASESIZE;
487 if (arrayKind == ELEMENT_TYPE_ARRAY)
488 baseSize += Rank*sizeof(DWORD)*2;
490 #if !defined(_TARGET_64BIT_) && (DATA_ALIGNMENT > 4)
491 if (dwComponentSize >= DATA_ALIGNMENT)
492 baseSize = (DWORD)ALIGN_UP(baseSize, DATA_ALIGNMENT);
493 #endif // !defined(_TARGET_64BIT_) && (DATA_ALIGNMENT > 4)
494 pMT->SetBaseSize(baseSize);
495 // Because of array method table persisting, we need to copy the map
496 for (unsigned index = 0; index < pParentClass->GetNumInterfaces(); ++index)
498 InterfaceInfo_t *pIntInfo = (InterfaceInfo_t *) (pMTHead + imapOffset + index * sizeof(InterfaceInfo_t));
499 pIntInfo->SetMethodTable((pParentClass->GetInterfaceMap() + index)->GetMethodTable());
501 pMT->SetInterfaceMap(pParentClass->GetNumInterfaces(), (InterfaceInfo_t *)(pMTHead + imapOffset));
503 // Copy down flags for these interfaces as well. This is simplified a bit since we know that System.Array
504 // only has a few interfaces and the flags will fit inline into the MethodTable's optional members.
505 _ASSERTE(MethodTable::GetExtraInterfaceInfoSize(pParentClass->GetNumInterfaces()) == 0);
506 pMT->InitializeExtraInterfaceInfo(NULL);
508 for (UINT32 i = 0; i < pParentClass->GetNumInterfaces(); i++)
510 if (pParentClass->IsInterfaceDeclaredOnClass(i))
511 pMT->SetInterfaceDeclaredOnClass(i);
514 // The type is sufficiently initialized for most general purpose accessor methods to work.
515 // Mark the type as restored to avoid asserts. Note that this also enables IBC logging.
516 pMTWriteableData->SetIsFullyLoadedForBuildMethodTable();
519 // Fill out the vtable indirection slots
520 MethodTable::VtableIndirectionSlotIterator it = pMT->IterateVtableIndirectionSlots();
523 if (canShareVtableChunks)
525 // Share the parent chunk
526 it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
530 // Use the locally allocated chunk
531 it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(pMemory+cbArrayClass+offsetOfUnsharedVtableChunks));
532 offsetOfUnsharedVtableChunks += it.GetSize();
536 // If we are not sharing parent chunks, copy down the slot contents
537 if (!canShareVtableChunks)
539 // Copy top level class's vtable - note, vtable is contained within the MethodTable
540 MethodTable::MethodDataWrapper hParentMTData(MethodTable::GetMethodData(pParentClass, FALSE));
541 for (UINT32 i = 0; i < numVirtuals; i++)
543 pMT->CopySlotFrom(i, hParentMTData, pParentClass);
548 pMT->SetNonVirtualSlotsArray((PTR_PCODE)(pMemory+cbArrayClass+offsetOfNonVirtualSlots));
552 StackSString debugName;
553 TypeString::AppendType(debugName, TypeHandle(pMT));
554 StackScratchBuffer buff;
555 const char* pDebugNameUTF8 = debugName.GetUTF8(buff);
556 S_SIZE_T safeLen = S_SIZE_T(strlen(pDebugNameUTF8))+S_SIZE_T(1);
557 if(safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
558 size_t len = safeLen.Value();
559 char * name = (char*) pamTracker->Track(pAllocator->
560 GetHighFrequencyHeap()->
562 strcpy_s(name, len, pDebugNameUTF8);
565 pClass->SetDebugClassName(name);
566 pMT->SetDebugClassName(name);
571 // Count the number of method descs we need so we can allocate chunks.
572 DWORD dwMethodDescs = numCtors
573 + 3; // for rank specific Get, Set, Address
575 MethodDescChunk * pChunks = MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
576 dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */, FALSE /* fComPlusCallInfo */,
578 pClass->SetChunks(pChunks);
580 MethodTable::IntroducedMethodIterator it(pMT);
582 DWORD dwMethodIndex = 0;
583 for (; it.IsValid(); it.Next())
585 ArrayMethodDesc* pNewMD = (ArrayMethodDesc *) it.GetMethodDesc();
586 _ASSERTE(pNewMD->GetClassification() == mcArray);
591 if (dwMethodIndex < ArrayMethodDesc::ARRAY_FUNC_CTOR)
593 // Generate a new stand-alone, Rank Specific Get, Set and Address method.
595 dwFuncType = dwMethodIndex;
599 if (arrayKind == ELEMENT_TYPE_SZARRAY)
601 // For SZARRAY arrays, set up multiple constructors.
602 dwFuncRank = 1 + (dwMethodIndex - ArrayMethodDesc::ARRAY_FUNC_CTOR);
606 // ELEMENT_TYPE_ARRAY has two constructors, one without lower bounds and one with lower bounds
607 _ASSERTE((dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR) || (dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR+1));
608 dwFuncRank = (dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR) ? Rank : 2 * Rank;
610 dwFuncType = ArrayMethodDesc::ARRAY_FUNC_CTOR;
613 PCCOR_SIGNATURE pSig;
616 pClass->GenerateArrayAccessorCallSig(dwFuncRank, dwFuncType, &pSig, &cSig, pAllocator, pamTracker
617 #ifdef FEATURE_ARRAYSTUB_AS_IL
622 pClass->InitArrayMethodDesc(pNewMD, pSig, cSig, numVirtuals + dwMethodIndex, pAllocator, pamTracker);
626 _ASSERTE(dwMethodIndex == dwMethodDescs);
629 // Set up GC information
630 if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
632 // If it's an array of value classes, there is a different format for the GCDesc if it contains pointers
633 if (pElemMT->ContainsPointers())
635 CGCDescSeries *pSeries;
637 // There must be only one series for value classes
638 CGCDescSeries *pByValueSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetHighestSeries();
640 pMT->SetContainsPointers();
642 // negative series has a special meaning, indicating a different form of GCDesc
643 SSIZE_T nSeries = (SSIZE_T) CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
644 CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, nSeries);
646 pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
649 SSIZE_T AllocSizeSeries;
650 if (!ClrSafeInt<SSIZE_T>::multiply(sizeof(CGCDescSeries*), nSeries, AllocSizeSeries))
652 CGCDescSeries** sortedSeries = (CGCDescSeries**) _alloca(AllocSizeSeries);
654 for (index = 0; index < nSeries; index++)
655 sortedSeries[index] = &pByValueSeries[-index];
658 for (int i = 0; i < nSeries; i++) {
659 for (int j = i+1; j < nSeries; j++)
660 if (sortedSeries[j]->GetSeriesOffset() < sortedSeries[i]->GetSeriesOffset())
662 CGCDescSeries* temp = sortedSeries[i];
663 sortedSeries[i] = sortedSeries[j];
664 sortedSeries[j] = temp;
668 // Offset of the first pointer in the array
669 // This equals the offset of the first pointer if this were an array of entirely pointers, plus the offset of the
670 // first pointer in the value class
671 pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT)
672 + (sortedSeries[0]->GetSeriesOffset()) - OBJECT_SIZE);
673 for (index = 0; index < nSeries; index ++)
675 size_t numPtrsInBytes = sortedSeries[index]->GetSeriesSize()
676 + pElemMT->GetBaseSize();
677 size_t currentOffset;
679 currentOffset = sortedSeries[index]->GetSeriesOffset()+numPtrsInBytes;
680 if (index != nSeries-1)
682 skip = sortedSeries[index+1]->GetSeriesOffset()-currentOffset;
686 skip = pElemMT->GetAlignedNumInstanceFieldBytes() - numPtrsInBytes;
690 skip = sortedSeries[0]->GetSeriesOffset() + pElemMT->GetBaseSize()
691 - OBJECT_BASESIZE - currentOffset;
694 _ASSERTE(!"Module::CreateArrayMethodTable() - unaligned GC info" || IS_ALIGNED(skip, TARGET_POINTER_SIZE));
696 unsigned short NumPtrs = (unsigned short) (numPtrsInBytes / TARGET_POINTER_SIZE);
697 if(skip > MAX_SIZE_FOR_VALUECLASS_IN_ARRAY || numPtrsInBytes > MAX_PTRS_FOR_VALUECLASSS_IN_ARRAY) {
698 StackSString ssElemName;
699 elemTypeHnd.GetName(ssElemName);
701 StackScratchBuffer scratch;
702 elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch),
703 IDS_CLASSLOAD_VALUECLASSTOOLARGE);
706 val_serie_item *val_item = &(pSeries->val_serie[-index]);
708 val_item->set_val_serie_item (NumPtrs, (unsigned short)skip);
712 else if (CorTypeInfo::IsObjRef(elemType))
714 CGCDescSeries *pSeries;
716 pMT->SetContainsPointers();
718 // This array is all GC Pointers
719 CGCDesc::GetCGCDescFromMT(pMT)->Init( pMT, 1 );
721 pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
723 pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT));
724 // For arrays, the size is the negative of the BaseSize (the GC always adds the total
725 // size of the object, so what you end up with is the size of the data portion of the array)
726 pSeries->SetSeriesSize(-(SSIZE_T)(pMT->GetBaseSize()));
729 #ifdef FEATURE_COLLECTIBLE_TYPES
730 if (!pMT->ContainsPointers() && this->IsCollectible())
732 CGCDescSeries *pSeries;
734 // For collectible types, insert empty gc series
735 CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, 1);
736 pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
737 pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT));
738 pSeries->val_serie[0].set_val_serie_item (0, static_cast<HALF_SIZE_T>(pMT->GetComponentSize()));
742 // If we get here we are assuming that there was no truncation. If this is not the case then
743 // an array whose base type is not a value class was created and was larger then 0xffff (a word)
744 _ASSERTE(dwComponentSize == pMT->GetComponentSize());
746 #ifdef FEATURE_PREJIT
747 _ASSERTE(pComputedPZM == Module::GetPreferredZapModuleForMethodTable(pMT));
751 } // Module::CreateArrayMethodTable
753 #ifndef CROSSGEN_COMPILE
755 #ifdef FEATURE_ARRAYSTUB_AS_IL
757 class ArrayOpLinker : public ILStubLinker
759 ILCodeStream * m_pCode;
760 ArrayMethodDesc * m_pMD;
762 SigTypeContext m_emptyContext;
765 ArrayOpLinker(ArrayMethodDesc * pMD)
766 : ILStubLinker(pMD->GetModule(), pMD->GetSignature(), &m_emptyContext, pMD, TRUE, TRUE, FALSE)
768 m_pCode = NewCodeStream(kDispatch);
774 MethodTable *pMT = m_pMD->GetMethodTable();
775 BOOL fHasLowerBounds = pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY;
777 DWORD dwTotalLocalNum = NewLocal(ELEMENT_TYPE_I4);
778 DWORD dwLengthLocalNum = NewLocal(ELEMENT_TYPE_I4);
780 mdToken tokRawData = GetToken(MscorlibBinder::GetField(FIELD__RAW_DATA__DATA));
782 ILCodeLabel * pRangeExceptionLabel = NewCodeLabel();
783 ILCodeLabel * pRangeExceptionLabel1 = NewCodeLabel();
784 ILCodeLabel * pCheckDone = NewCodeLabel();
785 ILCodeLabel * pNotSZArray = NewCodeLabel();
786 ILCodeLabel * pTypeMismatchExceptionLabel = NULL;
788 UINT rank = pMT->GetRank();
790 UINT hiddenArgIdx = rank;
794 if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
801 ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
802 if(pcls->GetArrayElementType() == ELEMENT_TYPE_CLASS)
805 if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_SET)
807 ILCodeLabel * pTypeCheckOK = NewCodeLabel();
809 m_pCode->EmitLDARG(rank); // load value to store
810 m_pCode->EmitBRFALSE(pTypeCheckOK); //Storing NULL is OK
812 m_pCode->EmitLDARG(rank); // return param
813 m_pCode->EmitLDFLDA(tokRawData);
814 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
816 m_pCode->EmitLDIND_I(); // TypeHandle
818 m_pCode->EmitLoadThis();
819 m_pCode->EmitLDFLDA(tokRawData);
820 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
822 m_pCode->EmitLDIND_I(); // Array MT
823 m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
825 m_pCode->EmitLDIND_I();
828 m_pCode->EmitBRTRUE(pTypeCheckOK); // Same type is OK
830 // Call type check helper
831 m_pCode->EmitLDARG(rank);
832 m_pCode->EmitLoadThis();
833 m_pCode->EmitCALL(METHOD__STUBHELPERS__ARRAY_TYPE_CHECK,2,0);
835 m_pCode->EmitLabel(pTypeCheckOK);
838 else if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
840 // Check that the hidden param is same type
841 ILCodeLabel *pTypeCheckPassed = NewCodeLabel();
842 pTypeMismatchExceptionLabel = NewCodeLabel();
844 m_pCode->EmitLDARG(hiddenArgIdx); // hidden param
845 m_pCode->EmitBRFALSE(pTypeCheckPassed);
846 m_pCode->EmitLDARG(hiddenArgIdx);
847 m_pCode->EmitLDFLDA(tokRawData);
848 m_pCode->EmitLDC(offsetof(ParamTypeDesc, m_Arg) - (Object::GetOffsetOfFirstField()+2));
850 m_pCode->EmitLDIND_I();
852 m_pCode->EmitLoadThis();
853 m_pCode->EmitLDFLDA(tokRawData);
854 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
856 m_pCode->EmitLDIND_I(); // Array MT
857 m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
859 m_pCode->EmitLDIND_I();
862 m_pCode->EmitBRFALSE(pTypeMismatchExceptionLabel); // throw exception if not same
863 m_pCode->EmitLabel(pTypeCheckPassed);
867 if(rank == 1 && fHasLowerBounds)
869 // check if the array is SZArray.
870 m_pCode->EmitLoadThis();
871 m_pCode->EmitLDFLDA(tokRawData);
872 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
874 m_pCode->EmitLDIND_I();
875 m_pCode->EmitLDC(MethodTable::GetOffsetOfFlags());
877 m_pCode->EmitLDIND_I4();
878 m_pCode->EmitLDC(MethodTable::GetIfArrayThenSzArrayFlag());
880 m_pCode->EmitBRFALSE(pNotSZArray); // goto multi-dimmArray code if not szarray
884 m_pCode->EmitLoadThis();
885 m_pCode->EmitLDFLDA(tokRawData);
886 m_pCode->EmitLDC(ArrayBase::GetOffsetOfNumComponents() - Object::GetOffsetOfFirstField());
888 m_pCode->EmitLDIND_I4();
889 m_pCode->EmitLDARG(firstIdx);
890 m_pCode->EmitBLE_UN(pRangeExceptionLabel);
892 m_pCode->EmitLoadThis();
893 m_pCode->EmitLDFLDA(tokRawData);
894 m_pCode->EmitLDC(ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField());
896 m_pCode->EmitLDARG(firstIdx);
897 m_pCode->EmitBR(pCheckDone);
898 m_pCode->EmitLabel(pNotSZArray);
901 for (UINT i = 0; i < rank; i++)
904 m_pCode->EmitLoadThis();
905 m_pCode->EmitLDFLDA(tokRawData);
906 m_pCode->EmitLDC((ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + i*sizeof(DWORD));
908 m_pCode->EmitLDIND_I4();
909 m_pCode->EmitSTLOC(dwLengthLocalNum);
912 m_pCode->EmitLDARG(firstIdx + i);
917 m_pCode->EmitLoadThis();
918 m_pCode->EmitLDFLDA(tokRawData);
919 m_pCode->EmitLDC((ArrayBase::GetLowerBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + i*sizeof(DWORD));
921 m_pCode->EmitLDIND_I4();
923 // Subtract lower bound
927 // Compare with length
929 m_pCode->EmitLDLOC(dwLengthLocalNum);
930 m_pCode->EmitBGE_UN(pRangeExceptionLabel1);
932 // Add to the running total if we have one already
935 m_pCode->EmitLDLOC(dwTotalLocalNum);
936 m_pCode->EmitLDLOC(dwLengthLocalNum);
940 m_pCode->EmitSTLOC(dwTotalLocalNum);
943 // Compute element address
944 m_pCode->EmitLoadThis();
945 m_pCode->EmitLDFLDA(tokRawData);
946 m_pCode->EmitLDC(ArrayBase::GetDataPtrOffset(pMT) - Object::GetOffsetOfFirstField());
948 m_pCode->EmitLDLOC(dwTotalLocalNum);
950 m_pCode->EmitLabel(pCheckDone);
952 m_pCode->EmitCONV_U();
954 SIZE_T elemSize = pMT->GetComponentSize();
957 m_pCode->EmitLDC(elemSize);
962 LocalDesc elemType(pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType());
964 switch (m_pMD->GetArrayFuncIndex())
967 case ArrayMethodDesc::ARRAY_FUNC_GET:
968 if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
970 m_pCode->EmitLDOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
973 m_pCode->EmitLDIND_T(&elemType);
976 case ArrayMethodDesc::ARRAY_FUNC_SET:
977 // Value to store into the array
978 m_pCode->EmitLDARG(rank);
980 if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
982 m_pCode->EmitSTOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
985 m_pCode->EmitSTIND_T(&elemType);
988 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
992 _ASSERTE(!"Unknown ArrayFuncIndex");
998 m_pCode->EmitLabel(pRangeExceptionLabel1); // Assumes that there is one "int" pushed on the stack
1001 mdToken tokIndexOutOfRangeCtorExcep = GetToken((MscorlibBinder::GetException(kIndexOutOfRangeException))->GetDefaultConstructor());
1002 m_pCode->EmitLabel(pRangeExceptionLabel);
1003 m_pCode->EmitNEWOBJ(tokIndexOutOfRangeCtorExcep, 0);
1004 m_pCode->EmitTHROW();
1006 if(pTypeMismatchExceptionLabel != NULL)
1008 mdToken tokTypeMismatchExcepCtor = GetToken((MscorlibBinder::GetException(kArrayTypeMismatchException))->GetDefaultConstructor());
1010 m_pCode->EmitLabel(pTypeMismatchExceptionLabel);
1011 m_pCode->EmitNEWOBJ(tokTypeMismatchExcepCtor, 0);
1012 m_pCode->EmitTHROW();
1017 Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
1019 STANDARD_VM_CONTRACT;
1021 ArrayOpLinker sl(pMD);
1025 PCCOR_SIGNATURE pSig;
1027 AllocMemTracker amTracker;
1029 if (pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
1031 // The stub has to have signature with explicit hidden argument instead of CORINFO_CALLCONV_PARAMTYPE.
1032 // Generate a new signature for the stub here.
1033 ((ArrayClass*)(pMD->GetMethodTable()->GetClass()))->GenerateArrayAccessorCallSig(pMD->GetMethodTable()->GetRank(),
1034 ArrayMethodDesc::ARRAY_FUNC_ADDRESS,
1037 pMD->GetLoaderAllocator(),
1043 pMD->GetSig(&pSig,&cbSig);
1046 amTracker.SuppressRelease();
1048 static const ILStubTypes stubTypes[3] = { ILSTUB_ARRAYOP_GET, ILSTUB_ARRAYOP_SET, ILSTUB_ARRAYOP_ADDRESS };
1050 _ASSERTE(pMD->GetArrayFuncIndex() <= COUNTOF(stubTypes));
1051 NDirectStubFlags arrayOpStubFlag = (NDirectStubFlags)stubTypes[pMD->GetArrayFuncIndex()];
1053 MethodDesc * pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc(pMD->GetLoaderAllocator(),
1054 pMD->GetMethodTable(),
1061 return Stub::NewStub(JitILStub(pStubMD));
1064 #else // FEATURE_ARRAYSTUB_AS_IL
1065 //========================================================================
1066 // Generates the platform-independent arrayop stub.
1067 //========================================================================
1068 void GenerateArrayOpScript(ArrayMethodDesc *pMD, ArrayOpScript *paos)
1070 STANDARD_VM_CONTRACT;
1072 ArrayOpIndexSpec *pai = NULL;
1073 MethodTable *pMT = pMD->GetMethodTable();
1074 ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
1076 // The ArrayOpScript and ArrayOpIndexSpec structs double as hash keys
1077 // for the ArrayStubCache. Thus, it's imperative that there be no
1078 // unused "pad" fields that contain unstable values.
1079 // pMT->GetRank() is bounded so the arithmetics here is safe.
1080 memset(paos, 0, sizeof(ArrayOpScript) + sizeof(ArrayOpIndexSpec) * pMT->GetRank());
1082 paos->m_rank = (BYTE)(pMT->GetRank());
1083 paos->m_fHasLowerBounds = (pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY);
1085 paos->m_ofsoffirst = ArrayBase::GetDataPtrOffset(pMT);
1087 switch (pMD->GetArrayFuncIndex())
1089 case ArrayMethodDesc::ARRAY_FUNC_GET:
1090 paos->m_op = ArrayOpScript::LOAD;
1092 case ArrayMethodDesc::ARRAY_FUNC_SET:
1093 paos->m_op = ArrayOpScript::STORE;
1095 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
1096 paos->m_op = ArrayOpScript::LOADADDR;
1099 _ASSERTE(!"Unknown array func!");
1103 _ASSERTE(!msig.IsVarArg()); // No array signature is varargs, code below does not expect it.
1105 switch (pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType())
1107 // These are all different because of sign extension
1109 case ELEMENT_TYPE_I1:
1110 paos->m_elemsize = 1;
1111 paos->m_signed = TRUE;
1114 case ELEMENT_TYPE_BOOLEAN:
1115 case ELEMENT_TYPE_U1:
1116 paos->m_elemsize = 1;
1119 case ELEMENT_TYPE_I2:
1120 paos->m_elemsize = 2;
1121 paos->m_signed = TRUE;
1124 case ELEMENT_TYPE_CHAR:
1125 case ELEMENT_TYPE_U2:
1126 paos->m_elemsize = 2;
1129 case ELEMENT_TYPE_I4:
1130 IN_WIN32(case ELEMENT_TYPE_I:)
1131 paos->m_elemsize = 4;
1132 paos->m_signed = TRUE;
1135 case ELEMENT_TYPE_U4:
1136 IN_WIN32(case ELEMENT_TYPE_U:)
1137 IN_WIN32(case ELEMENT_TYPE_PTR:)
1138 paos->m_elemsize = 4;
1141 case ELEMENT_TYPE_I8:
1142 IN_WIN64(case ELEMENT_TYPE_I:)
1143 paos->m_elemsize = 8;
1144 paos->m_signed = TRUE;
1147 case ELEMENT_TYPE_U8:
1148 IN_WIN64(case ELEMENT_TYPE_U:)
1149 IN_WIN64(case ELEMENT_TYPE_PTR:)
1150 paos->m_elemsize = 8;
1153 case ELEMENT_TYPE_R4:
1154 paos->m_elemsize = 4;
1155 paos->m_flags |= paos->ISFPUTYPE;
1158 case ELEMENT_TYPE_R8:
1159 paos->m_elemsize = 8;
1160 paos->m_flags |= paos->ISFPUTYPE;
1163 case ELEMENT_TYPE_SZARRAY:
1164 case ELEMENT_TYPE_ARRAY:
1165 case ELEMENT_TYPE_CLASS:
1166 case ELEMENT_TYPE_STRING:
1167 case ELEMENT_TYPE_OBJECT:
1168 paos->m_elemsize = sizeof(LPVOID);
1169 paos->m_flags |= paos->NEEDSWRITEBARRIER;
1170 if (paos->m_op != ArrayOpScript::LOAD)
1172 paos->m_flags |= paos->NEEDSTYPECHECK;
1177 case ELEMENT_TYPE_VALUETYPE:
1178 paos->m_elemsize = pMT->GetComponentSize();
1179 if (pMT->ContainsPointers())
1181 paos->m_gcDesc = CGCDesc::GetCGCDescFromMT(pMT);
1182 paos->m_flags |= paos->NEEDSWRITEBARRIER;
1187 _ASSERTE(!"Unsupported Array Type!");
1190 ArgIterator argit(&msig);
1193 paos->m_cbretpop = argit.CbStackPop();
1196 if (argit.HasRetBuffArg())
1198 paos->m_flags |= ArrayOpScript::HASRETVALBUFFER;
1199 paos->m_fRetBufLoc = argit.GetRetBuffArgOffset();
1202 if (paos->m_op == ArrayOpScript::LOADADDR)
1204 paos->m_typeParamOffs = argit.GetParamTypeArgOffset();
1207 for (UINT idx = 0; idx < paos->m_rank; idx++)
1209 pai = (ArrayOpIndexSpec*)(paos->GetArrayOpIndexSpecs() + idx);
1211 pai->m_idxloc = argit.GetNextOffset();
1212 pai->m_lboundofs = paos->m_fHasLowerBounds ? (UINT32) (ArrayBase::GetLowerBoundsOffset(pMT) + idx*sizeof(DWORD)) : 0;
1213 pai->m_lengthofs = ArrayBase::GetBoundsOffset(pMT) + idx*sizeof(DWORD);
1216 if (paos->m_op == paos->STORE)
1218 paos->m_fValLoc = argit.GetNextOffset();
1222 //---------------------------------------------------------
1223 // Cache for array stubs
1224 //---------------------------------------------------------
1225 class ArrayStubCache : public StubCacheBase
1227 virtual void CompileStub(const BYTE *pRawStub,
1229 virtual UINT Length(const BYTE *pRawStub);
1232 static ArrayStubCache * GetArrayStubCache()
1234 STANDARD_VM_CONTRACT;
1236 static ArrayStubCache * s_pArrayStubCache = NULL;
1238 if (s_pArrayStubCache == NULL)
1240 ArrayStubCache * pArrayStubCache = new ArrayStubCache();
1241 if (FastInterlockCompareExchangePointer(&s_pArrayStubCache, pArrayStubCache, NULL) != NULL)
1242 delete pArrayStubCache;
1245 return s_pArrayStubCache;
1249 Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
1251 STANDARD_VM_CONTRACT;
1253 MethodTable *pMT = pMD->GetMethodTable();
1255 ArrayOpScript *paos = (ArrayOpScript*)_alloca(sizeof(ArrayOpScript) + sizeof(ArrayOpIndexSpec) * pMT->GetRank());
1257 GenerateArrayOpScript(pMD, paos);
1260 pArrayOpStub = ArrayStubCache::GetArrayStubCache()->Canonicalize((const BYTE *)paos);
1261 if (pArrayOpStub == NULL)
1264 return pArrayOpStub;
1267 void ArrayStubCache::CompileStub(const BYTE *pRawStub,
1270 STANDARD_VM_CONTRACT;
1272 ((CPUSTUBLINKER*)psl)->EmitArrayOpStub((ArrayOpScript*)pRawStub);
1275 UINT ArrayStubCache::Length(const BYTE *pRawStub)
1277 LIMITED_METHOD_CONTRACT;
1278 return ((ArrayOpScript*)pRawStub)->Length();
1281 #endif // FEATURE_ARRAYSTUB_AS_IL
1283 #endif // CROSSGEN_COMPILE
1285 //---------------------------------------------------------------------
1286 // This method returns TRUE if pInterfaceMT could be one of the interfaces
1287 // that are implicitly implemented by SZArrays
1289 BOOL IsImplicitInterfaceOfSZArray(MethodTable *pInterfaceMT)
1291 LIMITED_METHOD_CONTRACT;
1292 PRECONDITION(pInterfaceMT->IsInterface());
1294 // Is target interface Anything<T> in mscorlib?
1295 if (!pInterfaceMT->HasInstantiation() || !pInterfaceMT->GetModule()->IsSystem())
1298 unsigned rid = pInterfaceMT->GetTypeDefRid();
1300 // Is target interface IList<T> or one of its ancestors, or IReadOnlyList<T>?
1301 return (rid == MscorlibBinder::GetExistingClass(CLASS__ILISTGENERIC)->GetTypeDefRid() ||
1302 rid == MscorlibBinder::GetExistingClass(CLASS__ICOLLECTIONGENERIC)->GetTypeDefRid() ||
1303 rid == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)->GetTypeDefRid() ||
1304 rid == MscorlibBinder::GetExistingClass(CLASS__IREADONLYCOLLECTIONGENERIC)->GetTypeDefRid() ||
1305 rid == MscorlibBinder::GetExistingClass(CLASS__IREADONLYLISTGENERIC)->GetTypeDefRid());
1308 //---------------------------------------------------------------------
1309 // Check if arrays supports certain interfaces that don't appear in the base interface
1310 // list. It does not check the base interfaces themselves - you must do that
1312 //---------------------------------------------------------------------
1313 BOOL ArraySupportsBizarreInterface(ArrayTypeDesc *pArrayTypeDesc, MethodTable *pInterfaceMT)
1319 INJECT_FAULT(COMPlusThrowOM(););
1321 PRECONDITION(pInterfaceMT->IsInterface());
1322 PRECONDITION(pArrayTypeDesc->IsArray());
1327 MethodTable *pArrayMT = pArrayTypeDesc->GetMethodTable();
1328 _ASSERTE(pArrayMT->IsArray());
1329 _ASSERTE(pArrayMT->IsRestored());
1332 // IList<T> & IReadOnlyList<T> only supported for SZ_ARRAYS
1333 if (pArrayTypeDesc->GetInternalCorElementType() != ELEMENT_TYPE_SZARRAY)
1336 ClassLoader::EnsureLoaded(pInterfaceMT, CLASS_DEPENDENCIES_LOADED);
1338 if (!IsImplicitInterfaceOfSZArray(pInterfaceMT))
1341 return TypeDesc::CanCastParam(pArrayTypeDesc->GetTypeParam(), pInterfaceMT->GetInstantiation()[0], NULL);
1344 //----------------------------------------------------------------------------------
1345 // Calls to (IList<T>)(array).Meth are actually implemented by SZArrayHelper.Meth<T>
1346 // This workaround exists for two reasons:
1348 // - For working set reasons, we don't want insert these methods in the array hierachy
1349 // in the normal way.
1350 // - For platform and devtime reasons, we still want to use the C# compiler to generate
1351 // the method bodies.
1353 // (Though it's questionable whether any devtime was saved.)
1355 // This method takes care of the mapping between the two. Give it a method
1356 // IList<T>.Meth, and it will return SZArrayHelper.Meth<T>.
1357 //----------------------------------------------------------------------------------
1358 MethodDesc* GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod(MethodDesc *pItfcMeth, TypeHandle theT)
1364 INJECT_FAULT(COMPlusThrowOM());
1368 int slot = pItfcMeth->GetSlot();
1370 // We need to pick the right starting method depending on the depth of the inheritance chain
1371 static const BinderMethodID startingMethod[] = {
1372 METHOD__SZARRAYHELPER__GETENUMERATOR, // First method of IEnumerable`1
1373 METHOD__SZARRAYHELPER__GET_COUNT, // First method of ICollection`1/IReadOnlyCollection`1
1374 METHOD__SZARRAYHELPER__GET_ITEM // First method of IList`1/IReadOnlyList`1
1377 // Subtract one for the non-generic IEnumerable that the generic enumerable inherits from
1378 unsigned int inheritanceDepth = pItfcMeth->GetMethodTable()->GetNumInterfaces() - 1;
1379 PREFIX_ASSUME(0 <= inheritanceDepth && inheritanceDepth < NumItems(startingMethod));
1381 MethodDesc *pGenericImplementor = MscorlibBinder::GetMethod((BinderMethodID)(startingMethod[inheritanceDepth] + slot));
1383 // The most common reason for this assert is that the order of the SZArrayHelper methods in
1384 // mscorlib.h does not match the order they are implemented on the generic interfaces.
1385 _ASSERTE(pGenericImplementor == MemberLoader::FindMethodByName(g_pSZArrayHelperClass, pItfcMeth->GetName()));
1387 // OPTIMIZATION: For any method other than GetEnumerator(), we can safely substitute
1388 // "Object" for reference-type theT's. This causes fewer methods to be instantiated.
1389 if (startingMethod[inheritanceDepth] != METHOD__SZARRAYHELPER__GETENUMERATOR &&
1390 !theT.IsValueType())
1392 theT = TypeHandle(g_pObjectClass);
1395 MethodDesc *pActualImplementor = MethodDesc::FindOrCreateAssociatedMethodDesc(pGenericImplementor,
1396 g_pSZArrayHelperClass,
1398 Instantiation(&theT, 1),
1399 FALSE // allowInstParam
1401 _ASSERTE(pActualImplementor);
1402 return pActualImplementor;
1404 #endif // DACCESS_COMPILE
1406 CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType)
1408 LIMITED_METHOD_CONTRACT;
1410 _ASSERTE(CorTypeInfo::IsPrimitiveType_NoThrow(elementType));
1412 // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable
1413 // Enums with interchangeable underlying types are interchangable
1414 // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2
1416 switch (elementType)
1418 case ELEMENT_TYPE_U1:
1419 case ELEMENT_TYPE_U2:
1420 case ELEMENT_TYPE_U4:
1421 case ELEMENT_TYPE_U8:
1422 case ELEMENT_TYPE_U:
1423 return (CorElementType)(elementType - 1); // normalize to signed type