[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / vm / array.cpp
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.
4 // File: ARRAY.CPP
5 //
6
7 //
8 // File which contains a bunch of of array related things.
9 //
10
11 #include "common.h"
12
13 #include "clsload.hpp"
14 #include "method.hpp"
15 #include "class.h"
16 #include "object.h"
17 #include "field.h"
18 #include "util.hpp"
19 #include "excep.h"
20 #include "siginfo.hpp"
21 #include "threads.h"
22 #include "stublink.h"
23 #include "stubcache.h"
24 #include "dllimport.h"
25 #include "gcdesc.h"
26 #include "jitinterface.h"
27 #include "eeconfig.h"
28 #include "log.h"
29 #include "fieldmarshaler.h"
30 #include "cgensys.h"
31 #include "array.h"
32 #include "typestring.h"
33 #include "sigbuilder.h"
34
35 #define MAX_SIZE_FOR_VALUECLASS_IN_ARRAY 0xffff
36 #define MAX_PTRS_FOR_VALUECLASSS_IN_ARRAY 0xffff
37
38 /*****************************************************************************************/
39 LPCUTF8 ArrayMethodDesc::GetMethodName()
40 {
41     LIMITED_METHOD_DAC_CONTRACT;
42
43     switch (GetArrayFuncIndex())
44     {
45     case ARRAY_FUNC_GET:
46         return "Get";
47     case ARRAY_FUNC_SET:
48         return "Set";
49     case ARRAY_FUNC_ADDRESS:
50         return "Address";
51     default:
52         return COR_CTOR_METHOD_NAME;    // ".ctor"
53     }
54 }
55
56 /*****************************************************************************************/
57 DWORD ArrayMethodDesc::GetAttrs()
58 {
59     LIMITED_METHOD_CONTRACT;
60     return (GetArrayFuncIndex() >= ARRAY_FUNC_CTOR) ? (mdPublic | mdRTSpecialName) : mdPublic;
61 }
62
63 /*****************************************************************************************/
64 CorInfoIntrinsics ArrayMethodDesc::GetIntrinsicID()
65 {
66     LIMITED_METHOD_CONTRACT;
67
68     switch (GetArrayFuncIndex())
69     {
70     case ARRAY_FUNC_GET:
71         return CORINFO_INTRINSIC_Array_Get;
72     case ARRAY_FUNC_SET:
73         return CORINFO_INTRINSIC_Array_Set;
74     case ARRAY_FUNC_ADDRESS:
75         return CORINFO_INTRINSIC_Array_Address;
76     default:
77         return CORINFO_INTRINSIC_Illegal;
78     }
79 }
80
81 #ifndef DACCESS_COMPILE
82
83 /*****************************************************************************************/
84
85 //
86 // Generate a short sig (descr) for an array accessors
87 //
88
89 VOID ArrayClass::GenerateArrayAccessorCallSig(
90     DWORD   dwRank,
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
97     ,BOOL fForStubAsIL
98 #endif
99     ) 
100 {
101     CONTRACTL {
102         STANDARD_VM_CHECK;
103         PRECONDITION(dwRank >= 1 && dwRank < 0x3ffff);
104     } CONTRACTL_END;
105
106     PCOR_SIGNATURE pSig;
107     PCOR_SIGNATURE pSigMemory;
108     DWORD   dwCallSigSize = dwRank;
109     DWORD   dwArgCount = (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET) ? dwRank+1 : dwRank;
110     DWORD   i;
111
112     switch (dwFuncType)
113     {
114         // <callconv> <argcount> VAR 0 I4 , ... , I4
115         case ArrayMethodDesc::ARRAY_FUNC_GET:
116             dwCallSigSize += 4;
117             break;
118
119         // <callconv> <argcount> VOID I4 , ... , I4
120         case ArrayMethodDesc::ARRAY_FUNC_CTOR:
121             dwCallSigSize += 3;
122             break;
123
124         // <callconv> <argcount> VOID I4 , ... , I4 VAR 0
125         case ArrayMethodDesc::ARRAY_FUNC_SET:
126             dwCallSigSize += 5;
127             break;
128
129         // <callconv> <argcount> BYREF VAR 0 I4 , ... , I4
130         case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
131             dwCallSigSize += 5;
132 #ifdef FEATURE_ARRAYSTUB_AS_IL
133             if(fForStubAsIL) {dwArgCount++; dwCallSigSize++;}
134 #endif
135             break;
136     }
137
138     // If the argument count is larger than 127 then it will require 2 bytes for the encoding
139     if (dwArgCount > 0x7f)
140         dwCallSigSize++;
141
142     pSigMemory = (PCOR_SIGNATURE)pamTracker->Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(dwCallSigSize)));
143
144     pSig = pSigMemory;
145     BYTE callConv = IMAGE_CEE_CS_CALLCONV_DEFAULT + IMAGE_CEE_CS_CALLCONV_HASTHIS;
146
147     if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS
148 #ifdef FEATURE_ARRAYSTUB_AS_IL
149         && !fForStubAsIL
150 #endif
151            )
152     {
153         callConv |= CORINFO_CALLCONV_PARAMTYPE;     // Address routine needs special hidden arg
154     }
155
156     *pSig++ = callConv;
157     pSig += CorSigCompressData(dwArgCount, pSig);   // Argument count
158     switch (dwFuncType)
159     {
160         case ArrayMethodDesc::ARRAY_FUNC_GET:
161             *pSig++ = ELEMENT_TYPE_VAR;
162             *pSig++ = 0;        // variable 0
163             break;
164         case ArrayMethodDesc::ARRAY_FUNC_CTOR:
165             *pSig++ = (BYTE) ELEMENT_TYPE_VOID;             // Return type
166             break;
167         case ArrayMethodDesc::ARRAY_FUNC_SET:
168             *pSig++ = (BYTE) ELEMENT_TYPE_VOID;             // Return type
169             break;
170         case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
171             *pSig++ = (BYTE) ELEMENT_TYPE_BYREF;            // Return type
172             *pSig++ = ELEMENT_TYPE_VAR;
173             *pSig++ = 0;        // variable 0
174             break;
175     }
176         
177 #if defined(FEATURE_ARRAYSTUB_AS_IL ) && !defined(_TARGET_X86_)
178     if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
179     {
180         *pSig++ = ELEMENT_TYPE_I;
181     }
182 #endif
183         
184     for (i = 0; i < dwRank; i++)
185         *pSig++ = ELEMENT_TYPE_I4;
186
187     if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET)
188     {
189         *pSig++ = ELEMENT_TYPE_VAR;
190         *pSig++ = 0;        // variable 0
191     }
192 #if defined(FEATURE_ARRAYSTUB_AS_IL ) && defined(_TARGET_X86_)
193     else if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
194     {
195         *pSig++ = ELEMENT_TYPE_I;
196     }
197 #endif
198
199     // Make sure the sig came out exactly as large as we expected
200     _ASSERTE(pSig == pSigMemory + dwCallSigSize);
201
202     *ppSig = pSigMemory;
203     *pcSig = (DWORD)(pSig-pSigMemory);
204 }
205
206 //
207 // Allocate a new MethodDesc for a fake array method.
208 //
209 // Based on code in class.cpp.
210 //
211 void ArrayClass::InitArrayMethodDesc(
212     ArrayMethodDesc *pNewMD,
213     PCCOR_SIGNATURE pShortSig,
214     DWORD   cShortSig,
215     DWORD   dwVtableSlot,
216     LoaderAllocator *pLoaderAllocator,
217     AllocMemTracker *pamTracker)
218 {
219     STANDARD_VM_CONTRACT;
220
221     // Note: The method desc memory is zero initialized
222
223     pNewMD->SetMemberDef(0);
224
225     pNewMD->SetSlot((WORD) dwVtableSlot);
226     pNewMD->SetStoredMethodSig(pShortSig, cShortSig);
227
228     _ASSERTE(!pNewMD->MayHaveNativeCode());
229     pNewMD->SetTemporaryEntryPoint(pLoaderAllocator, pamTracker);
230
231 #ifdef _DEBUG 
232     _ASSERTE(pNewMD->GetMethodName() && GetDebugClassName());
233     pNewMD->m_pszDebugMethodName = pNewMD->GetMethodName();
234     pNewMD->m_pszDebugClassName  = GetDebugClassName();
235     pNewMD->m_pDebugMethodTable.SetValue(pNewMD->GetMethodTable());
236 #endif // _DEBUG
237 }
238
239 /*****************************************************************************************/
240 MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementType arrayKind, unsigned Rank, AllocMemTracker *pamTracker)
241 {
242     CONTRACTL {
243         STANDARD_VM_CHECK;
244         PRECONDITION(Rank > 0);
245     } CONTRACTL_END;
246
247     MethodTable * pElemMT = elemTypeHnd.GetMethodTable();
248
249     CorElementType elemType = elemTypeHnd.GetSignatureCorElementType();    
250
251     // Shared EEClass if there is one
252     MethodTable * pCanonMT = NULL;
253
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());
264     }
265
266     // Arrays of reference types all share the same EEClass.
267     //
268     // We can't share nested SZARRAYs because they have different
269     // numbers of constructors.
270     //
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)
273     {
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();
277     }
278
279     BOOL            containsPointers = CorTypeInfo::IsObjRef(elemType);
280     if (elemType == ELEMENT_TYPE_VALUETYPE && pElemMT->ContainsPointers())
281         containsPointers = TRUE;
282
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());
287
288     DWORD numCtors = 2;         // ELEMENT_TYPE_ARRAY has two ctor functions, one with and one without lower bounds
289     if (arrayKind == ELEMENT_TYPE_SZARRAY)
290     {
291         numCtors = 1;
292         TypeHandle ptr = elemTypeHnd;
293         while (ptr.IsTypeDesc() && ptr.AsTypeDesc()->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY) {
294             numCtors++;
295             ptr = ptr.AsTypeDesc()->GetTypeParam();
296         }
297     }
298
299     /****************************************************************************************/
300
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
305
306     size_t cbMT = sizeof(MethodTable);
307     cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(MethodTable::VTableIndir_t);
308
309     // GC info
310     size_t cbCGCDescData = 0;
311     if (containsPointers)
312     {
313         cbCGCDescData += CGCDesc::ComputeSize(1);
314         if (elemType == ELEMENT_TYPE_VALUETYPE)
315         {
316             size_t nSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
317             cbCGCDescData += (nSeries - 1)*sizeof (val_serie_item);
318             _ASSERTE(cbCGCDescData == CGCDesc::ComputeSizeRepeating(nSeries));
319         }
320     }
321 #ifdef FEATURE_COLLECTIBLE_TYPES
322     else if (this->IsCollectible())
323     {
324         cbCGCDescData = (DWORD)CGCDesc::ComputeSize(1);
325     }
326 #endif
327
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;
335
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
340                                                           FALSE,                           // GuidInfo
341                                                           FALSE,                           // CCWTemplate
342                                                           FALSE,                           // RCWPerTypeData
343                                                           FALSE);                          // TokenOverflow
344
345     // This is the offset of the beginning of the interface map
346     size_t imapOffset = cbMT;
347
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;
351
352     // Inherit top level class's interface map
353     cbMT += pParentClass->GetNumInterfaces() * sizeof(InterfaceInfo_t);
354
355 #ifdef FEATURE_PREJIT
356     Module* pComputedPZM = Module::ComputePreferredZapModule(NULL, Instantiation(&elemTypeHnd, 1));
357     BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this, pComputedPZM);
358 #else
359     BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this);
360 #endif // FEATURE_PREJIT
361
362     size_t offsetOfUnsharedVtableChunks = cbMT;
363
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)
367     {
368         cbMT += numVirtuals * sizeof(MethodTable::VTableIndir2_t);
369     }
370
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;
374
375     if (pCanonMT == NULL)
376     {
377         offsetOfNonVirtualSlots = cbMT;
378         cbMT += numNonVirtualSlots * sizeof(PCODE);
379
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*));
383     }
384
385     // ArrayClass already includes one void*
386     LoaderAllocator* pAllocator= this->GetLoaderAllocator(); 
387     BYTE* pMemory = (BYTE *)pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbArrayClass) +
388                                                                                             S_SIZE_T(cbMT)));
389
390     // Note: Memory allocated on loader heap is zero filled
391     // memset(pMemory, 0, sizeof(ArrayClass) + cbMT);
392
393     ArrayClass* pClass = NULL;
394
395     if (pCanonMT == NULL)
396     {
397         pClass = ::new (pMemory) ArrayClass();
398     }
399
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;
403
404     MethodTable* pMT = (MethodTable *) pMTHead;
405
406     pMT->SetMultipurposeSlotsMask(dwMultipurposeSlotsMask);
407
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);
412
413     // This also disables IBC logging until the type is sufficiently intitialized so
414     // it needs to be done early
415     pMTWriteableData->SetIsNotFullyLoadedForBuildMethodTable();
416
417     // Fill in pClass
418     if (pClass != NULL)
419     {
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);
425
426         // Fill In the method table
427         pClass->SetNumMethods(static_cast<WORD>(numVirtuals + numNonVirtualSlots));
428
429         pClass->SetNumNonVirtualSlots(static_cast<WORD>(numNonVirtualSlots));
430     }
431
432     pMT->SetNumVirtuals(static_cast<WORD>(numVirtuals));
433
434     pMT->SetParentMethodTable(pParentClass);
435
436     DWORD dwComponentSize = elemTypeHnd.GetSize();
437
438     if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
439     {
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);
445
446             StackScratchBuffer scratch;
447             elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch), IDS_CLASSLOAD_VALUECLASSTOOLARGE);
448         }
449     }
450
451     if (pClass != NULL)
452     {
453         pMT->SetClass(pClass);
454     }
455     else
456     {
457         pMT->SetCanonicalMethodTable(pCanonMT);
458     }
459
460     pMT->SetIsArray(arrayKind, elemType);
461
462     pMT->SetApproxArrayElementTypeHandle(elemTypeHnd);
463
464     _ASSERTE(FitsIn<WORD>(dwComponentSize));
465     pMT->SetComponentSize(static_cast<WORD>(dwComponentSize));
466
467     pMT->SetLoaderModule(this);
468     pMT->SetLoaderAllocator(pAllocator);
469
470     pMT->SetModule(elemTypeHnd.GetModule());
471
472     if (elemTypeHnd.ContainsGenericVariables())
473         pMT->SetContainsGenericVariables();
474
475 #ifdef FEATURE_TYPEEQUIVALENCE
476     if (elemTypeHnd.HasTypeEquivalence())
477     {
478         // propagate the type equivalence flag
479         pMT->SetHasTypeEquivalence();
480     }
481 #endif // FEATURE_TYPEEQUIVALENCE
482
483     _ASSERTE(pMT->IsClassPreInited());
484
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;
489
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)
497     {
498       InterfaceInfo_t *pIntInfo = (InterfaceInfo_t *) (pMTHead + imapOffset + index * sizeof(InterfaceInfo_t));
499       pIntInfo->SetMethodTable((pParentClass->GetInterfaceMap() + index)->GetMethodTable());
500     }
501     pMT->SetInterfaceMap(pParentClass->GetNumInterfaces(), (InterfaceInfo_t *)(pMTHead + imapOffset));
502
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);
507
508     for (UINT32 i = 0; i < pParentClass->GetNumInterfaces(); i++)
509     {
510         if (pParentClass->IsInterfaceDeclaredOnClass(i))
511             pMT->SetInterfaceDeclaredOnClass(i);
512     }
513
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();
517
518     {
519         // Fill out the vtable indirection slots
520         MethodTable::VtableIndirectionSlotIterator it = pMT->IterateVtableIndirectionSlots();
521         while (it.Next())
522         {
523             if (canShareVtableChunks)
524             {
525                 // Share the parent chunk
526                 it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
527             }
528             else 
529             {
530                 // Use the locally allocated chunk
531                 it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(pMemory+cbArrayClass+offsetOfUnsharedVtableChunks));
532                 offsetOfUnsharedVtableChunks += it.GetSize();
533             }
534         }
535
536         // If we are not sharing parent chunks, copy down the slot contents
537         if (!canShareVtableChunks)
538         {
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++)
542             {
543                 pMT->CopySlotFrom(i, hParentMTData, pParentClass);
544             }
545         }
546
547         if (pClass != NULL)
548             pMT->SetNonVirtualSlotsArray((PTR_PCODE)(pMemory+cbArrayClass+offsetOfNonVirtualSlots));
549     }
550
551 #ifdef _DEBUG 
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()->
561                                 AllocMem(safeLen));
562     strcpy_s(name, len, pDebugNameUTF8);
563
564     if (pClass != NULL)
565         pClass->SetDebugClassName(name);
566     pMT->SetDebugClassName(name);
567 #endif // _DEBUG
568
569     if (pClass != NULL)
570     {
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
574
575         MethodDescChunk * pChunks = MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
576                             dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */, FALSE /* fComPlusCallInfo */,
577                             pMT, pamTracker);
578         pClass->SetChunks(pChunks);
579
580         MethodTable::IntroducedMethodIterator it(pMT);
581
582         DWORD dwMethodIndex = 0;
583         for (; it.IsValid(); it.Next())
584         {
585             ArrayMethodDesc* pNewMD = (ArrayMethodDesc *) it.GetMethodDesc();
586             _ASSERTE(pNewMD->GetClassification() == mcArray);
587
588             DWORD dwFuncRank;
589             DWORD dwFuncType;
590
591             if (dwMethodIndex < ArrayMethodDesc::ARRAY_FUNC_CTOR)
592             {
593                 // Generate a new stand-alone, Rank Specific Get, Set and Address method.
594                 dwFuncRank = Rank;
595                 dwFuncType = dwMethodIndex;
596             }
597             else
598             {
599                 if (arrayKind == ELEMENT_TYPE_SZARRAY)
600                 {
601                     // For SZARRAY arrays, set up multiple constructors.
602                     dwFuncRank = 1 + (dwMethodIndex - ArrayMethodDesc::ARRAY_FUNC_CTOR);
603                 }
604                 else
605                 {
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;
609                 }
610                 dwFuncType = ArrayMethodDesc::ARRAY_FUNC_CTOR;
611             }
612
613             PCCOR_SIGNATURE pSig;
614             DWORD           cSig;
615
616             pClass->GenerateArrayAccessorCallSig(dwFuncRank, dwFuncType, &pSig, &cSig, pAllocator, pamTracker
617     #ifdef FEATURE_ARRAYSTUB_AS_IL
618                                                  ,0
619     #endif
620                                                 );
621
622             pClass->InitArrayMethodDesc(pNewMD, pSig, cSig, numVirtuals + dwMethodIndex, pAllocator, pamTracker);
623
624             dwMethodIndex++;
625         }
626         _ASSERTE(dwMethodIndex == dwMethodDescs);
627     }
628
629     // Set up GC information
630     if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
631     {
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())
634         {
635             CGCDescSeries  *pSeries;
636
637             // There must be only one series for value classes
638             CGCDescSeries  *pByValueSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetHighestSeries();
639
640             pMT->SetContainsPointers();
641
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);
645
646             pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
647
648             // sort by offset
649             SSIZE_T AllocSizeSeries;
650             if (!ClrSafeInt<SSIZE_T>::multiply(sizeof(CGCDescSeries*), nSeries, AllocSizeSeries))
651                 COMPlusThrowOM();
652             CGCDescSeries** sortedSeries = (CGCDescSeries**) _alloca(AllocSizeSeries);
653             int index;
654             for (index = 0; index < nSeries; index++)
655                 sortedSeries[index] = &pByValueSeries[-index];
656
657             // section sort
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())
661                     {
662                         CGCDescSeries* temp = sortedSeries[i];
663                         sortedSeries[i] = sortedSeries[j];
664                         sortedSeries[j] = temp;
665                     }
666             }
667
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 ++)
674             {
675                 size_t numPtrsInBytes = sortedSeries[index]->GetSeriesSize()
676                     + pElemMT->GetBaseSize();
677                 size_t currentOffset;
678                 size_t skip;
679                 currentOffset = sortedSeries[index]->GetSeriesOffset()+numPtrsInBytes;
680                 if (index != nSeries-1)
681                 {
682                     skip = sortedSeries[index+1]->GetSeriesOffset()-currentOffset;
683                 }
684                 else if (index == 0)
685                 {
686                     skip = pElemMT->GetAlignedNumInstanceFieldBytes() - numPtrsInBytes;
687                 }
688                 else
689                 {
690                     skip = sortedSeries[0]->GetSeriesOffset() + pElemMT->GetBaseSize()
691                          - OBJECT_BASESIZE - currentOffset;
692                 }
693
694                 _ASSERTE(!"Module::CreateArrayMethodTable() - unaligned GC info" || IS_ALIGNED(skip, TARGET_POINTER_SIZE));
695
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);
700
701                     StackScratchBuffer scratch;
702                     elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch),
703                                                                       IDS_CLASSLOAD_VALUECLASSTOOLARGE);
704                 }
705
706                 val_serie_item *val_item = &(pSeries->val_serie[-index]);
707
708                 val_item->set_val_serie_item (NumPtrs, (unsigned short)skip);
709             }
710         }
711     }
712     else if (CorTypeInfo::IsObjRef(elemType))
713     {
714         CGCDescSeries  *pSeries;
715
716         pMT->SetContainsPointers();
717
718         // This array is all GC Pointers
719         CGCDesc::GetCGCDescFromMT(pMT)->Init( pMT, 1 );
720
721         pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
722
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()));
727     }
728
729 #ifdef FEATURE_COLLECTIBLE_TYPES
730     if (!pMT->ContainsPointers() && this->IsCollectible())
731     {
732         CGCDescSeries  *pSeries;
733
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()));
739     }
740 #endif
741
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());
745
746 #ifdef FEATURE_PREJIT
747     _ASSERTE(pComputedPZM == Module::GetPreferredZapModuleForMethodTable(pMT));
748 #endif
749
750     return(pMT);
751 } // Module::CreateArrayMethodTable
752
753 #ifndef CROSSGEN_COMPILE
754
755 #ifdef FEATURE_ARRAYSTUB_AS_IL
756
757 class ArrayOpLinker : public ILStubLinker
758 {
759     ILCodeStream * m_pCode;
760     ArrayMethodDesc * m_pMD;
761
762     SigTypeContext m_emptyContext;
763
764 public:
765     ArrayOpLinker(ArrayMethodDesc * pMD)
766         : ILStubLinker(pMD->GetModule(), pMD->GetSignature(), &m_emptyContext, pMD, TRUE, TRUE, FALSE)
767     {
768         m_pCode = NewCodeStream(kDispatch);
769         m_pMD = pMD;
770     }
771
772     void EmitStub()
773     {
774         MethodTable *pMT = m_pMD->GetMethodTable();
775         BOOL fHasLowerBounds = pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY;
776
777         DWORD dwTotalLocalNum = NewLocal(ELEMENT_TYPE_I4);
778         DWORD dwLengthLocalNum = NewLocal(ELEMENT_TYPE_I4);
779
780         mdToken tokRawData = GetToken(MscorlibBinder::GetField(FIELD__RAW_DATA__DATA));
781
782         ILCodeLabel * pRangeExceptionLabel = NewCodeLabel();
783         ILCodeLabel * pRangeExceptionLabel1 = NewCodeLabel();
784         ILCodeLabel * pCheckDone = NewCodeLabel();
785         ILCodeLabel * pNotSZArray = NewCodeLabel();
786         ILCodeLabel * pTypeMismatchExceptionLabel = NULL;
787
788         UINT rank = pMT->GetRank();
789         UINT firstIdx = 0;
790         UINT hiddenArgIdx = rank;
791         _ASSERTE(rank>0);
792
793 #ifndef _TARGET_X86_
794         if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
795         {
796             firstIdx = 1;
797             hiddenArgIdx = 0;
798         }
799 #endif
800
801         ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
802         if(pcls->GetArrayElementType() == ELEMENT_TYPE_CLASS)
803         {
804             // Type Check
805             if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_SET)
806             {
807                 ILCodeLabel * pTypeCheckOK = NewCodeLabel();
808                 
809                 m_pCode->EmitLDARG(rank); // load value to store
810                 m_pCode->EmitBRFALSE(pTypeCheckOK); //Storing NULL is OK
811                 
812                 m_pCode->EmitLDARG(rank); // return param
813                 m_pCode->EmitLDFLDA(tokRawData);
814                 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
815                 m_pCode->EmitSUB();
816                 m_pCode->EmitLDIND_I(); // TypeHandle
817
818                 m_pCode->EmitLoadThis();
819                 m_pCode->EmitLDFLDA(tokRawData);
820                 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
821                 m_pCode->EmitSUB();
822                 m_pCode->EmitLDIND_I(); // Array MT
823                 m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
824                 m_pCode->EmitADD();
825                 m_pCode->EmitLDIND_I();
826                 
827                 m_pCode->EmitCEQ();
828                 m_pCode->EmitBRTRUE(pTypeCheckOK); // Same type is OK
829
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);
834
835                 m_pCode->EmitLabel(pTypeCheckOK);
836
837             }
838             else if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
839             {
840                 // Check that the hidden param is same type
841                 ILCodeLabel *pTypeCheckPassed = NewCodeLabel();
842                 pTypeMismatchExceptionLabel = NewCodeLabel();
843
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));
849                 m_pCode->EmitADD();
850                 m_pCode->EmitLDIND_I();
851                 
852                 m_pCode->EmitLoadThis();
853                 m_pCode->EmitLDFLDA(tokRawData);
854                 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
855                 m_pCode->EmitSUB();
856                 m_pCode->EmitLDIND_I(); // Array MT
857                 m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
858                 m_pCode->EmitADD();
859                 m_pCode->EmitLDIND_I();
860                 
861                 m_pCode->EmitCEQ();
862                 m_pCode->EmitBRFALSE(pTypeMismatchExceptionLabel); // throw exception if not same
863                 m_pCode->EmitLabel(pTypeCheckPassed);
864             }
865         }
866
867         if(rank == 1 && fHasLowerBounds)
868         {
869             // check if the array is SZArray.
870             m_pCode->EmitLoadThis();
871             m_pCode->EmitLDFLDA(tokRawData);
872             m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
873             m_pCode->EmitSUB();
874             m_pCode->EmitLDIND_I();
875             m_pCode->EmitLDC(MethodTable::GetOffsetOfFlags());
876             m_pCode->EmitADD();
877             m_pCode->EmitLDIND_I4();
878             m_pCode->EmitLDC(MethodTable::GetIfArrayThenSzArrayFlag());
879             m_pCode->EmitAND();
880             m_pCode->EmitBRFALSE(pNotSZArray); // goto multi-dimmArray code if not szarray
881
882             // it is SZArray
883             // bounds check
884             m_pCode->EmitLoadThis();
885             m_pCode->EmitLDFLDA(tokRawData);
886             m_pCode->EmitLDC(ArrayBase::GetOffsetOfNumComponents() - Object::GetOffsetOfFirstField());
887             m_pCode->EmitADD();
888             m_pCode->EmitLDIND_I4();
889             m_pCode->EmitLDARG(firstIdx);
890             m_pCode->EmitBLE_UN(pRangeExceptionLabel);
891
892             m_pCode->EmitLoadThis();
893             m_pCode->EmitLDFLDA(tokRawData);
894             m_pCode->EmitLDC(ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField());
895             m_pCode->EmitADD();
896             m_pCode->EmitLDARG(firstIdx);
897             m_pCode->EmitBR(pCheckDone);
898             m_pCode->EmitLabel(pNotSZArray);
899         }
900
901         for (UINT i = 0; i < rank; i++)
902         {
903             // Cache length
904             m_pCode->EmitLoadThis();
905             m_pCode->EmitLDFLDA(tokRawData);
906             m_pCode->EmitLDC((ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + i*sizeof(DWORD));
907             m_pCode->EmitADD();
908             m_pCode->EmitLDIND_I4();
909             m_pCode->EmitSTLOC(dwLengthLocalNum);
910
911             // Fetch index
912             m_pCode->EmitLDARG(firstIdx + i);
913
914             if (fHasLowerBounds)
915             {
916                 // Load lower bound
917                 m_pCode->EmitLoadThis();
918                 m_pCode->EmitLDFLDA(tokRawData);
919                 m_pCode->EmitLDC((ArrayBase::GetLowerBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + i*sizeof(DWORD));
920                 m_pCode->EmitADD();
921                 m_pCode->EmitLDIND_I4();
922
923                 // Subtract lower bound
924                 m_pCode->EmitSUB();
925             }
926
927             // Compare with length
928             m_pCode->EmitDUP();
929             m_pCode->EmitLDLOC(dwLengthLocalNum);
930             m_pCode->EmitBGE_UN(pRangeExceptionLabel1);
931
932             // Add to the running total if we have one already
933             if (i > 0)
934             {
935                 m_pCode->EmitLDLOC(dwTotalLocalNum);
936                 m_pCode->EmitLDLOC(dwLengthLocalNum);
937                 m_pCode->EmitMUL();
938                 m_pCode->EmitADD();
939             }
940             m_pCode->EmitSTLOC(dwTotalLocalNum);
941         }
942
943         // Compute element address
944         m_pCode->EmitLoadThis();
945         m_pCode->EmitLDFLDA(tokRawData);
946         m_pCode->EmitLDC(ArrayBase::GetDataPtrOffset(pMT) - Object::GetOffsetOfFirstField());
947         m_pCode->EmitADD();
948         m_pCode->EmitLDLOC(dwTotalLocalNum);
949
950         m_pCode->EmitLabel(pCheckDone);
951
952         m_pCode->EmitCONV_U();
953
954         SIZE_T elemSize = pMT->GetComponentSize();
955         if (elemSize != 1)
956         {
957             m_pCode->EmitLDC(elemSize);
958             m_pCode->EmitMUL();
959         }
960         m_pCode->EmitADD();
961
962         LocalDesc elemType(pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType());
963
964         switch (m_pMD->GetArrayFuncIndex())
965         {
966
967         case ArrayMethodDesc::ARRAY_FUNC_GET:
968             if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
969             {
970                 m_pCode->EmitLDOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
971             }
972             else
973                 m_pCode->EmitLDIND_T(&elemType);
974             break;
975
976         case ArrayMethodDesc::ARRAY_FUNC_SET:
977             // Value to store into the array
978             m_pCode->EmitLDARG(rank);
979             
980             if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
981             {
982                 m_pCode->EmitSTOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
983             }
984             else
985                 m_pCode->EmitSTIND_T(&elemType);
986             break;
987
988         case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
989             break;
990
991         default:
992             _ASSERTE(!"Unknown ArrayFuncIndex");
993         }
994
995         m_pCode->EmitRET();
996
997         m_pCode->EmitLDC(0);
998         m_pCode->EmitLabel(pRangeExceptionLabel1); // Assumes that there is one "int" pushed on the stack
999         m_pCode->EmitPOP();
1000
1001         mdToken tokIndexOutOfRangeCtorExcep = GetToken((MscorlibBinder::GetException(kIndexOutOfRangeException))->GetDefaultConstructor());
1002         m_pCode->EmitLabel(pRangeExceptionLabel);
1003         m_pCode->EmitNEWOBJ(tokIndexOutOfRangeCtorExcep, 0);
1004         m_pCode->EmitTHROW();
1005
1006         if(pTypeMismatchExceptionLabel != NULL)
1007         {
1008             mdToken tokTypeMismatchExcepCtor = GetToken((MscorlibBinder::GetException(kArrayTypeMismatchException))->GetDefaultConstructor());
1009
1010             m_pCode->EmitLabel(pTypeMismatchExceptionLabel);
1011             m_pCode->EmitNEWOBJ(tokTypeMismatchExcepCtor, 0);
1012             m_pCode->EmitTHROW();
1013         }
1014     }
1015 };
1016
1017 Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
1018 {
1019     STANDARD_VM_CONTRACT;
1020
1021     ArrayOpLinker sl(pMD);
1022
1023     sl.EmitStub();
1024
1025     PCCOR_SIGNATURE pSig;
1026     DWORD cbSig;
1027     AllocMemTracker amTracker;
1028
1029     if (pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
1030     {
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,
1035                                                                                           &pSig,
1036                                                                                           &cbSig,
1037                                                                                           pMD->GetLoaderAllocator(),
1038                                                                                           &amTracker,
1039                                                                                           1);
1040     }
1041     else
1042     {
1043          pMD->GetSig(&pSig,&cbSig);
1044     }
1045
1046     amTracker.SuppressRelease();
1047
1048     static const ILStubTypes stubTypes[3] = { ILSTUB_ARRAYOP_GET, ILSTUB_ARRAYOP_SET, ILSTUB_ARRAYOP_ADDRESS };
1049
1050     _ASSERTE(pMD->GetArrayFuncIndex() <= COUNTOF(stubTypes));
1051     NDirectStubFlags arrayOpStubFlag = (NDirectStubFlags)stubTypes[pMD->GetArrayFuncIndex()];
1052
1053     MethodDesc * pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc(pMD->GetLoaderAllocator(),
1054                                                             pMD->GetMethodTable(),
1055                                                             arrayOpStubFlag,
1056                                                             pMD->GetModule(),
1057                                                             pSig, cbSig,
1058                                                             NULL,
1059                                                             &sl);
1060
1061     return Stub::NewStub(JitILStub(pStubMD));
1062 }
1063
1064 #else // FEATURE_ARRAYSTUB_AS_IL
1065 //========================================================================
1066 // Generates the platform-independent arrayop stub.
1067 //========================================================================
1068 void GenerateArrayOpScript(ArrayMethodDesc *pMD, ArrayOpScript *paos)
1069 {
1070     STANDARD_VM_CONTRACT;
1071
1072     ArrayOpIndexSpec *pai = NULL;
1073     MethodTable *pMT = pMD->GetMethodTable();
1074     ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
1075
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());
1081
1082     paos->m_rank            = (BYTE)(pMT->GetRank());
1083     paos->m_fHasLowerBounds = (pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY);
1084
1085     paos->m_ofsoffirst      = ArrayBase::GetDataPtrOffset(pMT);
1086
1087     switch (pMD->GetArrayFuncIndex())
1088     {
1089     case ArrayMethodDesc::ARRAY_FUNC_GET:
1090         paos->m_op = ArrayOpScript::LOAD;
1091         break;
1092     case ArrayMethodDesc::ARRAY_FUNC_SET:
1093         paos->m_op = ArrayOpScript::STORE;
1094         break;
1095     case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
1096         paos->m_op = ArrayOpScript::LOADADDR;
1097         break;
1098     default:
1099         _ASSERTE(!"Unknown array func!");
1100     }
1101
1102     MetaSig msig(pMD);
1103     _ASSERTE(!msig.IsVarArg());     // No array signature is varargs, code below does not expect it.
1104
1105     switch (pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType())
1106     {
1107         // These are all different because of sign extension
1108
1109         case ELEMENT_TYPE_I1:
1110             paos->m_elemsize = 1;
1111             paos->m_signed = TRUE;
1112             break;
1113
1114         case ELEMENT_TYPE_BOOLEAN:
1115         case ELEMENT_TYPE_U1:
1116             paos->m_elemsize = 1;
1117             break;
1118
1119         case ELEMENT_TYPE_I2:
1120             paos->m_elemsize = 2;
1121             paos->m_signed = TRUE;
1122             break;
1123
1124         case ELEMENT_TYPE_CHAR:
1125         case ELEMENT_TYPE_U2:
1126             paos->m_elemsize = 2;
1127             break;
1128
1129         case ELEMENT_TYPE_I4:
1130         IN_WIN32(case ELEMENT_TYPE_I:)
1131             paos->m_elemsize = 4;
1132             paos->m_signed = TRUE;
1133             break;
1134
1135         case ELEMENT_TYPE_U4:
1136         IN_WIN32(case ELEMENT_TYPE_U:)
1137         IN_WIN32(case ELEMENT_TYPE_PTR:)
1138             paos->m_elemsize = 4;
1139             break;
1140
1141         case ELEMENT_TYPE_I8:
1142         IN_WIN64(case ELEMENT_TYPE_I:)
1143             paos->m_elemsize = 8;
1144             paos->m_signed = TRUE;
1145             break;
1146
1147         case ELEMENT_TYPE_U8:
1148         IN_WIN64(case ELEMENT_TYPE_U:)
1149         IN_WIN64(case ELEMENT_TYPE_PTR:)
1150             paos->m_elemsize = 8;
1151             break;
1152
1153         case ELEMENT_TYPE_R4:
1154             paos->m_elemsize = 4;
1155             paos->m_flags |= paos->ISFPUTYPE;
1156             break;
1157
1158         case ELEMENT_TYPE_R8:
1159             paos->m_elemsize = 8;
1160             paos->m_flags |= paos->ISFPUTYPE;
1161             break;
1162
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)
1171             {
1172                 paos->m_flags |= paos->NEEDSTYPECHECK;
1173             }
1174
1175             break;
1176
1177         case ELEMENT_TYPE_VALUETYPE:
1178             paos->m_elemsize = pMT->GetComponentSize();
1179             if (pMT->ContainsPointers()) 
1180             {
1181                 paos->m_gcDesc = CGCDesc::GetCGCDescFromMT(pMT);
1182                 paos->m_flags |= paos->NEEDSWRITEBARRIER;
1183             }
1184             break;
1185
1186         default:
1187             _ASSERTE(!"Unsupported Array Type!");
1188     }
1189
1190     ArgIterator argit(&msig);
1191
1192 #ifdef _TARGET_X86_
1193     paos->m_cbretpop = argit.CbStackPop();
1194 #endif
1195
1196     if (argit.HasRetBuffArg())
1197     {
1198         paos->m_flags |= ArrayOpScript::HASRETVALBUFFER;
1199         paos->m_fRetBufLoc = argit.GetRetBuffArgOffset();
1200     }
1201
1202     if (paos->m_op == ArrayOpScript::LOADADDR) 
1203     {
1204         paos->m_typeParamOffs = argit.GetParamTypeArgOffset();
1205     }
1206
1207     for (UINT idx = 0; idx < paos->m_rank; idx++)
1208     {
1209         pai = (ArrayOpIndexSpec*)(paos->GetArrayOpIndexSpecs() + idx);
1210
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);
1214     }
1215
1216     if (paos->m_op == paos->STORE)
1217     {
1218         paos->m_fValLoc = argit.GetNextOffset();
1219     }
1220 }
1221
1222 //---------------------------------------------------------
1223 // Cache for array stubs
1224 //---------------------------------------------------------
1225 class ArrayStubCache : public StubCacheBase
1226 {
1227     virtual void CompileStub(const BYTE *pRawStub,
1228                              StubLinker *psl);
1229     virtual UINT Length(const BYTE *pRawStub);
1230
1231 public:
1232     static ArrayStubCache * GetArrayStubCache()
1233     {
1234         STANDARD_VM_CONTRACT;
1235
1236         static ArrayStubCache * s_pArrayStubCache = NULL;
1237
1238         if (s_pArrayStubCache == NULL)
1239         {
1240             ArrayStubCache * pArrayStubCache = new ArrayStubCache();
1241             if (FastInterlockCompareExchangePointer(&s_pArrayStubCache, pArrayStubCache, NULL) != NULL)
1242                 delete pArrayStubCache;
1243         }
1244
1245         return s_pArrayStubCache;
1246     }
1247 };
1248
1249 Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
1250 {
1251     STANDARD_VM_CONTRACT;
1252
1253     MethodTable *pMT = pMD->GetMethodTable();
1254
1255     ArrayOpScript *paos = (ArrayOpScript*)_alloca(sizeof(ArrayOpScript) + sizeof(ArrayOpIndexSpec) * pMT->GetRank());
1256
1257     GenerateArrayOpScript(pMD, paos);
1258
1259     Stub *pArrayOpStub;
1260     pArrayOpStub = ArrayStubCache::GetArrayStubCache()->Canonicalize((const BYTE *)paos);
1261     if (pArrayOpStub == NULL)
1262         COMPlusThrowOM();
1263
1264     return pArrayOpStub;
1265 }
1266
1267 void ArrayStubCache::CompileStub(const BYTE *pRawStub,
1268                                  StubLinker *psl)
1269 {
1270     STANDARD_VM_CONTRACT;
1271
1272     ((CPUSTUBLINKER*)psl)->EmitArrayOpStub((ArrayOpScript*)pRawStub);
1273 }
1274
1275 UINT ArrayStubCache::Length(const BYTE *pRawStub)
1276 {
1277     LIMITED_METHOD_CONTRACT;
1278     return ((ArrayOpScript*)pRawStub)->Length();
1279 }
1280
1281 #endif // FEATURE_ARRAYSTUB_AS_IL
1282
1283 #endif // CROSSGEN_COMPILE
1284
1285 //---------------------------------------------------------------------
1286 // This method returns TRUE if pInterfaceMT could be one of the interfaces
1287 // that are implicitly implemented by SZArrays
1288
1289 BOOL IsImplicitInterfaceOfSZArray(MethodTable *pInterfaceMT)
1290 {
1291     LIMITED_METHOD_CONTRACT;
1292     PRECONDITION(pInterfaceMT->IsInterface());
1293
1294     // Is target interface Anything<T> in mscorlib?
1295     if (!pInterfaceMT->HasInstantiation() || !pInterfaceMT->GetModule()->IsSystem())
1296         return FALSE;
1297
1298     unsigned rid = pInterfaceMT->GetTypeDefRid();
1299
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());
1306 }
1307
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
1311 // separately.
1312 //---------------------------------------------------------------------
1313 BOOL ArraySupportsBizarreInterface(ArrayTypeDesc *pArrayTypeDesc, MethodTable *pInterfaceMT)
1314 {
1315     CONTRACTL
1316     {
1317         THROWS;
1318         GC_TRIGGERS;
1319         INJECT_FAULT(COMPlusThrowOM(););
1320
1321         PRECONDITION(pInterfaceMT->IsInterface());
1322         PRECONDITION(pArrayTypeDesc->IsArray());
1323     }
1324     CONTRACTL_END
1325
1326 #ifdef _DEBUG
1327     MethodTable *pArrayMT = pArrayTypeDesc->GetMethodTable();
1328     _ASSERTE(pArrayMT->IsArray());
1329     _ASSERTE(pArrayMT->IsRestored());
1330 #endif
1331
1332     // IList<T> & IReadOnlyList<T> only supported for SZ_ARRAYS
1333     if (pArrayTypeDesc->GetInternalCorElementType() != ELEMENT_TYPE_SZARRAY)
1334         return FALSE;
1335
1336     ClassLoader::EnsureLoaded(pInterfaceMT, CLASS_DEPENDENCIES_LOADED);
1337
1338     if (!IsImplicitInterfaceOfSZArray(pInterfaceMT))
1339         return FALSE;
1340
1341     return TypeDesc::CanCastParam(pArrayTypeDesc->GetTypeParam(), pInterfaceMT->GetInstantiation()[0], NULL);
1342 }
1343
1344 //----------------------------------------------------------------------------------
1345 // Calls to (IList<T>)(array).Meth are actually implemented by SZArrayHelper.Meth<T>
1346 // This workaround exists for two reasons:
1347 //
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.
1352 //
1353 // (Though it's questionable whether any devtime was saved.)
1354 //
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)
1359 {
1360     CONTRACTL
1361     {
1362         THROWS;
1363         GC_TRIGGERS;
1364         INJECT_FAULT(COMPlusThrowOM());
1365     }
1366     CONTRACTL_END
1367
1368     int slot = pItfcMeth->GetSlot();
1369
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
1375     };
1376
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));
1380    
1381     MethodDesc *pGenericImplementor = MscorlibBinder::GetMethod((BinderMethodID)(startingMethod[inheritanceDepth] + slot));
1382
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()));
1386
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())
1391     {
1392         theT = TypeHandle(g_pObjectClass);
1393     }
1394
1395     MethodDesc *pActualImplementor = MethodDesc::FindOrCreateAssociatedMethodDesc(pGenericImplementor,
1396                                                                                   g_pSZArrayHelperClass,
1397                                                                                   FALSE,
1398                                                                                   Instantiation(&theT, 1),
1399                                                                                   FALSE // allowInstParam
1400                                                                                   );
1401     _ASSERTE(pActualImplementor);
1402     return pActualImplementor;
1403 }
1404 #endif // DACCESS_COMPILE
1405
1406 CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType)
1407 {
1408     LIMITED_METHOD_CONTRACT;
1409
1410     _ASSERTE(CorTypeInfo::IsPrimitiveType_NoThrow(elementType));
1411
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
1415
1416     switch (elementType)
1417     {
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
1424     default:
1425         break;
1426     }
1427
1428     return elementType;
1429 }