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//
5// File: genericdict.cpp
6//
7
8//
9// WARNING: Do NOT turn try to save dictionary slots except in the
10// hardbind case. Saving further dictionary slots can lead
11// to ComputeNeedsRestore returning TRUE for the dictionary and
12// the associated method table (though of course only if some
13// entries in the dictionary are prepopulated). However at
14// earlier stages in the NGEN, code may have been compiled
15// under the assumption that ComputeNeedsRestore was
16// FALSE for the assocaited method table, and indeed this result
17// may have been cached in the ComputeNeedsRestore
18// for the MethodTable. Thus the combination of populating
19// the dictionary and saving further dictionary slots could lead
20// to inconsistencies and unsoundnesses in compilation.
21//
22
23//
24// ============================================================================
25
26#include "common.h"
27#include "genericdict.h"
28#include "typestring.h"
29#include "field.h"
30#include "typectxt.h"
31#include "virtualcallstub.h"
32#include "sigbuilder.h"
33#include "compile.h"
34
35#ifndef DACCESS_COMPILE
36
37//---------------------------------------------------------------------------------------
38//
39//static
40DictionaryLayout *
41DictionaryLayout::Allocate(
42 WORD numSlots,
43 LoaderAllocator * pAllocator,
44 AllocMemTracker * pamTracker)
45{
46 CONTRACT(DictionaryLayout*)
47 {
48 THROWS;
49 GC_NOTRIGGER;
50 INJECT_FAULT(COMPlusThrowOM(););
51 PRECONDITION(CheckPointer(pAllocator));
52 PRECONDITION(numSlots > 0);
53 POSTCONDITION(CheckPointer(RETVAL));
54 }
55 CONTRACT_END
56
57 S_SIZE_T bytes = S_SIZE_T(sizeof(DictionaryLayout)) + S_SIZE_T(sizeof(DictionaryEntryLayout)) * S_SIZE_T(numSlots-1);
58
59 TaggedMemAllocPtr ptr = pAllocator->GetLowFrequencyHeap()->AllocMem(bytes);
60
61 if (pamTracker != NULL)
62 pamTracker->Track(ptr);
63
64 DictionaryLayout * pD = (DictionaryLayout *)(void *)ptr;
65
66 // When bucket spills we'll allocate another layout structure
67 pD->m_pNext = NULL;
68
69 // This is the number of slots excluding the type parameters
70 pD->m_numSlots = numSlots;
71
72 RETURN pD;
73} // DictionaryLayout::Allocate
74
75#endif //!DACCESS_COMPILE
76
77//---------------------------------------------------------------------------------------
78//
79// Count the number of bytes that are required by the first bucket in a dictionary with the specified layout
80//
81//static
82DWORD
83DictionaryLayout::GetFirstDictionaryBucketSize(
84 DWORD numGenericArgs,
85 PTR_DictionaryLayout pDictLayout)
86{
87 LIMITED_METHOD_DAC_CONTRACT;
88 PRECONDITION(numGenericArgs > 0);
89 PRECONDITION(CheckPointer(pDictLayout, NULL_OK));
90
91 DWORD bytes = numGenericArgs * sizeof(TypeHandle);
92 if (pDictLayout != NULL)
93 bytes += pDictLayout->m_numSlots * sizeof(void*);
94
95 return bytes;
96}
97
98#ifndef DACCESS_COMPILE
99//---------------------------------------------------------------------------------------
100//
101// Find a token in the dictionary layout and return the offsets of indirections
102// required to get to its slot in the actual dictionary
103//
104// NOTE: We will currently never return more than one indirection. We don't
105// cascade dictionaries but we will record overflows in the dictionary layout
106// (and cascade that accordingly) so we can prepopulate the overflow hash in
107// reliability scenarios.
108//
109// Optimize the case of a token being !i (for class dictionaries) or !!i (for method dictionaries)
110//
111/* static */
112BOOL
113DictionaryLayout::FindTokenWorker(LoaderAllocator * pAllocator,
114 DWORD numGenericArgs,
115 DictionaryLayout * pDictLayout,
116 CORINFO_RUNTIME_LOOKUP * pResult,
117 SigBuilder * pSigBuilder,
118 BYTE * pSig,
119 DWORD cbSig,
120 int nFirstOffset,
121 DictionaryEntrySignatureSource signatureSource,
122 WORD * pSlotOut)
123{
124 CONTRACTL
125 {
126 STANDARD_VM_CHECK;
127 PRECONDITION(numGenericArgs > 0);
128 PRECONDITION(CheckPointer(pDictLayout));
129 PRECONDITION(CheckPointer(pSlotOut));
130 PRECONDITION(CheckPointer(pSig));
131 PRECONDITION((pSigBuilder == NULL && cbSig == -1) || (CheckPointer(pSigBuilder) && cbSig > 0));
132 }
133 CONTRACTL_END
134
135 BOOL isFirstBucket = TRUE;
136
137 // First bucket also contains type parameters
138 _ASSERTE(FitsIn<WORD>(numGenericArgs));
139 WORD slot = static_cast<WORD>(numGenericArgs);
140 for (;;)
141 {
142 for (DWORD iSlot = 0; iSlot < pDictLayout->m_numSlots; iSlot++)
143 {
144 RetryMatch:
145 BYTE * pCandidate = (BYTE *)pDictLayout->m_slots[iSlot].m_signature;
146 if (pCandidate != NULL)
147 {
148 bool signaturesMatch = false;
149
150 if (pSigBuilder != NULL)
151 {
152 // JIT case: compare signatures by comparing the bytes in them. We exclude
153 // any ReadyToRun signatures from the JIT case.
154
155 if (pDictLayout->m_slots[iSlot].m_signatureSource != FromReadyToRunImage)
156 {
157 // Compare the signatures. We do not need to worry about the size of pCandidate.
158 // As long as we are comparing one byte at a time we are guaranteed to not overrun.
159 DWORD j;
160 for (j = 0; j < cbSig; j++)
161 {
162 if (pCandidate[j] != pSig[j])
163 break;
164 }
165 signaturesMatch = (j == cbSig);
166 }
167 }
168 else
169 {
170 // ReadyToRun case: compare signatures by comparing their pointer values
171 signaturesMatch = (pCandidate == pSig);
172 }
173
174 // We've found it
175 if (signaturesMatch)
176 {
177 pResult->signature = pDictLayout->m_slots[iSlot].m_signature;
178
179 // We don't store entries outside the first bucket in the layout in the dictionary (they'll be cached in a hash
180 // instead).
181 if (!isFirstBucket)
182 {
183 return FALSE;
184 }
185 _ASSERTE(FitsIn<WORD>(nFirstOffset + 1));
186 pResult->indirections = static_cast<WORD>(nFirstOffset + 1);
187 pResult->offsets[nFirstOffset] = slot * sizeof(DictionaryEntry);
188 *pSlotOut = slot;
189 return TRUE;
190 }
191 }
192 // If we hit an empty slot then there's no more so use it
193 else
194 {
195 {
196 BaseDomain::LockHolder lh(pAllocator->GetDomain());
197
198 if (pDictLayout->m_slots[iSlot].m_signature != NULL)
199 goto RetryMatch;
200
201 PVOID pResultSignature = pSig;
202
203 if (pSigBuilder != NULL)
204 {
205 pSigBuilder->AppendData(isFirstBucket ? slot : 0);
206
207 DWORD cbNewSig;
208 PVOID pNewSig = pSigBuilder->GetSignature(&cbNewSig);
209
210 pResultSignature = pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbNewSig));
211 memcpy(pResultSignature, pNewSig, cbNewSig);
212 }
213
214 *EnsureWritablePages(&(pDictLayout->m_slots[iSlot].m_signature)) = pResultSignature;
215 *EnsureWritablePages(&(pDictLayout->m_slots[iSlot].m_signatureSource)) = signatureSource;
216 }
217
218 pResult->signature = pDictLayout->m_slots[iSlot].m_signature;
219
220 // Again, we only store entries in the first layout bucket in the dictionary.
221 if (!isFirstBucket)
222 {
223 return FALSE;
224 }
225 _ASSERTE(FitsIn<WORD>(nFirstOffset + 1));
226 pResult->indirections = static_cast<WORD>(nFirstOffset + 1);
227 pResult->offsets[nFirstOffset] = slot * sizeof(DictionaryEntry);
228 *pSlotOut = slot;
229 return TRUE;
230 }
231 slot++;
232 }
233
234 // If we've reached the end of the chain we need to allocate another bucket. Make the pointer update carefully to avoid
235 // orphaning a bucket in a race. We leak the loser in such a race (since the allocation comes from the loader heap) but both
236 // the race and the overflow should be very rare.
237 if (pDictLayout->m_pNext == NULL)
238 FastInterlockCompareExchangePointer(EnsureWritablePages(&(pDictLayout->m_pNext)), Allocate(4, pAllocator, NULL), 0);
239
240 pDictLayout = pDictLayout->m_pNext;
241 isFirstBucket = FALSE;
242 }
243} // DictionaryLayout::FindToken
244
245/* static */
246BOOL
247DictionaryLayout::FindToken(LoaderAllocator * pAllocator,
248 DWORD numGenericArgs,
249 DictionaryLayout * pDictLayout,
250 CORINFO_RUNTIME_LOOKUP * pResult,
251 SigBuilder * pSigBuilder,
252 int nFirstOffset,
253 DictionaryEntrySignatureSource signatureSource)
254{
255 WRAPPER_NO_CONTRACT;
256
257 DWORD cbSig;
258 BYTE * pSig = (BYTE *)pSigBuilder->GetSignature(&cbSig);
259
260 WORD slotDummy;
261 return FindTokenWorker(pAllocator, numGenericArgs, pDictLayout, pResult, pSigBuilder, pSig, cbSig, nFirstOffset, signatureSource, &slotDummy);
262}
263
264/* static */
265BOOL
266DictionaryLayout::FindToken(LoaderAllocator * pAllocator,
267 DWORD numGenericArgs,
268 DictionaryLayout * pDictLayout,
269 CORINFO_RUNTIME_LOOKUP * pResult,
270 BYTE * signature,
271 int nFirstOffset,
272 DictionaryEntrySignatureSource signatureSource,
273 WORD * pSlotOut)
274{
275 WRAPPER_NO_CONTRACT;
276
277 return FindTokenWorker(pAllocator, numGenericArgs, pDictLayout, pResult, NULL, signature, -1, nFirstOffset, signatureSource, pSlotOut);
278}
279
280#endif //!DACCESS_COMPILE
281
282//---------------------------------------------------------------------------------------
283//
284DWORD
285DictionaryLayout::GetMaxSlots()
286{
287 LIMITED_METHOD_CONTRACT;
288 return m_numSlots;
289}
290
291//---------------------------------------------------------------------------------------
292//
293DWORD
294DictionaryLayout::GetNumUsedSlots()
295{
296 LIMITED_METHOD_CONTRACT;
297
298 DWORD numUsedSlots = 0;
299 for (DWORD i = 0; i < m_numSlots; i++)
300 {
301 if (GetEntryLayout(i)->m_signature != NULL)
302 numUsedSlots++;
303 }
304 return numUsedSlots;
305}
306
307
308//---------------------------------------------------------------------------------------
309//
310DictionaryEntryKind
311DictionaryEntryLayout::GetKind()
312{
313 STANDARD_VM_CONTRACT;
314
315 if (m_signature == NULL)
316 return EmptySlot;
317
318 SigPointer ptr((PCCOR_SIGNATURE)dac_cast<TADDR>(m_signature));
319
320 ULONG kind; // DictionaryEntryKind
321 IfFailThrow(ptr.GetData(&kind));
322
323 return (DictionaryEntryKind)kind;
324}
325
326#ifndef DACCESS_COMPILE
327#ifdef FEATURE_NATIVE_IMAGE_GENERATION
328
329//---------------------------------------------------------------------------------------
330//
331DWORD
332DictionaryLayout::GetObjectSize()
333{
334 LIMITED_METHOD_CONTRACT;
335 return sizeof(DictionaryLayout) + sizeof(DictionaryEntryLayout) * (m_numSlots-1);
336}
337
338//---------------------------------------------------------------------------------------
339//
340// Save the dictionary layout for prejitting
341//
342void
343DictionaryLayout::Save(
344 DataImage * image)
345{
346 STANDARD_VM_CONTRACT;
347
348 DictionaryLayout *pDictLayout = this;
349
350 while (pDictLayout)
351 {
352 image->StoreStructure(pDictLayout, pDictLayout->GetObjectSize(), DataImage::ITEM_DICTIONARY_LAYOUT);
353 pDictLayout = pDictLayout->m_pNext;
354 }
355
356}
357
358//---------------------------------------------------------------------------------------
359//
360// Save the dictionary layout for prejitting
361//
362void
363DictionaryLayout::Trim()
364{
365 CONTRACTL
366 {
367 THROWS;
368 GC_NOTRIGGER;
369 }
370 CONTRACTL_END;
371
372 // Only the last bucket in the chain may have unused entries
373 DictionaryLayout *pDictLayout = this;
374 while (pDictLayout->m_pNext)
375 pDictLayout = pDictLayout->m_pNext;
376
377 // Trim down the size to what's actually used
378 DWORD dwSlots = pDictLayout->GetNumUsedSlots();
379 _ASSERTE(FitsIn<WORD>(dwSlots));
380 *EnsureWritablePages(&pDictLayout->m_numSlots) = static_cast<WORD>(dwSlots);
381
382}
383
384//---------------------------------------------------------------------------------------
385//
386// Fixup pointers in the dictionary layout for prejitting
387//
388void
389DictionaryLayout::Fixup(
390 DataImage * image,
391 BOOL fMethod)
392{
393 STANDARD_VM_CONTRACT;
394
395 DictionaryLayout *pDictLayout = this;
396
397 while (pDictLayout)
398 {
399 for (DWORD i = 0; i < pDictLayout->m_numSlots; i++)
400 {
401 PVOID signature = pDictLayout->m_slots[i].m_signature;
402 if (signature != NULL)
403 {
404 image->FixupFieldToNode(pDictLayout, (BYTE *)&pDictLayout->m_slots[i].m_signature - (BYTE *)pDictLayout,
405 image->GetGenericSignature(signature, fMethod));
406 }
407 }
408 image->FixupPointerField(pDictLayout, offsetof(DictionaryLayout, m_pNext));
409 pDictLayout = pDictLayout->m_pNext;
410 }
411}
412
413//---------------------------------------------------------------------------------------
414//
415// Fixup pointers in the actual dictionary, including the type arguments. Delete entries
416// that are expensive or difficult to restore.
417//
418void
419Dictionary::Fixup(
420 DataImage * image,
421 BOOL canSaveInstantiation,
422 BOOL canSaveSlots,
423 DWORD numGenericArgs,
424 Module * pModule,
425 DictionaryLayout * pDictLayout)
426{
427 STANDARD_VM_CONTRACT;
428
429 // First fixup the type handles in the instantiation itself
430 FixupPointer<TypeHandle> *pInst = GetInstantiation();
431 for (DWORD j = 0; j < numGenericArgs; j++)
432 {
433 if (canSaveInstantiation)
434 {
435 image->FixupTypeHandlePointer(pInst, &pInst[j]);
436 }
437 else
438 {
439 image->ZeroPointerField(AsPtr(), j * sizeof(DictionaryEntry));
440 }
441 }
442
443 // Now traverse the remaining slots
444 if (pDictLayout != NULL)
445 {
446 for (DWORD i = 0; i < pDictLayout->m_numSlots; i++)
447 {
448 int slotOffset = (numGenericArgs + i) * sizeof(DictionaryEntry);
449
450 // First check if we can simply hardbind to a prerestored object
451 DictionaryEntryLayout *pLayout = pDictLayout->GetEntryLayout(i);
452 switch (pLayout->GetKind())
453 {
454 case TypeHandleSlot:
455 case DeclaringTypeHandleSlot:
456 if (canSaveSlots &&
457 !IsSlotEmpty(numGenericArgs,i) &&
458 image->CanPrerestoreEagerBindToTypeHandle(GetTypeHandleSlot(numGenericArgs, i), NULL) &&
459 image->CanHardBindToZapModule(GetTypeHandleSlot(numGenericArgs, i).GetLoaderModule()))
460 {
461 image->HardBindTypeHandlePointer(AsPtr(), slotOffset);
462 }
463 else
464 {
465 // Otherwise just zero the slot
466 image->ZeroPointerField(AsPtr(), slotOffset);
467 }
468 break;
469 case MethodDescSlot:
470 if (canSaveSlots &&
471 !IsSlotEmpty(numGenericArgs,i) &&
472 image->CanPrerestoreEagerBindToMethodDesc(GetMethodDescSlot(numGenericArgs,i), NULL) &&
473 image->CanHardBindToZapModule(GetMethodDescSlot(numGenericArgs,i)->GetLoaderModule()))
474 {
475 image->FixupPointerField(AsPtr(), slotOffset);
476 }
477 else
478 {
479 // Otherwise just zero the slot
480 image->ZeroPointerField(AsPtr(), slotOffset);
481 }
482 break;
483 case FieldDescSlot:
484 if (canSaveSlots &&
485 !IsSlotEmpty(numGenericArgs,i) &&
486 image->CanEagerBindToFieldDesc(GetFieldDescSlot(numGenericArgs,i)) &&
487 image->CanHardBindToZapModule(GetFieldDescSlot(numGenericArgs,i)->GetLoaderModule()))
488 {
489 image->FixupPointerField(AsPtr(), slotOffset);
490 }
491 else
492 {
493 // Otherwise just zero the slot
494 image->ZeroPointerField(AsPtr(), slotOffset);
495 }
496 break;
497 default:
498 // <TODO> Method entry points are currently not saved </TODO>
499 // <TODO> Stub dispatch slots are currently not saved </TODO>
500 // Otherwise just zero the slot
501 image->ZeroPointerField(AsPtr(), slotOffset);
502 }
503 }
504 }
505} // Dictionary::Fixup
506
507//---------------------------------------------------------------------------------------
508//
509BOOL
510Dictionary::IsWriteable(
511 DataImage * image,
512 BOOL canSaveSlots,
513 DWORD numGenericArgs, // Must be non-zero
514 Module * pModule, // module of the generic code
515 DictionaryLayout * pDictLayout)
516{
517 STANDARD_VM_CONTRACT;
518
519 // Traverse dictionary slots
520 if (pDictLayout != NULL)
521 {
522 for (DWORD i = 0; i < pDictLayout->m_numSlots; i++)
523 {
524 // First check if we can simply hardbind to a prerestored object
525 DictionaryEntryLayout *pLayout = pDictLayout->GetEntryLayout(i);
526 switch (pLayout->GetKind())
527 {
528 case TypeHandleSlot:
529 case DeclaringTypeHandleSlot:
530 if (canSaveSlots &&
531 !IsSlotEmpty(numGenericArgs,i) &&
532 image->CanPrerestoreEagerBindToTypeHandle(GetTypeHandleSlot(numGenericArgs, i), NULL) &&
533 image->CanHardBindToZapModule(GetTypeHandleSlot(numGenericArgs, i).GetLoaderModule()))
534 {
535 // do nothing
536 }
537 else
538 {
539 return TRUE;
540 }
541 break;
542 case MethodDescSlot:
543 if (canSaveSlots &&
544 !IsSlotEmpty(numGenericArgs,i) &&
545 image->CanPrerestoreEagerBindToMethodDesc(GetMethodDescSlot(numGenericArgs,i), NULL) &&
546 image->CanHardBindToZapModule(GetMethodDescSlot(numGenericArgs,i)->GetLoaderModule()))
547 {
548 // do nothing
549 }
550 else
551 {
552 return TRUE;
553 }
554 break;
555 case FieldDescSlot:
556 if (canSaveSlots &&
557 !IsSlotEmpty(numGenericArgs,i) &&
558 image->CanEagerBindToFieldDesc(GetFieldDescSlot(numGenericArgs,i)) &&
559 image->CanHardBindToZapModule(GetFieldDescSlot(numGenericArgs,i)->GetLoaderModule()))
560 {
561 // do nothing
562 }
563 else
564 {
565 return TRUE;
566 }
567 break;
568 default:
569 // <TODO> Method entry points are currently not saved </TODO>
570 // <TODO> Stub dispatch slots are currently not saved </TODO>
571 return TRUE;
572 }
573 }
574 }
575
576 return FALSE;
577} // Dictionary::IsWriteable
578
579//---------------------------------------------------------------------------------------
580//
581BOOL
582Dictionary::ComputeNeedsRestore(
583 DataImage * image,
584 TypeHandleList * pVisited,
585 DWORD numGenericArgs)
586{
587 STANDARD_VM_CONTRACT;
588
589 // First check the type handles in the instantiation itself
590 FixupPointer<TypeHandle> *inst = GetInstantiation();
591 for (DWORD j = 0; j < numGenericArgs; j++)
592 {
593 if (!image->CanPrerestoreEagerBindToTypeHandle(inst[j].GetValue(), pVisited))
594 return TRUE;
595 }
596
597 // Unless prepopulating we don't need to check the entries
598 // of the dictionary because if we can't
599 // hardbind to them we just zero the dictionary entry and recover
600 // it on demand.
601
602 return FALSE;
603}
604#endif //FEATURE_NATIVE_IMAGE_GENERATION
605
606#ifdef FEATURE_PREJIT
607//---------------------------------------------------------------------------------------
608//
609void
610Dictionary::Restore(
611 DWORD numGenericArgs,
612 ClassLoadLevel level)
613{
614 CONTRACTL
615 {
616 THROWS;
617 GC_TRIGGERS;
618 INSTANCE_CHECK;
619 }
620 CONTRACTL_END
621
622 // First restore the type handles in the instantiation itself
623 FixupPointer<TypeHandle> *inst = GetInstantiation();
624 for (DWORD j = 0; j < numGenericArgs; j++)
625 {
626 Module::RestoreTypeHandlePointer(&inst[j], NULL, level);
627 }
628
629 // We don't restore the remainder of the dictionary - see
630 // long comment at the start of this file as to why
631}
632#endif // FEATURE_PREJIT
633
634//---------------------------------------------------------------------------------------
635//
636DictionaryEntry
637Dictionary::PopulateEntry(
638 MethodDesc * pMD,
639 MethodTable * pMT,
640 LPVOID signature,
641 BOOL nonExpansive,
642 DictionaryEntry ** ppSlot,
643 DWORD dictionaryIndexAndSlot, /* = -1 */
644 Module * pModule /* = NULL */)
645{
646 CONTRACTL {
647 THROWS;
648 GC_TRIGGERS;
649 } CONTRACTL_END;
650
651 CORINFO_GENERIC_HANDLE result = NULL;
652 Dictionary * pDictionary = NULL;
653 *ppSlot = NULL;
654
655 bool isReadyToRunModule = (pModule != NULL && pModule->IsReadyToRun());
656
657 ZapSig::Context zapSigContext(NULL, NULL, ZapSig::NormalTokens);
658 ZapSig::Context * pZapSigContext = NULL;
659
660 ULONG kind = DictionaryEntryKind::EmptySlot;
661
662 SigPointer ptr((PCCOR_SIGNATURE)signature);
663
664 if (isReadyToRunModule)
665 {
666 PCCOR_SIGNATURE pBlob = (PCCOR_SIGNATURE)signature;
667
668 BYTE fixupKind = *pBlob++;
669
670 Module * pInfoModule = pModule;
671 if (fixupKind & ENCODE_MODULE_OVERRIDE)
672 {
673 DWORD moduleIndex = CorSigUncompressData(pBlob);
674 pInfoModule = pModule->GetModuleFromIndex(moduleIndex);
675 fixupKind &= ~ENCODE_MODULE_OVERRIDE;
676 }
677
678 _ASSERTE(fixupKind == ENCODE_DICTIONARY_LOOKUP_THISOBJ ||
679 fixupKind == ENCODE_DICTIONARY_LOOKUP_TYPE ||
680 fixupKind == ENCODE_DICTIONARY_LOOKUP_METHOD);
681
682 if (fixupKind == ENCODE_DICTIONARY_LOOKUP_THISOBJ)
683 {
684 SigPointer p(pBlob);
685 p.SkipExactlyOne();
686 pBlob = p.GetPtr();
687 }
688
689 CORCOMPILE_FIXUP_BLOB_KIND signatureKind = (CORCOMPILE_FIXUP_BLOB_KIND)CorSigUncompressData(pBlob);
690 switch (signatureKind)
691 {
692 case ENCODE_DECLARINGTYPE_HANDLE: kind = DeclaringTypeHandleSlot; break;
693 case ENCODE_TYPE_HANDLE: kind = TypeHandleSlot; break;
694 case ENCODE_FIELD_HANDLE: kind = FieldDescSlot; break;
695 case ENCODE_METHOD_HANDLE: kind = MethodDescSlot; break;
696 case ENCODE_METHOD_ENTRY: kind = MethodEntrySlot; break;
697 case ENCODE_VIRTUAL_ENTRY: kind = DispatchStubAddrSlot; break;
698
699 default:
700 _ASSERTE(!"Unexpected CORCOMPILE_FIXUP_BLOB_KIND");
701 ThrowHR(COR_E_BADIMAGEFORMAT);
702 }
703
704 ptr = SigPointer(pBlob);
705
706 zapSigContext = ZapSig::Context(pInfoModule, pModule, ZapSig::NormalTokens);
707 pZapSigContext = &zapSigContext;
708 }
709 else
710 {
711 ptr = SigPointer((PCCOR_SIGNATURE)signature);
712 IfFailThrow(ptr.GetData(&kind));
713
714 Module * pContainingZapModule = ExecutionManager::FindZapModule(dac_cast<TADDR>(signature));
715
716 zapSigContext = ZapSig::Context(MscorlibBinder::GetModule(), (void *)pContainingZapModule, ZapSig::NormalTokens);
717 pZapSigContext = (pContainingZapModule != NULL) ? &zapSigContext : NULL;
718 }
719
720 Module * pLookupModule = (isReadyToRunModule) ? pZapSigContext->pInfoModule : MscorlibBinder::GetModule();
721
722 if (pMT != NULL)
723 {
724 // We need to normalize the class passed in (if any) for reliability purposes. That's because preparation of a code region that
725 // contains these handle lookups depends on being able to predict exactly which lookups are required (so we can pre-cache the
726 // answers and remove any possibility of failure at runtime). This is hard to do if the lookup (in this case the lookup of the
727 // dictionary overflow cache) is keyed off the somewhat arbitrary type of the instance on which the call is made (we'd need to
728 // prepare for every possible derived type of the type containing the method). So instead we have to locate the exactly
729 // instantiated (non-shared) super-type of the class passed in.
730
731 pDictionary = pMT->GetDictionary();
732
733 ULONG dictionaryIndex = 0;
734
735 if (isReadyToRunModule)
736 {
737 dictionaryIndex = dictionaryIndexAndSlot >> 16;
738 }
739 else
740 {
741 IfFailThrow(ptr.GetData(&dictionaryIndex));
742 }
743
744 // MethodTable is expected to be normalized
745 _ASSERTE(pDictionary == pMT->GetPerInstInfo()[dictionaryIndex].GetValueMaybeNull());
746 }
747 else
748 {
749 pDictionary = pMD->GetMethodDictionary();
750 }
751
752 {
753 SigTypeContext typeContext;
754
755 if (pMT != NULL)
756 {
757 SigTypeContext::InitTypeContext(pMT, &typeContext);
758 }
759 else
760 {
761 SigTypeContext::InitTypeContext(pMD, &typeContext);
762 }
763
764 TypeHandle constraintType;
765 TypeHandle declaringType;
766
767 switch (kind)
768 {
769 case DeclaringTypeHandleSlot:
770 {
771 declaringType = ptr.GetTypeHandleThrowing(
772 pLookupModule,
773 &typeContext,
774 (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes),
775 CLASS_LOADED,
776 FALSE,
777 NULL,
778 pZapSigContext);
779 if (declaringType.IsNull())
780 {
781 _ASSERTE(nonExpansive);
782 return NULL;
783 }
784 IfFailThrow(ptr.SkipExactlyOne());
785
786 // fall through
787 }
788
789 case TypeHandleSlot:
790 {
791 TypeHandle th = ptr.GetTypeHandleThrowing(
792 pLookupModule,
793 &typeContext,
794 (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes),
795 CLASS_LOADED,
796 FALSE,
797 NULL,
798 pZapSigContext);
799 if (th.IsNull())
800 {
801 _ASSERTE(nonExpansive);
802 return NULL;
803 }
804 IfFailThrow(ptr.SkipExactlyOne());
805
806 if (!declaringType.IsNull())
807 {
808 th = th.GetMethodTable()->GetMethodTableMatchingParentClass(declaringType.AsMethodTable());
809 }
810
811 if (!IsCompilationProcess())
812 {
813 th.GetMethodTable()->EnsureInstanceActive();
814 }
815
816 result = (CORINFO_GENERIC_HANDLE)th.AsPtr();
817 break;
818 }
819
820 case ConstrainedMethodEntrySlot:
821 {
822 constraintType = ptr.GetTypeHandleThrowing(
823 pLookupModule,
824 &typeContext,
825 (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes),
826 CLASS_LOADED,
827 FALSE,
828 NULL,
829 pZapSigContext);
830 if (constraintType.IsNull())
831 {
832 _ASSERTE(nonExpansive);
833 return NULL;
834 }
835 IfFailThrow(ptr.SkipExactlyOne());
836
837 // fall through
838 }
839
840 case MethodDescSlot:
841 case DispatchStubAddrSlot:
842 case MethodEntrySlot:
843 {
844 TypeHandle ownerType;
845 MethodTable * pOwnerMT = NULL;
846 MethodDesc * pMethod = NULL;
847
848 DWORD methodFlags = 0;
849 BOOL isInstantiatingStub = 0;
850 BOOL isUnboxingStub = 0;
851 BOOL fMethodNeedsInstantiation = 0;
852
853 DWORD methodSlot = -1;
854 BOOL fRequiresDispatchStub = 0;
855
856 if (isReadyToRunModule)
857 {
858 IfFailThrow(ptr.GetData(&methodFlags));
859
860 if (methodFlags & ENCODE_METHOD_SIG_Constrained)
861 kind = ConstrainedMethodEntrySlot;
862
863 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0) || (kind == MethodEntrySlot);
864 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
865 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
866
867 if (methodFlags & ENCODE_METHOD_SIG_OwnerType)
868 {
869 ownerType = ptr.GetTypeHandleThrowing(
870 pZapSigContext->pInfoModule,
871 &typeContext,
872 ClassLoader::LoadTypes,
873 CLASS_LOADED,
874 FALSE,
875 NULL,
876 pZapSigContext);
877
878 IfFailThrow(ptr.SkipExactlyOne());
879 }
880
881 if (methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken)
882 {
883 // get the method desc using slot number
884 IfFailThrow(ptr.GetData(&methodSlot));
885
886 _ASSERTE(!ownerType.IsNull());
887 pMethod = ownerType.GetMethodTable()->GetMethodDescForSlot(methodSlot);
888 }
889 else
890 {
891 //
892 // decode method token
893 //
894 RID rid;
895 IfFailThrow(ptr.GetData(&rid));
896
897 if (methodFlags & ENCODE_METHOD_SIG_MemberRefToken)
898 {
899 if (ownerType.IsNull())
900 {
901 FieldDesc * pFDDummy = NULL;
902
903 MemberLoader::GetDescFromMemberRef(pZapSigContext->pInfoModule, TokenFromRid(rid, mdtMemberRef), &pMethod, &pFDDummy, NULL, FALSE, &ownerType);
904 _ASSERTE(pMethod != NULL && pFDDummy == NULL);
905 }
906 else
907 {
908 pMethod = MemberLoader::GetMethodDescFromMemberRefAndType(pZapSigContext->pInfoModule, TokenFromRid(rid, mdtMemberRef), ownerType.GetMethodTable());
909 }
910 }
911 else
912 {
913 pMethod = MemberLoader::GetMethodDescFromMethodDef(pZapSigContext->pInfoModule, TokenFromRid(rid, mdtMethodDef), FALSE);
914 }
915 }
916
917 if (ownerType.IsNull())
918 ownerType = pMethod->GetMethodTable();
919
920 _ASSERT(!ownerType.IsNull() && !nonExpansive);
921 pOwnerMT = ownerType.GetMethodTable();
922
923 if (kind == DispatchStubAddrSlot && pMethod->IsVtableMethod())
924 {
925 fRequiresDispatchStub = TRUE;
926 methodSlot = pMethod->GetSlot();
927 }
928 }
929 else
930 {
931 ownerType = ptr.GetTypeHandleThrowing(
932 pLookupModule,
933 &typeContext,
934 (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes),
935 CLASS_LOADED,
936 FALSE,
937 NULL,
938 pZapSigContext);
939 if (ownerType.IsNull())
940 {
941 _ASSERTE(nonExpansive);
942 return NULL;
943 }
944 IfFailThrow(ptr.SkipExactlyOne());
945
946 // <NICE> wsperf: Create a path that doesn't load types or create new handles if nonExpansive is set </NICE>
947 if (nonExpansive)
948 return NULL;
949
950 pOwnerMT = ownerType.GetMethodTable();
951 _ASSERTE(pOwnerMT != NULL);
952
953 IfFailThrow(ptr.GetData(&methodFlags));
954
955 isInstantiatingStub = ((methodFlags & ENCODE_METHOD_SIG_InstantiatingStub) != 0);
956 isUnboxingStub = ((methodFlags & ENCODE_METHOD_SIG_UnboxingStub) != 0);
957 fMethodNeedsInstantiation = ((methodFlags & ENCODE_METHOD_SIG_MethodInstantiation) != 0);
958
959 if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) != 0)
960 {
961 // get the method desc using slot number
962 IfFailThrow(ptr.GetData(&methodSlot));
963
964 if (kind == DispatchStubAddrSlot)
965 {
966 if (NingenEnabled())
967 return NULL;
968
969#ifndef CROSSGEN_COMPILE
970 fRequiresDispatchStub = TRUE;
971#endif
972 }
973
974 if (!fRequiresDispatchStub)
975 pMethod = pOwnerMT->GetMethodDescForSlot(methodSlot);
976 }
977 else
978 {
979 // Decode type where the method token is defined
980 TypeHandle thMethodDefType = ptr.GetTypeHandleThrowing(
981 pLookupModule,
982 &typeContext,
983 (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes),
984 CLASS_LOADED,
985 FALSE,
986 NULL,
987 pZapSigContext);
988 if (thMethodDefType.IsNull())
989 {
990 _ASSERTE(nonExpansive);
991 return NULL;
992 }
993 IfFailThrow(ptr.SkipExactlyOne());
994 MethodTable * pMethodDefMT = thMethodDefType.GetMethodTable();
995 _ASSERTE(pMethodDefMT != NULL);
996
997 // decode method token
998 RID rid;
999 IfFailThrow(ptr.GetData(&rid));
1000 mdMethodDef token = TokenFromRid(rid, mdtMethodDef);
1001
1002 // The RID map should have been filled out if we fully loaded the class
1003 pMethod = pMethodDefMT->GetModule()->LookupMethodDef(token);
1004 _ASSERTE(pMethod != NULL);
1005 pMethod->CheckRestore();
1006 }
1007 }
1008
1009 if (fRequiresDispatchStub)
1010 {
1011#ifndef CROSSGEN_COMPILE
1012 // Generate a dispatch stub and store it in the dictionary.
1013 //
1014 // We generate an indirection so we don't have to write to the dictionary
1015 // when we do updates, and to simplify stub indirect callsites. Stubs stored in
1016 // dictionaries use "RegisterIndirect" stub calling, e.g. "call [eax]",
1017 // i.e. here the register "eax" would contain the value fetched from the dictionary,
1018 // which in turn points to the stub indirection which holds the value the current stub
1019 // address itself. If we just used "call eax" then we wouldn't know which stub indirection
1020 // to update. If we really wanted to avoid the extra indirection we could return the _address_ of the
1021 // dictionary entry to the caller, still using "call [eax]", and then the
1022 // stub dispatch mechanism can update the dictitonary itself and we don't
1023 // need an indirection.
1024 LoaderAllocator * pDictLoaderAllocator = (pMT != NULL) ? pMT->GetLoaderAllocator() : pMD->GetLoaderAllocator();
1025
1026 VirtualCallStubManager * pMgr = pDictLoaderAllocator->GetVirtualCallStubManager();
1027
1028 // We indirect through a cell so that updates can take place atomically.
1029 // The call stub and the indirection cell have the same lifetime as the dictionary itself, i.e.
1030 // are allocated in the domain of the dicitonary.
1031 //
1032 // In the case of overflow (where there is no dictionary, just a global hash table) then
1033 // the entry will be placed in the overflow hash table (JitGenericHandleCache). This
1034 // is partitioned according to domain, i.e. is scraped each time an AppDomain gets unloaded.
1035 PCODE addr = pMgr->GetCallStub(ownerType, methodSlot);
1036
1037 result = (CORINFO_GENERIC_HANDLE)pMgr->GenerateStubIndirection(addr);
1038 break;
1039#endif // CROSSGEN_COMPILE
1040 }
1041
1042 Instantiation inst;
1043
1044 // Instantiate the method if needed, or create a stub to a static method in a generic class.
1045 if (fMethodNeedsInstantiation)
1046 {
1047 DWORD nargs;
1048 IfFailThrow(ptr.GetData(&nargs));
1049
1050 SIZE_T cbMem;
1051
1052 if (!ClrSafeInt<SIZE_T>::multiply(nargs, sizeof(TypeHandle), cbMem/* passed by ref */))
1053 ThrowHR(COR_E_OVERFLOW);
1054
1055 TypeHandle * pInst = (TypeHandle*)_alloca(cbMem);
1056 for (DWORD i = 0; i < nargs; i++)
1057 {
1058 pInst[i] = ptr.GetTypeHandleThrowing(
1059 pLookupModule,
1060 &typeContext,
1061 ClassLoader::LoadTypes,
1062 CLASS_LOADED,
1063 FALSE,
1064 NULL,
1065 pZapSigContext);
1066 IfFailThrow(ptr.SkipExactlyOne());
1067 }
1068
1069 inst = Instantiation(pInst, nargs);
1070 }
1071 else
1072 {
1073 inst = pMethod->GetMethodInstantiation();
1074 }
1075
1076 // This must be called even if nargs == 0, in order to create an instantiating
1077 // stub for static methods in generic classees if needed, also for BoxedEntryPointStubs
1078 // in non-generic structs.
1079 pMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(
1080 pMethod,
1081 pOwnerMT,
1082 isUnboxingStub,
1083 inst,
1084 (!isInstantiatingStub && !isUnboxingStub));
1085
1086 if (kind == ConstrainedMethodEntrySlot)
1087 {
1088 if (isReadyToRunModule)
1089 {
1090 _ASSERTE((methodFlags & ENCODE_METHOD_SIG_Constrained) == ENCODE_METHOD_SIG_Constrained);
1091
1092 constraintType = ptr.GetTypeHandleThrowing(
1093 pZapSigContext->pInfoModule,
1094 &typeContext,
1095 ClassLoader::LoadTypes,
1096 CLASS_LOADED,
1097 FALSE,
1098 NULL,
1099 pZapSigContext);
1100 }
1101 _ASSERTE(!constraintType.IsNull());
1102
1103 MethodDesc *pResolvedMD = constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);
1104
1105 // All such calls should be resolvable. If not then for now just throw an error.
1106 _ASSERTE(pResolvedMD);
1107 INDEBUG(if (!pResolvedMD) constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);)
1108 if (!pResolvedMD)
1109 COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
1110
1111 result = (CORINFO_GENERIC_HANDLE)pResolvedMD->GetMultiCallableAddrOfCode();
1112 }
1113 else
1114 if (kind == MethodEntrySlot)
1115 {
1116 result = (CORINFO_GENERIC_HANDLE)pMethod->GetMultiCallableAddrOfCode();
1117 }
1118 else
1119 if (kind == DispatchStubAddrSlot)
1120 {
1121 _ASSERTE((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0);
1122 PCODE *ppCode = (PCODE*)(void*)pMethod->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PCODE)));
1123 *ppCode = pMethod->GetMultiCallableAddrOfCode();
1124 result = (CORINFO_GENERIC_HANDLE)ppCode;
1125 }
1126 else
1127 {
1128 _ASSERTE(kind == MethodDescSlot);
1129 result = (CORINFO_GENERIC_HANDLE)pMethod;
1130 }
1131 break;
1132 }
1133
1134 case FieldDescSlot:
1135 {
1136 TypeHandle ownerType;
1137
1138 if (isReadyToRunModule)
1139 {
1140 FieldDesc* pField = ZapSig::DecodeField((Module*)pZapSigContext->pModuleContext, pZapSigContext->pInfoModule, ptr.GetPtr(), &typeContext, &ownerType);
1141 _ASSERTE(!ownerType.IsNull());
1142
1143 if (!IsCompilationProcess())
1144 ownerType.AsMethodTable()->EnsureInstanceActive();
1145
1146 result = (CORINFO_GENERIC_HANDLE)pField;
1147 }
1148 else
1149 {
1150 ownerType = ptr.GetTypeHandleThrowing(
1151 pLookupModule,
1152 &typeContext,
1153 (nonExpansive ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes),
1154 CLASS_LOADED,
1155 FALSE,
1156 NULL,
1157 pZapSigContext);
1158 if (ownerType.IsNull())
1159 {
1160 _ASSERTE(nonExpansive);
1161 return NULL;
1162 }
1163 IfFailThrow(ptr.SkipExactlyOne());
1164
1165 DWORD fieldIndex;
1166 IfFailThrow(ptr.GetData(&fieldIndex));
1167
1168 if (!IsCompilationProcess())
1169 ownerType.AsMethodTable()->EnsureInstanceActive();
1170
1171 result = (CORINFO_GENERIC_HANDLE)ownerType.AsMethodTable()->GetFieldDescByIndex(fieldIndex);
1172 }
1173 break;
1174 }
1175
1176 default:
1177 _ASSERTE(!"Invalid DictionaryEntryKind");
1178 break;
1179 }
1180
1181 ULONG slotIndex;
1182 if (isReadyToRunModule)
1183 {
1184 _ASSERT(dictionaryIndexAndSlot != -1);
1185 slotIndex = (ULONG)(dictionaryIndexAndSlot & 0xFFFF);
1186 }
1187 else
1188 {
1189 IfFailThrow(ptr.GetData(&slotIndex));
1190 }
1191
1192 MemoryBarrier();
1193
1194 if ((slotIndex != 0) && !IsCompilationProcess())
1195 {
1196 *EnsureWritablePages(pDictionary->GetSlotAddr(0, slotIndex)) = result;
1197 *ppSlot = pDictionary->GetSlotAddr(0, slotIndex);
1198 }
1199 }
1200
1201 return result;
1202} // Dictionary::PopulateEntry
1203
1204//---------------------------------------------------------------------------------------
1205//
1206void
1207Dictionary::PrepopulateDictionary(
1208 MethodDesc * pMD,
1209 MethodTable * pMT,
1210 BOOL nonExpansive)
1211{
1212 STANDARD_VM_CONTRACT;
1213
1214 DictionaryLayout * pDictLayout = (pMT != NULL) ? pMT->GetClass()->GetDictionaryLayout() : pMD->GetDictionaryLayout();
1215 DWORD numGenericArgs = (pMT != NULL) ? pMT->GetNumGenericArgs() : pMD->GetNumGenericMethodArgs();
1216
1217 if (pDictLayout != NULL)
1218 {
1219 for (DWORD i = 0; i < pDictLayout->GetNumUsedSlots(); i++)
1220 {
1221 if (IsSlotEmpty(numGenericArgs,i))
1222 {
1223 DictionaryEntry * pSlot;
1224 DictionaryEntry entry;
1225 entry = PopulateEntry(
1226 pMD,
1227 pMT,
1228 pDictLayout->GetEntryLayout(i)->m_signature,
1229 nonExpansive,
1230 &pSlot);
1231
1232 _ASSERT((entry == NULL) || (entry == GetSlot(numGenericArgs,i)) || IsCompilationProcess());
1233 _ASSERT((pSlot == NULL) || (pSlot == GetSlotAddr(numGenericArgs,i)));
1234 }
1235 }
1236 }
1237} // Dictionary::PrepopulateDictionary
1238
1239#endif //!DACCESS_COMPILE
1240