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 | /*****************************************************************************************/ |
45 | LPCUTF8 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 | /*****************************************************************************************/ |
63 | DWORD ArrayMethodDesc::GetAttrs() |
64 | { |
65 | LIMITED_METHOD_CONTRACT; |
66 | return (GetArrayFuncIndex() >= ARRAY_FUNC_CTOR) ? (mdPublic | mdRTSpecialName) : mdPublic; |
67 | } |
68 | |
69 | /*****************************************************************************************/ |
70 | CorInfoIntrinsics 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 | |
95 | VOID 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 | // |
217 | void 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 | /*****************************************************************************************/ |
246 | MethodTable* 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 | |
760 | class ArrayOpLinker : public ILStubLinker |
761 | { |
762 | ILCodeStream * m_pCode; |
763 | ArrayMethodDesc * m_pMD; |
764 | |
765 | SigTypeContext m_emptyContext; |
766 | |
767 | public: |
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 | |
1034 | Stub *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 | //======================================================================== |
1085 | void 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 | //--------------------------------------------------------- |
1242 | class ArrayStubCache : public StubCacheBase |
1243 | { |
1244 | virtual void CompileStub(const BYTE *pRawStub, |
1245 | StubLinker *psl); |
1246 | virtual UINT Length(const BYTE *pRawStub); |
1247 | |
1248 | public: |
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 | |
1266 | Stub *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 | |
1284 | void ArrayStubCache::CompileStub(const BYTE *pRawStub, |
1285 | StubLinker *psl) |
1286 | { |
1287 | STANDARD_VM_CONTRACT; |
1288 | |
1289 | ((CPUSTUBLINKER*)psl)->EmitArrayOpStub((ArrayOpScript*)pRawStub); |
1290 | } |
1291 | |
1292 | UINT 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 | |
1307 | BOOL 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 | //--------------------------------------------------------------------- |
1331 | BOOL 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 | //---------------------------------------------------------------------------------- |
1376 | MethodDesc* 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 | |