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#ifdef _MSC_VER
36#pragma warning(push)
37#pragma warning(disable:4244)
38#endif // _MSC_VER
39
40#define MAX_SIZE_FOR_VALUECLASS_IN_ARRAY 0xffff
41#define MAX_PTRS_FOR_VALUECLASSS_IN_ARRAY 0xffff
42
43
44/*****************************************************************************************/
45LPCUTF8 ArrayMethodDesc::GetMethodName()
46{
47 LIMITED_METHOD_DAC_CONTRACT;
48
49 switch (GetArrayFuncIndex())
50 {
51 case ARRAY_FUNC_GET:
52 return "Get";
53 case ARRAY_FUNC_SET:
54 return "Set";
55 case ARRAY_FUNC_ADDRESS:
56 return "Address";
57 default:
58 return COR_CTOR_METHOD_NAME; // ".ctor"
59 }
60}
61
62/*****************************************************************************************/
63DWORD ArrayMethodDesc::GetAttrs()
64{
65 LIMITED_METHOD_CONTRACT;
66 return (GetArrayFuncIndex() >= ARRAY_FUNC_CTOR) ? (mdPublic | mdRTSpecialName) : mdPublic;
67}
68
69/*****************************************************************************************/
70CorInfoIntrinsics ArrayMethodDesc::GetIntrinsicID()
71{
72 LIMITED_METHOD_CONTRACT;
73
74 switch (GetArrayFuncIndex())
75 {
76 case ARRAY_FUNC_GET:
77 return CORINFO_INTRINSIC_Array_Get;
78 case ARRAY_FUNC_SET:
79 return CORINFO_INTRINSIC_Array_Set;
80 case ARRAY_FUNC_ADDRESS:
81 return CORINFO_INTRINSIC_Array_Address;
82 default:
83 return CORINFO_INTRINSIC_Illegal;
84 }
85}
86
87#ifndef DACCESS_COMPILE
88
89/*****************************************************************************************/
90
91//
92// Generate a short sig (descr) for an array accessors
93//
94
95VOID ArrayClass::GenerateArrayAccessorCallSig(
96 DWORD dwRank,
97 DWORD dwFuncType, // Load, store, or <init>
98 PCCOR_SIGNATURE *ppSig,// Generated signature
99 DWORD * pcSig, // Generated signature size
100 LoaderAllocator *pLoaderAllocator,
101 AllocMemTracker *pamTracker
102#ifdef FEATURE_ARRAYSTUB_AS_IL
103 ,BOOL fForStubAsIL
104#endif
105 )
106{
107 CONTRACTL {
108 STANDARD_VM_CHECK;
109 PRECONDITION(dwRank >= 1 && dwRank < 0x3ffff);
110 } CONTRACTL_END;
111
112 PCOR_SIGNATURE pSig;
113 PCOR_SIGNATURE pSigMemory;
114 DWORD dwCallSigSize = dwRank;
115 DWORD dwArgCount = (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET) ? dwRank+1 : dwRank;
116 DWORD i;
117
118 switch (dwFuncType)
119 {
120 // <callconv> <argcount> VAR 0 I4 , ... , I4
121 case ArrayMethodDesc::ARRAY_FUNC_GET:
122 dwCallSigSize += 4;
123 break;
124
125 // <callconv> <argcount> VOID I4 , ... , I4
126 case ArrayMethodDesc::ARRAY_FUNC_CTOR:
127 dwCallSigSize += 3;
128 break;
129
130 // <callconv> <argcount> VOID I4 , ... , I4 VAR 0
131 case ArrayMethodDesc::ARRAY_FUNC_SET:
132 dwCallSigSize += 5;
133 break;
134
135 // <callconv> <argcount> BYREF VAR 0 I4 , ... , I4
136 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
137 dwCallSigSize += 5;
138#ifdef FEATURE_ARRAYSTUB_AS_IL
139 if(fForStubAsIL) {dwArgCount++; dwCallSigSize++;}
140#endif
141 break;
142 }
143
144 // If the argument count is larger than 127 then it will require 2 bytes for the encoding
145 if (dwArgCount > 0x7f)
146 dwCallSigSize++;
147
148 pSigMemory = (PCOR_SIGNATURE)pamTracker->Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(dwCallSigSize)));
149
150 pSig = pSigMemory;
151 BYTE callConv = IMAGE_CEE_CS_CALLCONV_DEFAULT + IMAGE_CEE_CS_CALLCONV_HASTHIS;
152
153 if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS
154#ifdef FEATURE_ARRAYSTUB_AS_IL
155 && !fForStubAsIL
156#endif
157 )
158 {
159 callConv |= CORINFO_CALLCONV_PARAMTYPE; // Address routine needs special hidden arg
160 }
161
162 *pSig++ = callConv;
163 pSig += CorSigCompressData(dwArgCount, pSig); // Argument count
164 switch (dwFuncType)
165 {
166 case ArrayMethodDesc::ARRAY_FUNC_GET:
167 *pSig++ = ELEMENT_TYPE_VAR;
168 *pSig++ = 0; // variable 0
169 break;
170 case ArrayMethodDesc::ARRAY_FUNC_CTOR:
171 *pSig++ = (BYTE) ELEMENT_TYPE_VOID; // Return type
172 break;
173 case ArrayMethodDesc::ARRAY_FUNC_SET:
174 *pSig++ = (BYTE) ELEMENT_TYPE_VOID; // Return type
175 break;
176 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
177 *pSig++ = (BYTE) ELEMENT_TYPE_BYREF; // Return type
178 *pSig++ = ELEMENT_TYPE_VAR;
179 *pSig++ = 0; // variable 0
180 break;
181 }
182
183#if defined(FEATURE_ARRAYSTUB_AS_IL ) && !defined(_TARGET_X86_)
184 if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
185 {
186 *pSig++ = ELEMENT_TYPE_I;
187 }
188#endif
189
190 for (i = 0; i < dwRank; i++)
191 *pSig++ = ELEMENT_TYPE_I4;
192
193 if (dwFuncType == ArrayMethodDesc::ARRAY_FUNC_SET)
194 {
195 *pSig++ = ELEMENT_TYPE_VAR;
196 *pSig++ = 0; // variable 0
197 }
198#if defined(FEATURE_ARRAYSTUB_AS_IL ) && defined(_TARGET_X86_)
199 else if(dwFuncType == ArrayMethodDesc::ARRAY_FUNC_ADDRESS && fForStubAsIL)
200 {
201 *pSig++ = ELEMENT_TYPE_I;
202 }
203#endif
204
205 // Make sure the sig came out exactly as large as we expected
206 _ASSERTE(pSig == pSigMemory + dwCallSigSize);
207
208 *ppSig = pSigMemory;
209 *pcSig = (DWORD)(pSig-pSigMemory);
210}
211
212//
213// Allocate a new MethodDesc for a fake array method.
214//
215// Based on code in class.cpp.
216//
217void ArrayClass::InitArrayMethodDesc(
218 ArrayMethodDesc *pNewMD,
219 PCCOR_SIGNATURE pShortSig,
220 DWORD cShortSig,
221 DWORD dwVtableSlot,
222 LoaderAllocator *pLoaderAllocator,
223 AllocMemTracker *pamTracker)
224{
225 STANDARD_VM_CONTRACT;
226
227 // Note: The method desc memory is zero initialized
228
229 pNewMD->SetMemberDef(0);
230
231 pNewMD->SetSlot((WORD) dwVtableSlot);
232 pNewMD->SetStoredMethodSig(pShortSig, cShortSig);
233
234 _ASSERTE(!pNewMD->MayHaveNativeCode());
235 pNewMD->SetTemporaryEntryPoint(pLoaderAllocator, pamTracker);
236
237#ifdef _DEBUG
238 _ASSERTE(pNewMD->GetMethodName() && GetDebugClassName());
239 pNewMD->m_pszDebugMethodName = pNewMD->GetMethodName();
240 pNewMD->m_pszDebugClassName = GetDebugClassName();
241 pNewMD->m_pDebugMethodTable.SetValue(pNewMD->GetMethodTable());
242#endif // _DEBUG
243}
244
245/*****************************************************************************************/
246MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementType arrayKind, unsigned Rank, AllocMemTracker *pamTracker)
247{
248 CONTRACTL {
249 STANDARD_VM_CHECK;
250 PRECONDITION(Rank > 0);
251 } CONTRACTL_END;
252
253 MethodTable * pElemMT = elemTypeHnd.GetMethodTable();
254
255 CorElementType elemType = elemTypeHnd.GetSignatureCorElementType();
256
257 // Shared EEClass if there is one
258 MethodTable * pCanonMT = NULL;
259
260 // Strictly speaking no method table should be needed for
261 // arrays of the faked up TypeDescs for variable types that are
262 // used when verfifying generic code.
263 // However verification is tied in with some codegen in the JITs, so give these
264 // the shared MT just in case.
265 // This checks match precisely one in ParamTypeDesc::OwnsMethodTable
266 if (CorTypeInfo::IsGenericVariable(elemType)) {
267 // This is loading the canonical version of the array so we can override
268 OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
269 return(ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass), arrayKind, Rank).GetMethodTable());
270 }
271
272 // Arrays of reference types all share the same EEClass.
273 //
274 // We can't share nested SZARRAYs because they have different
275 // numbers of constructors.
276 //
277 // Unfortunately, we cannot share more because of it would affect user visible System.RuntimeMethodHandle behavior
278 if (CorTypeInfo::IsObjRef(elemType) && elemType != ELEMENT_TYPE_SZARRAY && pElemMT != g_pObjectClass)
279 {
280 // This is loading the canonical version of the array so we can override
281 OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
282 pCanonMT = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass), arrayKind, Rank).GetMethodTable();
283 }
284
285 BOOL containsPointers = CorTypeInfo::IsObjRef(elemType);
286 if (elemType == ELEMENT_TYPE_VALUETYPE && pElemMT->ContainsPointers())
287 containsPointers = TRUE;
288
289 // this is the base for every array type
290 MethodTable *pParentClass = g_pArrayClass;
291 _ASSERTE(pParentClass); // Must have already loaded the System.Array class
292 _ASSERTE(pParentClass->IsFullyLoaded());
293
294 DWORD numCtors = 2; // ELEMENT_TYPE_ARRAY has two ctor functions, one with and one without lower bounds
295 if (arrayKind == ELEMENT_TYPE_SZARRAY)
296 {
297 numCtors = 1;
298 TypeHandle ptr = elemTypeHnd;
299 while (ptr.IsTypeDesc() && ptr.AsTypeDesc()->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY) {
300 numCtors++;
301 ptr = ptr.AsTypeDesc()->GetTypeParam();
302 }
303 }
304
305 /****************************************************************************************/
306
307 // Parent class is the top level array
308 // The vtable will have all of top level class's methods, plus any methods we have for array classes
309 DWORD numVirtuals = pParentClass->GetNumVirtuals();
310 DWORD numNonVirtualSlots = numCtors + 3; // 3 for the proper rank Get, Set, Address
311
312 size_t cbMT = sizeof(MethodTable);
313 cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(MethodTable::VTableIndir_t);
314
315 // GC info
316 size_t cbCGCDescData = 0;
317 if (containsPointers)
318 {
319 cbCGCDescData += CGCDesc::ComputeSize(1);
320 if (elemType == ELEMENT_TYPE_VALUETYPE)
321 {
322 size_t nSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
323 cbCGCDescData += (nSeries - 1)*sizeof (val_serie_item);
324 _ASSERTE(cbCGCDescData == CGCDesc::ComputeSizeRepeating(nSeries));
325 }
326 }
327#ifdef FEATURE_COLLECTIBLE_TYPES
328 else if (this->IsCollectible())
329 {
330 cbCGCDescData = (DWORD)CGCDesc::ComputeSize(1);
331 }
332#endif
333
334 DWORD dwMultipurposeSlotsMask = 0;
335 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasPerInstInfo;
336 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasInterfaceMap;
337 if (pCanonMT == NULL)
338 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasNonVirtualSlots;
339 if (this != elemTypeHnd.GetModule())
340 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasModuleOverride;
341
342 // Allocate space for optional members
343 // We always have a non-virtual slot array, see assert at end
344 cbMT += MethodTable::GetOptionalMembersAllocationSize(dwMultipurposeSlotsMask,
345 FALSE, // GenericsStaticsInfo
346 FALSE, // GuidInfo
347 FALSE, // CCWTemplate
348 FALSE, // RCWPerTypeData
349 FALSE); // TokenOverflow
350
351 // This is the offset of the beginning of the interface map
352 size_t imapOffset = cbMT;
353
354 // This is added after we determine the offset of the interface maps
355 // because the memory appears before the pointer to the method table
356 cbMT += cbCGCDescData;
357
358 // Inherit top level class's interface map
359 cbMT += pParentClass->GetNumInterfaces() * sizeof(InterfaceInfo_t);
360
361#ifdef FEATURE_PREJIT
362 Module* pComputedPZM = Module::ComputePreferredZapModule(NULL, Instantiation(&elemTypeHnd, 1));
363 BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this, pComputedPZM);
364#else
365 BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pParentClass, this);
366#endif // FEATURE_PREJIT
367
368 size_t offsetOfUnsharedVtableChunks = cbMT;
369
370 // We either share all of the parent's virtual slots or none of them
371 // If none, we need to allocate space for the slots
372 if (!canShareVtableChunks)
373 {
374 cbMT += numVirtuals * sizeof(MethodTable::VTableIndir2_t);
375 }
376
377 // Canonical methodtable has an array of non virtual slots pointed to by the optional member
378 size_t offsetOfNonVirtualSlots = 0;
379 size_t cbArrayClass = 0;
380
381 if (pCanonMT == NULL)
382 {
383 offsetOfNonVirtualSlots = cbMT;
384 cbMT += numNonVirtualSlots * sizeof(PCODE);
385
386 // Allocate ArrayClass (including space for packed fields), MethodTable, and class name in one alloc.
387 // Remember to pad allocation size for ArrayClass portion to ensure MethodTable is pointer aligned.
388 cbArrayClass = ALIGN_UP(sizeof(ArrayClass) + sizeof(EEClassPackedFields), sizeof(void*));
389 }
390
391 // ArrayClass already includes one void*
392 LoaderAllocator* pAllocator= this->GetLoaderAllocator();
393 BYTE* pMemory = (BYTE *)pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbArrayClass) +
394 S_SIZE_T(cbMT)));
395
396 // Note: Memory allocated on loader heap is zero filled
397 // memset(pMemory, 0, sizeof(ArrayClass) + cbMT);
398
399 ArrayClass* pClass = NULL;
400
401 if (pCanonMT == NULL)
402 {
403 pClass = ::new (pMemory) ArrayClass();
404 }
405
406 // Head of MethodTable memory (starts after ArrayClass), this points at the GCDesc stuff in front
407 // of a method table (if needed)
408 BYTE* pMTHead = pMemory + cbArrayClass + cbCGCDescData;
409
410 MethodTable* pMT = (MethodTable *) pMTHead;
411
412 pMT->SetMultipurposeSlotsMask(dwMultipurposeSlotsMask);
413
414 // Allocate the private data block ("private" during runtime in the ngen'ed case).
415 MethodTableWriteableData * pMTWriteableData = (MethodTableWriteableData *) (BYTE *)
416 pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(MethodTableWriteableData))));
417 pMT->SetWriteableData(pMTWriteableData);
418
419 // This also disables IBC logging until the type is sufficiently intitialized so
420 // it needs to be done early
421 pMTWriteableData->SetIsNotFullyLoadedForBuildMethodTable();
422
423 // Fill in pClass
424 if (pClass != NULL)
425 {
426 pClass->SetInternalCorElementType(arrayKind);
427 pClass->SetAttrClass (tdPublic | tdSerializable | tdSealed); // This class is public, serializable, sealed
428 pClass->SetRank (Rank);
429 pClass->SetArrayElementType (elemType);
430 pClass->SetMethodTable (pMT);
431
432 // Fill In the method table
433 pClass->SetNumMethods(numVirtuals + numNonVirtualSlots);
434
435 pClass->SetNumNonVirtualSlots(numNonVirtualSlots);
436 }
437
438 pMT->SetNumVirtuals(numVirtuals);
439
440 pMT->SetParentMethodTable(pParentClass);
441
442 DWORD dwComponentSize = elemTypeHnd.GetSize();
443
444 if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
445 {
446 // The only way for dwComponentSize to be large is to be part of a value class. If this changes
447 // then the check will need to be moved outside valueclass check.
448 if(dwComponentSize > MAX_SIZE_FOR_VALUECLASS_IN_ARRAY) {
449 StackSString ssElemName;
450 elemTypeHnd.GetName(ssElemName);
451
452 StackScratchBuffer scratch;
453 elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch), IDS_CLASSLOAD_VALUECLASSTOOLARGE);
454 }
455 }
456
457 if (pClass != NULL)
458 {
459 pMT->SetClass(pClass);
460 }
461 else
462 {
463 pMT->SetCanonicalMethodTable(pCanonMT);
464 }
465
466 pMT->SetIsArray(arrayKind, elemType);
467
468 pMT->SetApproxArrayElementTypeHandle(elemTypeHnd);
469
470 _ASSERTE(FitsIn<WORD>(dwComponentSize));
471 pMT->SetComponentSize(static_cast<WORD>(dwComponentSize));
472
473 pMT->SetLoaderModule(this);
474 pMT->SetLoaderAllocator(pAllocator);
475
476 pMT->SetModule(elemTypeHnd.GetModule());
477
478 if (elemTypeHnd.ContainsGenericVariables())
479 pMT->SetContainsGenericVariables();
480
481#ifdef FEATURE_TYPEEQUIVALENCE
482 if (elemTypeHnd.HasTypeEquivalence())
483 {
484 // propagate the type equivalence flag
485 pMT->SetHasTypeEquivalence();
486 }
487#endif // FEATURE_TYPEEQUIVALENCE
488
489 _ASSERTE(pMT->IsClassPreInited());
490
491 // Set BaseSize to be size of non-data portion of the array
492 DWORD baseSize = ARRAYBASE_BASESIZE;
493 if (arrayKind == ELEMENT_TYPE_ARRAY)
494 baseSize += Rank*sizeof(DWORD)*2;
495
496#if !defined(_TARGET_64BIT_) && (DATA_ALIGNMENT > 4)
497 if (dwComponentSize >= DATA_ALIGNMENT)
498 baseSize = (DWORD)ALIGN_UP(baseSize, DATA_ALIGNMENT);
499#endif // !defined(_TARGET_64BIT_) && (DATA_ALIGNMENT > 4)
500 pMT->SetBaseSize(baseSize);
501 // Because of array method table persisting, we need to copy the map
502 for (unsigned index = 0; index < pParentClass->GetNumInterfaces(); ++index)
503 {
504 InterfaceInfo_t *pIntInfo = (InterfaceInfo_t *) (pMTHead + imapOffset + index * sizeof(InterfaceInfo_t));
505 pIntInfo->SetMethodTable((pParentClass->GetInterfaceMap() + index)->GetMethodTable());
506 }
507 pMT->SetInterfaceMap(pParentClass->GetNumInterfaces(), (InterfaceInfo_t *)(pMTHead + imapOffset));
508
509 // Copy down flags for these interfaces as well. This is simplified a bit since we know that System.Array
510 // only has a few interfaces and the flags will fit inline into the MethodTable's optional members.
511 _ASSERTE(MethodTable::GetExtraInterfaceInfoSize(pParentClass->GetNumInterfaces()) == 0);
512 pMT->InitializeExtraInterfaceInfo(NULL);
513
514 for (UINT32 i = 0; i < pParentClass->GetNumInterfaces(); i++)
515 {
516 if (pParentClass->IsInterfaceDeclaredOnClass(i))
517 pMT->SetInterfaceDeclaredOnClass(i);
518 }
519
520 // The type is sufficiently initialized for most general purpose accessor methods to work.
521 // Mark the type as restored to avoid asserts. Note that this also enables IBC logging.
522 pMTWriteableData->SetIsFullyLoadedForBuildMethodTable();
523
524 {
525 // Fill out the vtable indirection slots
526 MethodTable::VtableIndirectionSlotIterator it = pMT->IterateVtableIndirectionSlots();
527 while (it.Next())
528 {
529 if (canShareVtableChunks)
530 {
531 // Share the parent chunk
532 it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
533 }
534 else
535 {
536 // Use the locally allocated chunk
537 it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(pMemory+cbArrayClass+offsetOfUnsharedVtableChunks));
538 offsetOfUnsharedVtableChunks += it.GetSize();
539 }
540 }
541
542 // If we are not sharing parent chunks, copy down the slot contents
543 if (!canShareVtableChunks)
544 {
545 // Copy top level class's vtable - note, vtable is contained within the MethodTable
546 for (UINT32 i = 0; i < numVirtuals; i++)
547 pMT->SetSlot(i, pParentClass->GetSlot(i));
548 }
549
550 if (pClass != NULL)
551 pMT->SetNonVirtualSlotsArray((PTR_PCODE)(pMemory+cbArrayClass+offsetOfNonVirtualSlots));
552 }
553
554#ifdef _DEBUG
555 StackSString debugName;
556 TypeString::AppendType(debugName, TypeHandle(pMT));
557 StackScratchBuffer buff;
558 const char* pDebugNameUTF8 = debugName.GetUTF8(buff);
559 S_SIZE_T safeLen = S_SIZE_T(strlen(pDebugNameUTF8))+S_SIZE_T(1);
560 if(safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
561 size_t len = safeLen.Value();
562 char * name = (char*) pamTracker->Track(pAllocator->
563 GetHighFrequencyHeap()->
564 AllocMem(safeLen));
565 strcpy_s(name, len, pDebugNameUTF8);
566
567 if (pClass != NULL)
568 pClass->SetDebugClassName(name);
569 pMT->SetDebugClassName(name);
570#endif // _DEBUG
571
572 if (pClass != NULL)
573 {
574 // Count the number of method descs we need so we can allocate chunks.
575 DWORD dwMethodDescs = numCtors
576 + 3; // for rank specific Get, Set, Address
577
578 MethodDescChunk * pChunks = MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
579 dwMethodDescs, mcArray, FALSE /* fNonVtableSlot*/, FALSE /* fNativeCodeSlot */, FALSE /* fComPlusCallInfo */,
580 pMT, pamTracker);
581 pClass->SetChunks(pChunks);
582
583 MethodTable::IntroducedMethodIterator it(pMT);
584
585 DWORD dwMethodIndex = 0;
586 for (; it.IsValid(); it.Next())
587 {
588 ArrayMethodDesc* pNewMD = (ArrayMethodDesc *) it.GetMethodDesc();
589 _ASSERTE(pNewMD->GetClassification() == mcArray);
590
591 DWORD dwFuncRank;
592 DWORD dwFuncType;
593
594 if (dwMethodIndex < ArrayMethodDesc::ARRAY_FUNC_CTOR)
595 {
596 // Generate a new stand-alone, Rank Specific Get, Set and Address method.
597 dwFuncRank = Rank;
598 dwFuncType = dwMethodIndex;
599 }
600 else
601 {
602 if (arrayKind == ELEMENT_TYPE_SZARRAY)
603 {
604 // For SZARRAY arrays, set up multiple constructors.
605 dwFuncRank = 1 + (dwMethodIndex - ArrayMethodDesc::ARRAY_FUNC_CTOR);
606 }
607 else
608 {
609 // ELEMENT_TYPE_ARRAY has two constructors, one without lower bounds and one with lower bounds
610 _ASSERTE((dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR) || (dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR+1));
611 dwFuncRank = (dwMethodIndex == ArrayMethodDesc::ARRAY_FUNC_CTOR) ? Rank : 2 * Rank;
612 }
613 dwFuncType = ArrayMethodDesc::ARRAY_FUNC_CTOR;
614 }
615
616 PCCOR_SIGNATURE pSig;
617 DWORD cSig;
618
619 pClass->GenerateArrayAccessorCallSig(dwFuncRank, dwFuncType, &pSig, &cSig, pAllocator, pamTracker
620 #ifdef FEATURE_ARRAYSTUB_AS_IL
621 ,0
622 #endif
623 );
624
625 pClass->InitArrayMethodDesc(pNewMD, pSig, cSig, numVirtuals + dwMethodIndex, pAllocator, pamTracker);
626
627 dwMethodIndex++;
628 }
629 _ASSERTE(dwMethodIndex == dwMethodDescs);
630 }
631
632 // Set up GC information
633 if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_VOID)
634 {
635 // If it's an array of value classes, there is a different format for the GCDesc if it contains pointers
636 if (pElemMT->ContainsPointers())
637 {
638 CGCDescSeries *pSeries;
639
640 // There must be only one series for value classes
641 CGCDescSeries *pByValueSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetHighestSeries();
642
643 pMT->SetContainsPointers();
644
645 // negative series has a special meaning, indicating a different form of GCDesc
646 SSIZE_T nSeries = (SSIZE_T) CGCDesc::GetCGCDescFromMT(pElemMT)->GetNumSeries();
647 CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, nSeries);
648
649 pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
650
651 // sort by offset
652 SSIZE_T AllocSizeSeries;
653 if (!ClrSafeInt<SSIZE_T>::multiply(sizeof(CGCDescSeries*), nSeries, AllocSizeSeries))
654 COMPlusThrowOM();
655 CGCDescSeries** sortedSeries = (CGCDescSeries**) _alloca(AllocSizeSeries);
656 int index;
657 for (index = 0; index < nSeries; index++)
658 sortedSeries[index] = &pByValueSeries[-index];
659
660 // section sort
661 for (int i = 0; i < nSeries; i++) {
662 for (int j = i+1; j < nSeries; j++)
663 if (sortedSeries[j]->GetSeriesOffset() < sortedSeries[i]->GetSeriesOffset())
664 {
665 CGCDescSeries* temp = sortedSeries[i];
666 sortedSeries[i] = sortedSeries[j];
667 sortedSeries[j] = temp;
668 }
669 }
670
671 // Offset of the first pointer in the array
672 // This equals the offset of the first pointer if this were an array of entirely pointers, plus the offset of the
673 // first pointer in the value class
674 pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT)
675 + (sortedSeries[0]->GetSeriesOffset()) - OBJECT_SIZE);
676 for (index = 0; index < nSeries; index ++)
677 {
678 size_t numPtrsInBytes = sortedSeries[index]->GetSeriesSize()
679 + pElemMT->GetBaseSize();
680 size_t currentOffset;
681 size_t skip;
682 currentOffset = sortedSeries[index]->GetSeriesOffset()+numPtrsInBytes;
683 if (index != nSeries-1)
684 {
685 skip = sortedSeries[index+1]->GetSeriesOffset()-currentOffset;
686 }
687 else if (index == 0)
688 {
689 skip = pElemMT->GetAlignedNumInstanceFieldBytes() - numPtrsInBytes;
690 }
691 else
692 {
693 skip = sortedSeries[0]->GetSeriesOffset() + pElemMT->GetBaseSize()
694 - OBJECT_BASESIZE - currentOffset;
695 }
696
697 _ASSERTE(!"Module::CreateArrayMethodTable() - unaligned GC info" || IS_ALIGNED(skip, TARGET_POINTER_SIZE));
698
699 unsigned short NumPtrs = (unsigned short) (numPtrsInBytes / TARGET_POINTER_SIZE);
700 if(skip > MAX_SIZE_FOR_VALUECLASS_IN_ARRAY || numPtrsInBytes > MAX_PTRS_FOR_VALUECLASSS_IN_ARRAY) {
701 StackSString ssElemName;
702 elemTypeHnd.GetName(ssElemName);
703
704 StackScratchBuffer scratch;
705 elemTypeHnd.GetAssembly()->ThrowTypeLoadException(ssElemName.GetUTF8(scratch),
706 IDS_CLASSLOAD_VALUECLASSTOOLARGE);
707 }
708
709 val_serie_item *val_item = &(pSeries->val_serie[-index]);
710
711 val_item->set_val_serie_item (NumPtrs, (unsigned short)skip);
712 }
713 }
714 }
715 else if (CorTypeInfo::IsObjRef(elemType))
716 {
717 CGCDescSeries *pSeries;
718
719 pMT->SetContainsPointers();
720
721 // This array is all GC Pointers
722 CGCDesc::GetCGCDescFromMT(pMT)->Init( pMT, 1 );
723
724 pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
725
726 pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT));
727 // For arrays, the size is the negative of the BaseSize (the GC always adds the total
728 // size of the object, so what you end up with is the size of the data portion of the array)
729 pSeries->SetSeriesSize(-(SSIZE_T)(pMT->GetBaseSize()));
730 }
731
732#ifdef FEATURE_COLLECTIBLE_TYPES
733 if (!pMT->ContainsPointers() && this->IsCollectible())
734 {
735 CGCDescSeries *pSeries;
736
737 // For collectible types, insert empty gc series
738 CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, 1);
739 pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries();
740 pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT));
741 pSeries->val_serie[0].set_val_serie_item (0, pMT->GetComponentSize());
742 }
743#endif
744
745 // If we get here we are assuming that there was no truncation. If this is not the case then
746 // an array whose base type is not a value class was created and was larger then 0xffff (a word)
747 _ASSERTE(dwComponentSize == pMT->GetComponentSize());
748
749#ifdef FEATURE_PREJIT
750 _ASSERTE(pComputedPZM == Module::GetPreferredZapModuleForMethodTable(pMT));
751#endif
752
753 return(pMT);
754} // Module::CreateArrayMethodTable
755
756#ifndef CROSSGEN_COMPILE
757
758#ifdef FEATURE_ARRAYSTUB_AS_IL
759
760class ArrayOpLinker : public ILStubLinker
761{
762 ILCodeStream * m_pCode;
763 ArrayMethodDesc * m_pMD;
764
765 SigTypeContext m_emptyContext;
766
767public:
768 ArrayOpLinker(ArrayMethodDesc * pMD)
769 : ILStubLinker(pMD->GetModule(), pMD->GetSignature(), &m_emptyContext, pMD, TRUE, TRUE, FALSE)
770 {
771 m_pCode = NewCodeStream(kDispatch);
772 m_pMD = pMD;
773 }
774
775 void EmitStub()
776 {
777 MethodTable *pMT = m_pMD->GetMethodTable();
778 BOOL fHasLowerBounds = pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY;
779
780 DWORD dwTotalLocalNum = NewLocal(ELEMENT_TYPE_I4);
781 DWORD dwFactorLocalNum = NewLocal(ELEMENT_TYPE_I4);
782 DWORD dwLengthLocalNum = NewLocal(ELEMENT_TYPE_I4);
783
784 mdToken tokRawData = GetToken(MscorlibBinder::GetField(FIELD__RAW_DATA__DATA));
785
786 ILCodeLabel * pRangeExceptionLabel = NewCodeLabel();
787 ILCodeLabel * pRangeExceptionLabel1 = NewCodeLabel();
788 ILCodeLabel * pCheckDone = NewCodeLabel();
789 ILCodeLabel * pNotSZArray = NewCodeLabel();
790 ILCodeLabel * pTypeMismatchExceptionLabel = NULL;
791
792 UINT rank = pMT->GetRank();
793 UINT idx = rank;
794 UINT firstIdx = 0;
795 UINT hiddenArgIdx = rank;
796 _ASSERTE(rank>0);
797
798
799#ifndef _TARGET_X86_
800 if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
801 {
802 idx++;
803 firstIdx = 1;
804 hiddenArgIdx = 0;
805 }
806#endif
807
808 ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
809 if(pcls->GetArrayElementType() == ELEMENT_TYPE_CLASS)
810 {
811 // Type Check
812 if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_SET)
813 {
814 ILCodeLabel * pTypeCheckOK = NewCodeLabel();
815
816 m_pCode->EmitLDARG(rank); // load value to store
817 m_pCode->EmitBRFALSE(pTypeCheckOK); //Storing NULL is OK
818
819 m_pCode->EmitLDARG(rank); // return param
820 m_pCode->EmitLDFLDA(tokRawData);
821 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
822 m_pCode->EmitSUB();
823 m_pCode->EmitLDIND_I(); // TypeHandle
824
825 m_pCode->EmitLoadThis();
826 m_pCode->EmitLDFLDA(tokRawData);
827 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
828 m_pCode->EmitSUB();
829 m_pCode->EmitLDIND_I(); // Array MT
830 m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
831 m_pCode->EmitADD();
832 m_pCode->EmitLDIND_I();
833
834 m_pCode->EmitCEQ();
835 m_pCode->EmitBRTRUE(pTypeCheckOK); // Same type is OK
836
837 // Call type check helper
838 m_pCode->EmitLDARG(rank);
839 m_pCode->EmitLoadThis();
840 m_pCode->EmitCALL(METHOD__STUBHELPERS__ARRAY_TYPE_CHECK,2,0);
841
842 m_pCode->EmitLabel(pTypeCheckOK);
843
844 }
845 else if(m_pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
846 {
847 // Check that the hidden param is same type
848 ILCodeLabel *pTypeCheckPassed = NewCodeLabel();
849 pTypeMismatchExceptionLabel = NewCodeLabel();
850
851 m_pCode->EmitLDARG(hiddenArgIdx); // hidden param
852 m_pCode->EmitBRFALSE(pTypeCheckPassed);
853 m_pCode->EmitLDARG(hiddenArgIdx);
854 m_pCode->EmitLDFLDA(tokRawData);
855 m_pCode->EmitLDC(offsetof(ParamTypeDesc, m_Arg) - (Object::GetOffsetOfFirstField()+2));
856 m_pCode->EmitADD();
857 m_pCode->EmitLDIND_I();
858
859 m_pCode->EmitLoadThis();
860 m_pCode->EmitLDFLDA(tokRawData);
861 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
862 m_pCode->EmitSUB();
863 m_pCode->EmitLDIND_I(); // Array MT
864 m_pCode->EmitLDC(MethodTable::GetOffsetOfArrayElementTypeHandle());
865 m_pCode->EmitADD();
866 m_pCode->EmitLDIND_I();
867
868 m_pCode->EmitCEQ();
869 m_pCode->EmitBRFALSE(pTypeMismatchExceptionLabel); // throw exception if not same
870 m_pCode->EmitLabel(pTypeCheckPassed);
871 }
872 }
873
874 if(rank == 1 && fHasLowerBounds)
875 {
876 // check if the array is SZArray.
877 m_pCode->EmitLoadThis();
878 m_pCode->EmitLDFLDA(tokRawData);
879 m_pCode->EmitLDC(Object::GetOffsetOfFirstField());
880 m_pCode->EmitSUB();
881 m_pCode->EmitLDIND_I();
882 m_pCode->EmitLDC(MethodTable::GetOffsetOfFlags());
883 m_pCode->EmitADD();
884 m_pCode->EmitLDIND_I4();
885 m_pCode->EmitLDC(MethodTable::GetIfArrayThenSzArrayFlag());
886 m_pCode->EmitAND();
887 m_pCode->EmitBRFALSE(pNotSZArray); // goto multi-dimmArray code if not szarray
888
889 // it is SZArray
890 // bounds check
891 m_pCode->EmitLoadThis();
892 m_pCode->EmitLDFLDA(tokRawData);
893 m_pCode->EmitLDC(ArrayBase::GetOffsetOfNumComponents() - Object::GetOffsetOfFirstField());
894 m_pCode->EmitADD();
895 m_pCode->EmitLDIND_I4();
896 m_pCode->EmitLDARG(firstIdx);
897 m_pCode->EmitBLE_UN(pRangeExceptionLabel);
898
899 m_pCode->EmitLoadThis();
900 m_pCode->EmitLDFLDA(tokRawData);
901 m_pCode->EmitLDC(ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField());
902 m_pCode->EmitADD();
903 m_pCode->EmitLDARG(firstIdx);
904 m_pCode->EmitBR(pCheckDone);
905 m_pCode->EmitLabel(pNotSZArray);
906 }
907
908 while(idx-- > firstIdx)
909 {
910 // Cache length
911 m_pCode->EmitLoadThis();
912 m_pCode->EmitLDFLDA(tokRawData);
913 m_pCode->EmitLDC((ArrayBase::GetBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + (idx-firstIdx)*sizeof(DWORD));
914 m_pCode->EmitADD();
915 m_pCode->EmitLDIND_I4();
916 m_pCode->EmitSTLOC(dwLengthLocalNum);
917
918 // Fetch index
919 m_pCode->EmitLDARG(idx);
920
921 if (fHasLowerBounds)
922 {
923 // Load lower bound
924 m_pCode->EmitLoadThis();
925 m_pCode->EmitLDFLDA(tokRawData);
926 m_pCode->EmitLDC((ArrayBase::GetLowerBoundsOffset(pMT) - Object::GetOffsetOfFirstField()) + (idx-firstIdx)*sizeof(DWORD));
927 m_pCode->EmitADD();
928 m_pCode->EmitLDIND_I4();
929
930 // Subtract lower bound
931 m_pCode->EmitSUB();
932 }
933
934 // Compare with length
935 m_pCode->EmitDUP();
936 m_pCode->EmitLDLOC(dwLengthLocalNum);
937 m_pCode->EmitBGE_UN(pRangeExceptionLabel1);
938
939 // Add to the running total if we have one already
940 if ((idx-firstIdx) != (rank - 1))
941 {
942 m_pCode->EmitLDLOC(dwFactorLocalNum);
943 m_pCode->EmitMUL();
944 m_pCode->EmitLDLOC(dwTotalLocalNum);
945 m_pCode->EmitADD();
946 }
947 m_pCode->EmitSTLOC(dwTotalLocalNum);
948
949 // Update factor if this is not the last iteration
950 if ((idx-firstIdx) != 0)
951 {
952 m_pCode->EmitLDLOC(dwLengthLocalNum);
953 if ((idx-firstIdx) != (rank - 1))
954 {
955 m_pCode->EmitLDLOC(dwFactorLocalNum);
956 m_pCode->EmitMUL();
957 }
958 m_pCode->EmitSTLOC(dwFactorLocalNum);
959 }
960 }
961
962 // Compute element address
963 m_pCode->EmitLoadThis();
964 m_pCode->EmitLDFLDA(tokRawData);
965 m_pCode->EmitLDC(ArrayBase::GetDataPtrOffset(pMT) - Object::GetOffsetOfFirstField());
966 m_pCode->EmitADD();
967 m_pCode->EmitLDLOC(dwTotalLocalNum);
968
969 m_pCode->EmitLabel(pCheckDone);
970
971 SIZE_T elemSize = pMT->GetComponentSize();
972 if (elemSize != 1)
973 {
974 m_pCode->EmitLDC(elemSize);
975 m_pCode->EmitMUL();
976 }
977 m_pCode->EmitADD();
978
979 LocalDesc elemType(pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType());
980
981 switch (m_pMD->GetArrayFuncIndex())
982 {
983
984 case ArrayMethodDesc::ARRAY_FUNC_GET:
985 if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
986 {
987 m_pCode->EmitLDOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
988 }
989 else
990 m_pCode->EmitLDIND_T(&elemType);
991 break;
992
993 case ArrayMethodDesc::ARRAY_FUNC_SET:
994 // Value to store into the array
995 m_pCode->EmitLDARG(rank);
996
997 if(elemType.ElementType[0]==ELEMENT_TYPE_VALUETYPE)
998 {
999 m_pCode->EmitSTOBJ(GetToken(pMT->GetApproxArrayElementTypeHandle()));
1000 }
1001 else
1002 m_pCode->EmitSTIND_T(&elemType);
1003 break;
1004
1005 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
1006 break;
1007
1008 default:
1009 _ASSERTE(!"Unknown ArrayFuncIndex");
1010 }
1011
1012 m_pCode->EmitRET();
1013
1014 m_pCode->EmitLDC(0);
1015 m_pCode->EmitLabel(pRangeExceptionLabel1); // Assumes that there is one "int" pushed on the stack
1016 m_pCode->EmitPOP();
1017
1018 mdToken tokIndexOutOfRangeCtorExcep = GetToken((MscorlibBinder::GetException(kIndexOutOfRangeException))->GetDefaultConstructor());
1019 m_pCode->EmitLabel(pRangeExceptionLabel);
1020 m_pCode->EmitNEWOBJ(tokIndexOutOfRangeCtorExcep, 0);
1021 m_pCode->EmitTHROW();
1022
1023 if(pTypeMismatchExceptionLabel != NULL)
1024 {
1025 mdToken tokTypeMismatchExcepCtor = GetToken((MscorlibBinder::GetException(kArrayTypeMismatchException))->GetDefaultConstructor());
1026
1027 m_pCode->EmitLabel(pTypeMismatchExceptionLabel);
1028 m_pCode->EmitNEWOBJ(tokTypeMismatchExcepCtor, 0);
1029 m_pCode->EmitTHROW();
1030 }
1031 }
1032};
1033
1034Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
1035{
1036 STANDARD_VM_CONTRACT;
1037
1038 ArrayOpLinker sl(pMD);
1039
1040 sl.EmitStub();
1041
1042 PCCOR_SIGNATURE pSig;
1043 DWORD cbSig;
1044 AllocMemTracker amTracker;
1045
1046 if (pMD->GetArrayFuncIndex() == ArrayMethodDesc::ARRAY_FUNC_ADDRESS)
1047 {
1048 // The stub has to have signature with explicit hidden argument instead of CORINFO_CALLCONV_PARAMTYPE.
1049 // Generate a new signature for the stub here.
1050 ((ArrayClass*)(pMD->GetMethodTable()->GetClass()))->GenerateArrayAccessorCallSig(pMD->GetMethodTable()->GetRank(),
1051 ArrayMethodDesc::ARRAY_FUNC_ADDRESS,
1052 &pSig,
1053 &cbSig,
1054 pMD->GetLoaderAllocator(),
1055 &amTracker,
1056 1);
1057 }
1058 else
1059 {
1060 pMD->GetSig(&pSig,&cbSig);
1061 }
1062
1063 amTracker.SuppressRelease();
1064
1065 static const ILStubTypes stubTypes[3] = { ILSTUB_ARRAYOP_GET, ILSTUB_ARRAYOP_SET, ILSTUB_ARRAYOP_ADDRESS };
1066
1067 _ASSERTE(pMD->GetArrayFuncIndex() <= COUNTOF(stubTypes));
1068 NDirectStubFlags arrayOpStubFlag = (NDirectStubFlags)stubTypes[pMD->GetArrayFuncIndex()];
1069
1070 MethodDesc * pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc(pMD->GetLoaderAllocator(),
1071 pMD->GetMethodTable(),
1072 arrayOpStubFlag,
1073 pMD->GetModule(),
1074 pSig, cbSig,
1075 NULL,
1076 &sl);
1077
1078 return Stub::NewStub(JitILStub(pStubMD));
1079}
1080
1081#else // FEATURE_ARRAYSTUB_AS_IL
1082//========================================================================
1083// Generates the platform-independent arrayop stub.
1084//========================================================================
1085void GenerateArrayOpScript(ArrayMethodDesc *pMD, ArrayOpScript *paos)
1086{
1087 STANDARD_VM_CONTRACT;
1088
1089 ArrayOpIndexSpec *pai = NULL;
1090 MethodTable *pMT = pMD->GetMethodTable();
1091 ArrayClass *pcls = (ArrayClass*)(pMT->GetClass());
1092
1093 // The ArrayOpScript and ArrayOpIndexSpec structs double as hash keys
1094 // for the ArrayStubCache. Thus, it's imperative that there be no
1095 // unused "pad" fields that contain unstable values.
1096 // pMT->GetRank() is bounded so the arithmetics here is safe.
1097 memset(paos, 0, sizeof(ArrayOpScript) + sizeof(ArrayOpIndexSpec) * pMT->GetRank());
1098
1099 paos->m_rank = (BYTE)(pMT->GetRank());
1100 paos->m_fHasLowerBounds = (pMT->GetInternalCorElementType() == ELEMENT_TYPE_ARRAY);
1101
1102 paos->m_ofsoffirst = ArrayBase::GetDataPtrOffset(pMT);
1103
1104 switch (pMD->GetArrayFuncIndex())
1105 {
1106 case ArrayMethodDesc::ARRAY_FUNC_GET:
1107 paos->m_op = ArrayOpScript::LOAD;
1108 break;
1109 case ArrayMethodDesc::ARRAY_FUNC_SET:
1110 paos->m_op = ArrayOpScript::STORE;
1111 break;
1112 case ArrayMethodDesc::ARRAY_FUNC_ADDRESS:
1113 paos->m_op = ArrayOpScript::LOADADDR;
1114 break;
1115 default:
1116 _ASSERTE(!"Unknown array func!");
1117 }
1118
1119 MetaSig msig(pMD);
1120 _ASSERTE(!msig.IsVarArg()); // No array signature is varargs, code below does not expect it.
1121
1122 switch (pMT->GetApproxArrayElementTypeHandle().GetInternalCorElementType())
1123 {
1124 // These are all different because of sign extension
1125
1126 case ELEMENT_TYPE_I1:
1127 paos->m_elemsize = 1;
1128 paos->m_signed = TRUE;
1129 break;
1130
1131 case ELEMENT_TYPE_BOOLEAN:
1132 case ELEMENT_TYPE_U1:
1133 paos->m_elemsize = 1;
1134 break;
1135
1136 case ELEMENT_TYPE_I2:
1137 paos->m_elemsize = 2;
1138 paos->m_signed = TRUE;
1139 break;
1140
1141 case ELEMENT_TYPE_CHAR:
1142 case ELEMENT_TYPE_U2:
1143 paos->m_elemsize = 2;
1144 break;
1145
1146 case ELEMENT_TYPE_I4:
1147 IN_WIN32(case ELEMENT_TYPE_I:)
1148 paos->m_elemsize = 4;
1149 paos->m_signed = TRUE;
1150 break;
1151
1152 case ELEMENT_TYPE_U4:
1153 IN_WIN32(case ELEMENT_TYPE_U:)
1154 IN_WIN32(case ELEMENT_TYPE_PTR:)
1155 paos->m_elemsize = 4;
1156 break;
1157
1158 case ELEMENT_TYPE_I8:
1159 IN_WIN64(case ELEMENT_TYPE_I:)
1160 paos->m_elemsize = 8;
1161 paos->m_signed = TRUE;
1162 break;
1163
1164 case ELEMENT_TYPE_U8:
1165 IN_WIN64(case ELEMENT_TYPE_U:)
1166 IN_WIN64(case ELEMENT_TYPE_PTR:)
1167 paos->m_elemsize = 8;
1168 break;
1169
1170 case ELEMENT_TYPE_R4:
1171 paos->m_elemsize = 4;
1172 paos->m_flags |= paos->ISFPUTYPE;
1173 break;
1174
1175 case ELEMENT_TYPE_R8:
1176 paos->m_elemsize = 8;
1177 paos->m_flags |= paos->ISFPUTYPE;
1178 break;
1179
1180 case ELEMENT_TYPE_SZARRAY:
1181 case ELEMENT_TYPE_ARRAY:
1182 case ELEMENT_TYPE_CLASS:
1183 case ELEMENT_TYPE_STRING:
1184 case ELEMENT_TYPE_OBJECT:
1185 paos->m_elemsize = sizeof(LPVOID);
1186 paos->m_flags |= paos->NEEDSWRITEBARRIER;
1187 if (paos->m_op != ArrayOpScript::LOAD)
1188 {
1189 paos->m_flags |= paos->NEEDSTYPECHECK;
1190 }
1191
1192 break;
1193
1194 case ELEMENT_TYPE_VALUETYPE:
1195 paos->m_elemsize = pMT->GetComponentSize();
1196 if (pMT->ContainsPointers())
1197 {
1198 paos->m_gcDesc = CGCDesc::GetCGCDescFromMT(pMT);
1199 paos->m_flags |= paos->NEEDSWRITEBARRIER;
1200 }
1201 break;
1202
1203 default:
1204 _ASSERTE(!"Unsupported Array Type!");
1205 }
1206
1207 ArgIterator argit(&msig);
1208
1209#ifdef _TARGET_X86_
1210 paos->m_cbretpop = argit.CbStackPop();
1211#endif
1212
1213 if (argit.HasRetBuffArg())
1214 {
1215 paos->m_flags |= ArrayOpScript::HASRETVALBUFFER;
1216 paos->m_fRetBufLoc = argit.GetRetBuffArgOffset();
1217 }
1218
1219 if (paos->m_op == ArrayOpScript::LOADADDR)
1220 {
1221 paos->m_typeParamOffs = argit.GetParamTypeArgOffset();
1222 }
1223
1224 for (UINT idx = 0; idx < paos->m_rank; idx++)
1225 {
1226 pai = (ArrayOpIndexSpec*)(paos->GetArrayOpIndexSpecs() + idx);
1227
1228 pai->m_idxloc = argit.GetNextOffset();
1229 pai->m_lboundofs = paos->m_fHasLowerBounds ? (UINT32) (ArrayBase::GetLowerBoundsOffset(pMT) + idx*sizeof(DWORD)) : 0;
1230 pai->m_lengthofs = ArrayBase::GetBoundsOffset(pMT) + idx*sizeof(DWORD);
1231 }
1232
1233 if (paos->m_op == paos->STORE)
1234 {
1235 paos->m_fValLoc = argit.GetNextOffset();
1236 }
1237}
1238
1239//---------------------------------------------------------
1240// Cache for array stubs
1241//---------------------------------------------------------
1242class ArrayStubCache : public StubCacheBase
1243{
1244 virtual void CompileStub(const BYTE *pRawStub,
1245 StubLinker *psl);
1246 virtual UINT Length(const BYTE *pRawStub);
1247
1248public:
1249 static ArrayStubCache * GetArrayStubCache()
1250 {
1251 STANDARD_VM_CONTRACT;
1252
1253 static ArrayStubCache * s_pArrayStubCache = NULL;
1254
1255 if (s_pArrayStubCache == NULL)
1256 {
1257 ArrayStubCache * pArrayStubCache = new ArrayStubCache();
1258 if (FastInterlockCompareExchangePointer(&s_pArrayStubCache, pArrayStubCache, NULL) != NULL)
1259 delete pArrayStubCache;
1260 }
1261
1262 return s_pArrayStubCache;
1263 }
1264};
1265
1266Stub *GenerateArrayOpStub(ArrayMethodDesc* pMD)
1267{
1268 STANDARD_VM_CONTRACT;
1269
1270 MethodTable *pMT = pMD->GetMethodTable();
1271
1272 ArrayOpScript *paos = (ArrayOpScript*)_alloca(sizeof(ArrayOpScript) + sizeof(ArrayOpIndexSpec) * pMT->GetRank());
1273
1274 GenerateArrayOpScript(pMD, paos);
1275
1276 Stub *pArrayOpStub;
1277 pArrayOpStub = ArrayStubCache::GetArrayStubCache()->Canonicalize((const BYTE *)paos);
1278 if (pArrayOpStub == NULL)
1279 COMPlusThrowOM();
1280
1281 return pArrayOpStub;
1282}
1283
1284void ArrayStubCache::CompileStub(const BYTE *pRawStub,
1285 StubLinker *psl)
1286{
1287 STANDARD_VM_CONTRACT;
1288
1289 ((CPUSTUBLINKER*)psl)->EmitArrayOpStub((ArrayOpScript*)pRawStub);
1290}
1291
1292UINT ArrayStubCache::Length(const BYTE *pRawStub)
1293{
1294 LIMITED_METHOD_CONTRACT;
1295 STATIC_CONTRACT_SO_TOLERANT;
1296 return ((ArrayOpScript*)pRawStub)->Length();
1297}
1298
1299#endif // FEATURE_ARRAYSTUB_AS_IL
1300
1301#endif // CROSSGEN_COMPILE
1302
1303//---------------------------------------------------------------------
1304// This method returns TRUE if pInterfaceMT could be one of the interfaces
1305// that are implicitly implemented by SZArrays
1306
1307BOOL IsImplicitInterfaceOfSZArray(MethodTable *pInterfaceMT)
1308{
1309 LIMITED_METHOD_CONTRACT;
1310 PRECONDITION(pInterfaceMT->IsInterface());
1311
1312 // Is target interface Anything<T> in mscorlib?
1313 if (!pInterfaceMT->HasInstantiation() || !pInterfaceMT->GetModule()->IsSystem())
1314 return FALSE;
1315
1316 unsigned rid = pInterfaceMT->GetTypeDefRid();
1317
1318 // Is target interface IList<T> or one of its ancestors, or IReadOnlyList<T>?
1319 return (rid == MscorlibBinder::GetExistingClass(CLASS__ILISTGENERIC)->GetTypeDefRid() ||
1320 rid == MscorlibBinder::GetExistingClass(CLASS__ICOLLECTIONGENERIC)->GetTypeDefRid() ||
1321 rid == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)->GetTypeDefRid() ||
1322 rid == MscorlibBinder::GetExistingClass(CLASS__IREADONLYCOLLECTIONGENERIC)->GetTypeDefRid() ||
1323 rid == MscorlibBinder::GetExistingClass(CLASS__IREADONLYLISTGENERIC)->GetTypeDefRid());
1324}
1325
1326//---------------------------------------------------------------------
1327// Check if arrays supports certain interfaces that don't appear in the base interface
1328// list. It does not check the base interfaces themselves - you must do that
1329// separately.
1330//---------------------------------------------------------------------
1331BOOL ArraySupportsBizarreInterface(ArrayTypeDesc *pArrayTypeDesc, MethodTable *pInterfaceMT)
1332{
1333 CONTRACTL
1334 {
1335 THROWS;
1336 GC_TRIGGERS;
1337 INJECT_FAULT(COMPlusThrowOM(););
1338
1339 PRECONDITION(pInterfaceMT->IsInterface());
1340 PRECONDITION(pArrayTypeDesc->IsArray());
1341 }
1342 CONTRACTL_END
1343
1344#ifdef _DEBUG
1345 MethodTable *pArrayMT = pArrayTypeDesc->GetMethodTable();
1346 _ASSERTE(pArrayMT->IsArray());
1347 _ASSERTE(pArrayMT->IsRestored());
1348#endif
1349
1350 // IList<T> & IReadOnlyList<T> only supported for SZ_ARRAYS
1351 if (pArrayTypeDesc->GetInternalCorElementType() != ELEMENT_TYPE_SZARRAY)
1352 return FALSE;
1353
1354 ClassLoader::EnsureLoaded(pInterfaceMT, CLASS_DEPENDENCIES_LOADED);
1355
1356 if (!IsImplicitInterfaceOfSZArray(pInterfaceMT))
1357 return FALSE;
1358
1359 return TypeDesc::CanCastParam(pArrayTypeDesc->GetTypeParam(), pInterfaceMT->GetInstantiation()[0], NULL);
1360}
1361
1362//----------------------------------------------------------------------------------
1363// Calls to (IList<T>)(array).Meth are actually implemented by SZArrayHelper.Meth<T>
1364// This workaround exists for two reasons:
1365//
1366// - For working set reasons, we don't want insert these methods in the array hierachy
1367// in the normal way.
1368// - For platform and devtime reasons, we still want to use the C# compiler to generate
1369// the method bodies.
1370//
1371// (Though it's questionable whether any devtime was saved.)
1372//
1373// This method takes care of the mapping between the two. Give it a method
1374// IList<T>.Meth, and it will return SZArrayHelper.Meth<T>.
1375//----------------------------------------------------------------------------------
1376MethodDesc* GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod(MethodDesc *pItfcMeth, TypeHandle theT)
1377{
1378 CONTRACTL
1379 {
1380 THROWS;
1381 GC_TRIGGERS;
1382 INJECT_FAULT(COMPlusThrowOM());
1383 }
1384 CONTRACTL_END
1385
1386 int slot = pItfcMeth->GetSlot();
1387
1388 // We need to pick the right starting method depending on the depth of the inheritance chain
1389 static const BinderMethodID startingMethod[] = {
1390 METHOD__SZARRAYHELPER__GETENUMERATOR, // First method of IEnumerable`1
1391 METHOD__SZARRAYHELPER__GET_COUNT, // First method of ICollection`1/IReadOnlyCollection`1
1392 METHOD__SZARRAYHELPER__GET_ITEM // First method of IList`1/IReadOnlyList`1
1393 };
1394
1395 // Subtract one for the non-generic IEnumerable that the generic enumerable inherits from
1396 unsigned int inheritanceDepth = pItfcMeth->GetMethodTable()->GetNumInterfaces() - 1;
1397 PREFIX_ASSUME(0 <= inheritanceDepth && inheritanceDepth < NumItems(startingMethod));
1398
1399 MethodDesc *pGenericImplementor = MscorlibBinder::GetMethod((BinderMethodID)(startingMethod[inheritanceDepth] + slot));
1400
1401 // The most common reason for this assert is that the order of the SZArrayHelper methods in
1402 // mscorlib.h does not match the order they are implemented on the generic interfaces.
1403 _ASSERTE(pGenericImplementor == MemberLoader::FindMethodByName(g_pSZArrayHelperClass, pItfcMeth->GetName()));
1404
1405 // OPTIMIZATION: For any method other than GetEnumerator(), we can safely substitute
1406 // "Object" for reference-type theT's. This causes fewer methods to be instantiated.
1407 if (startingMethod[inheritanceDepth] != METHOD__SZARRAYHELPER__GETENUMERATOR &&
1408 !theT.IsValueType())
1409 {
1410 theT = TypeHandle(g_pObjectClass);
1411 }
1412
1413 MethodDesc *pActualImplementor = MethodDesc::FindOrCreateAssociatedMethodDesc(pGenericImplementor,
1414 g_pSZArrayHelperClass,
1415 FALSE,
1416 Instantiation(&theT, 1),
1417 FALSE // allowInstParam
1418 );
1419 _ASSERTE(pActualImplementor);
1420 return pActualImplementor;
1421}
1422#endif // DACCESS_COMPILE
1423
1424#ifdef _MSC_VER
1425#pragma warning(pop)
1426#pragma warning(disable:4244)
1427#endif // _MSC_VER: warning C4244
1428