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//
6// Hash table associated with each module that records for all types defined in that module the mapping
7// between type name and token (or TypeHandle).
8//
9
10#include "common.h"
11#include "classhash.h"
12#include "ngenhash.inl"
13#include "fstring.h"
14#include "classhash.inl"
15
16PTR_EEClassHashEntry EEClassHashEntry::GetEncloser()
17{
18 CONTRACTL
19 {
20 NOTHROW;
21 GC_NOTRIGGER;
22 MODE_ANY;
23 SUPPORTS_DAC;
24 }
25 CONTRACTL_END;
26
27 return m_pEncloser.Get();
28}
29
30PTR_VOID EEClassHashEntry::GetData()
31{
32 CONTRACTL
33 {
34 NOTHROW;
35 GC_NOTRIGGER;
36 MODE_ANY;
37 SUPPORTS_DAC;
38 }
39 CONTRACTL_END;
40
41 // TypeHandles are encoded as a relative pointer rather than a regular pointer to avoid the need for image
42 // fixups (any TypeHandles in this hash are defined in the same module).
43 if ((dac_cast<TADDR>(m_Data) & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
44 return RelativePointer<PTR_VOID>::GetValueMaybeNullAtPtr(PTR_HOST_INT_MEMBER_TADDR(EEClassHashEntry, this, m_Data));
45
46 return m_Data;
47}
48
49#ifndef DACCESS_COMPILE
50void EEClassHashEntry::SetData(void *data)
51{
52 CONTRACTL
53 {
54 NOTHROW;
55 GC_NOTRIGGER;
56 MODE_ANY;
57 }
58 CONTRACTL_END;
59
60 // TypeHandles are encoded as a relative pointer rather than a regular pointer to avoid the need for image
61 // fixups (any TypeHandles in this hash are defined in the same module).
62 if (((TADDR)data & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
63 {
64 RelativePointer<void *> *pRelPtr = (RelativePointer<void *> *) &m_Data;
65 pRelPtr->SetValueMaybeNull(data);
66 }
67 else
68 m_Data = data;
69}
70
71void EEClassHashEntry::SetEncloser(EEClassHashEntry *pEncloser)
72{
73 CONTRACTL
74 {
75 NOTHROW;
76 GC_NOTRIGGER;
77 MODE_ANY;
78 }
79 CONTRACTL_END;
80
81 m_pEncloser.Set(pEncloser);
82}
83
84/*static*/
85EEClassHashTable *EEClassHashTable::Create(Module *pModule, DWORD dwNumBuckets, BOOL bCaseInsensitive, AllocMemTracker *pamTracker)
86{
87 CONTRACTL
88 {
89 THROWS;
90 GC_TRIGGERS;
91 MODE_ANY;
92 INJECT_FAULT(COMPlusThrowOM(););
93 PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED());
94
95 }
96 CONTRACTL_END;
97
98 LoaderHeap *pHeap = pModule->GetAssembly()->GetLowFrequencyHeap();
99 EEClassHashTable *pThis = (EEClassHashTable*)pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(EEClassHashTable)));
100
101 // The base class get initialized through chaining of constructors. We allocated the hash instance via the
102 // loader heap instead of new so use an in-place new to call the constructors now.
103 new (pThis) EEClassHashTable(pModule, pHeap, dwNumBuckets);
104
105 pThis->m_bCaseInsensitive = bCaseInsensitive;
106
107 return pThis;
108}
109
110EEClassHashEntry_t *EEClassHashTable::AllocNewEntry(AllocMemTracker *pamTracker)
111{
112 CONTRACTL
113 {
114 THROWS;
115 GC_NOTRIGGER;
116 INJECT_FAULT(COMPlusThrowOM(););
117 MODE_ANY;
118
119 PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED());
120 }
121 CONTRACTL_END;
122
123 // Simply defer to the base class for entry allocation (this is required, the base class wraps the entry
124 // it returns to us in its own metadata).
125 return BaseAllocateEntry(pamTracker);
126}
127
128#endif // !DACCESS_COMPILE
129
130VOID EEClassHashTable::UncompressModuleAndNonExportClassDef(HashDatum Data, Module **ppModule, mdTypeDef *pCL)
131{
132 CONTRACTL
133 {
134 INSTANCE_CHECK;
135 NOTHROW;
136 GC_NOTRIGGER;
137 FORBID_FAULT;
138 MODE_ANY;
139 SUPPORTS_DAC;
140 }
141 CONTRACTL_END;
142
143 DWORD dwData = (DWORD)dac_cast<TADDR>(Data);
144 _ASSERTE((dwData & EECLASSHASH_TYPEHANDLE_DISCR) == EECLASSHASH_TYPEHANDLE_DISCR);
145 _ASSERTE(!(dwData & EECLASSHASH_MDEXPORT_DISCR));
146
147 *pCL = ((dwData >> 1) & 0x00ffffff) | mdtTypeDef;
148 *ppModule = GetModule();
149}
150
151bool EEClassHashTable::UncompressModuleAndClassDef(HashDatum Data, Loader::LoadFlag loadFlag,
152 Module **ppModule, mdTypeDef *pCL,
153 mdExportedType *pmdFoundExportedType)
154{
155 CONTRACT(bool)
156 {
157 INSTANCE_CHECK;
158 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
159 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
160 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM();); }
161 MODE_ANY;
162
163 PRECONDITION(CheckPointer(pCL));
164 PRECONDITION(CheckPointer(ppModule));
165 POSTCONDITION(*ppModule != nullptr || loadFlag != Loader::Load);
166 SUPPORTS_DAC;
167 }
168 CONTRACT_END
169
170 DWORD dwData = (DWORD)dac_cast<TADDR>(Data);
171 _ASSERTE((dwData & EECLASSHASH_TYPEHANDLE_DISCR) == EECLASSHASH_TYPEHANDLE_DISCR);
172 if(dwData & EECLASSHASH_MDEXPORT_DISCR) {
173 *pmdFoundExportedType = ((dwData >> 1) & 0x00ffffff) | mdtExportedType;
174
175 *ppModule = GetModule()->GetAssembly()->FindModuleByExportedType(*pmdFoundExportedType, loadFlag, mdTypeDefNil, pCL);
176 }
177 else {
178 UncompressModuleAndNonExportClassDef(Data, ppModule, pCL);
179 *pmdFoundExportedType = mdTokenNil;
180 _ASSERTE(*ppModule != nullptr); // Should never fail.
181 }
182
183 RETURN (*ppModule != nullptr);
184}
185
186/* static */
187mdToken EEClassHashTable::UncompressModuleAndClassDef(HashDatum Data)
188{
189 CONTRACTL
190 {
191 NOTHROW;
192 GC_NOTRIGGER;
193 FORBID_FAULT;
194 MODE_ANY;
195 SUPPORTS_DAC;
196 }
197 CONTRACTL_END
198
199 DWORD dwData = (DWORD)dac_cast<TADDR>(Data); // 64Bit: Pointer truncation is OK here - it's not actually a pointer
200 _ASSERTE((dwData & EECLASSHASH_TYPEHANDLE_DISCR) == EECLASSHASH_TYPEHANDLE_DISCR);
201
202 if(dwData & EECLASSHASH_MDEXPORT_DISCR)
203 return ((dwData >> 1) & 0x00ffffff) | mdtExportedType;
204 else
205 return ((dwData >> 1) & 0x00ffffff) | mdtTypeDef;
206}
207
208VOID EEClassHashTable::ConstructKeyFromData(PTR_EEClassHashEntry pEntry, // IN : Entry to compare
209 ConstructKeyCallback *pCallback) // This class will process the output
210{
211 CONTRACTL
212 {
213 THROWS;
214 WRAPPER(MODE_ANY);
215 WRAPPER(GC_TRIGGERS);
216 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else WRAPPER(FORBID_FAULT);
217 SUPPORTS_DAC;
218 }
219 CONTRACTL_END;
220
221 LPUTF8 Key[2];
222 Key[0] = Key[1] = NULL;
223
224 {
225#ifdef _DEBUG_IMPL
226 _ASSERTE(!(m_bCaseInsensitive && FORBIDGC_LOADER_USE_ENABLED()));
227#endif
228
229 // cqb - If m_bCaseInsensitive is true for the hash table, the bytes in Key will be allocated
230 // from cqb. This is to prevent wasting bytes in the Loader Heap. Thusly, it is important to note that
231 // in this case, the lifetime of Key is bounded by the lifetime of cqb, which will free the memory
232 // it allocated on destruction.
233
234 _ASSERTE(!m_pModule.IsNull());
235 LPSTR pszName = NULL;
236 LPSTR pszNameSpace = NULL;
237 IMDInternalImport *pInternalImport = NULL;
238
239 PTR_VOID Data = NULL;
240 if (!m_bCaseInsensitive)
241 Data = pEntry->GetData();
242 else
243 Data = (PTR_EEClassHashEntry(pEntry->GetData()))->GetData();
244
245 // Lower bit is a discriminator. If the lower bit is NOT SET, it means we have
246 // a TypeHandle, otherwise, we have a mdtTypedef/mdtExportedType.
247 if ((dac_cast<TADDR>(Data) & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
248 {
249 TypeHandle pType = TypeHandle::FromPtr(Data);
250 _ASSERTE (pType.GetMethodTable());
251 MethodTable *pMT = pType.GetMethodTable();
252 _ASSERTE(pMT != NULL);
253 IfFailThrow(pMT->GetMDImport()->GetNameOfTypeDef(pMT->GetCl(), (LPCSTR *)&pszName, (LPCSTR *)&pszNameSpace));
254 }
255 else // We have a mdtoken
256 {
257 // call the lightweight version first
258 mdToken mdtUncompressed = UncompressModuleAndClassDef(Data);
259 if (TypeFromToken(mdtUncompressed) == mdtExportedType)
260 {
261 IfFailThrow(GetModule()->GetClassLoader()->GetAssembly()->GetManifestImport()->GetExportedTypeProps(
262 mdtUncompressed,
263 (LPCSTR *)&pszNameSpace,
264 (LPCSTR *)&pszName,
265 NULL, //mdImpl
266 NULL, // type def
267 NULL)); // flags
268 }
269 else
270 {
271 _ASSERTE(TypeFromToken(mdtUncompressed) == mdtTypeDef);
272
273 Module * pUncompressedModule;
274 mdTypeDef UncompressedCl;
275 UncompressModuleAndNonExportClassDef(Data, &pUncompressedModule, &UncompressedCl);
276 _ASSERTE (pUncompressedModule && "Uncompressed token of unexpected type");
277 pInternalImport = pUncompressedModule->GetMDImport();
278 _ASSERTE(pInternalImport && "Uncompressed token has no MD import");
279 IfFailThrow(pInternalImport->GetNameOfTypeDef(UncompressedCl, (LPCSTR *)&pszName, (LPCSTR *)&pszNameSpace));
280 }
281 }
282
283 if (!m_bCaseInsensitive)
284 {
285 Key[0] = pszNameSpace;
286 Key[1] = pszName;
287 }
288 else
289 {
290 CONTRACT_VIOLATION(ThrowsViolation|FaultViolation);
291
292#ifndef DACCESS_COMPILE
293 // We can call the nothrow version here because we fulfilled the requirement of calling
294 // InitTables() in the "new" method.
295 INT32 iNSLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszNameSpace);
296 if (!iNSLength)
297 {
298 COMPlusThrowOM();
299 }
300
301 INT32 iNameLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszName);
302 if (!iNameLength)
303 {
304 COMPlusThrowOM();
305 }
306
307 // Prefast overflow sanity check before alloc.
308 INT32 iAllocSize;
309 if (!ClrSafeInt<INT32>::addition(iNSLength, iNameLength, iAllocSize))
310 COMPlusThrowOM();
311 LPUTF8 pszOutNameSpace = (LPUTF8) _alloca(iAllocSize);
312 if (iNSLength == 1)
313 {
314 *pszOutNameSpace = '\0';
315 }
316 else
317 {
318 if (!InternalCasingHelper::InvariantToLowerNoThrow(pszOutNameSpace, iNSLength, pszNameSpace))
319 {
320 COMPlusThrowOM();
321 }
322 }
323 LPUTF8 pszOutName = (LPUTF8) pszOutNameSpace + iNSLength;
324
325 if (!InternalCasingHelper::InvariantToLowerNoThrow(pszOutName, iNameLength, pszName))
326 {
327 COMPlusThrowOM();
328 }
329 Key[0] = pszOutNameSpace;
330 Key[1] = pszOutName;
331#else
332 DacNotImpl();
333#endif // #ifndef DACCESS_COMPILE
334 }
335 }
336
337 pCallback->UseKeys(Key);
338}
339
340#ifndef DACCESS_COMPILE
341
342EEClassHashEntry_t *EEClassHashTable::InsertValue(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID Data, EEClassHashEntry_t *pEncloser, AllocMemTracker *pamTracker)
343{
344 CONTRACTL
345 {
346 THROWS;
347 GC_NOTRIGGER;
348 MODE_ANY;
349
350 INJECT_FAULT(COMPlusThrowOM(););
351 PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED());
352 }
353 CONTRACTL_END;
354
355 _ASSERTE(pszNamespace != NULL);
356 _ASSERTE(pszClassName != NULL);
357 _ASSERTE(!m_pModule.IsNull());
358
359 EEClassHashEntry *pEntry = BaseAllocateEntry(pamTracker);
360
361 pEntry->SetData(Data);
362 pEntry->SetEncloser(pEncloser);
363#ifdef _DEBUG
364 pEntry->DebugKey[0] = pszNamespace;
365 pEntry->DebugKey[1] = pszClassName;
366#endif
367
368 BaseInsertEntry(Hash(pszNamespace, pszClassName), pEntry);
369
370 return pEntry;
371}
372
373#ifdef _DEBUG
374class ConstructKeyCallbackValidate : public EEClassHashTable::ConstructKeyCallback
375{
376public:
377 virtual void UseKeys(__in_ecount(2) LPUTF8 *Key)
378 {
379 LIMITED_METHOD_CONTRACT;
380 STATIC_CONTRACT_DEBUG_ONLY;
381 _ASSERTE (strcmp(pNewEntry->DebugKey[1], Key[1]) == 0);
382 _ASSERTE (strcmp(pNewEntry->DebugKey[0], Key[0]) == 0);
383 SUPPORTS_DAC;
384 }
385
386 EEClassHashEntry_t *pNewEntry;
387
388};
389#endif // _DEBUG
390
391// This entrypoint lets the caller separate the allocation of the entrypoint from the actual insertion into the hashtable. (This lets us
392// do multiple insertions without having to worry about an OOM occuring inbetween.)
393//
394// The newEntry must have been allocated using AllocEntry. It must not be referenced by any other entity (other than a holder or tracker)
395// If this function throws, the caller is responsible for freeing the entry.
396EEClassHashEntry_t *EEClassHashTable::InsertValueUsingPreallocatedEntry(EEClassHashEntry_t *pNewEntry, LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID Data, EEClassHashEntry_t *pEncloser)
397{
398 CONTRACTL
399 {
400 NOTHROW;
401 GC_NOTRIGGER;
402 MODE_ANY;
403 FORBID_FAULT;
404
405 PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED());
406 }
407 CONTRACTL_END;
408
409 pNewEntry->SetData(Data);
410 pNewEntry->SetEncloser(pEncloser);
411
412#ifdef _DEBUG
413 pNewEntry->DebugKey[0] = pszNamespace;
414 pNewEntry->DebugKey[1] = pszClassName;
415#endif
416
417 BaseInsertEntry(Hash(pszNamespace, pszClassName), pNewEntry);
418
419 return pNewEntry;
420}
421
422EEClassHashEntry_t *EEClassHashTable::InsertValueIfNotFound(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID *pData, EEClassHashEntry_t *pEncloser, BOOL IsNested, BOOL *pbFound, AllocMemTracker *pamTracker)
423{
424 CONTRACTL
425 {
426 THROWS;
427 GC_NOTRIGGER;
428 MODE_ANY;
429 INJECT_FAULT(COMPlusThrowOM(););
430
431 PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED());
432 }
433 CONTRACTL_END;
434
435 _ASSERTE(!m_pModule.IsNull());
436 _ASSERTE(pszNamespace != NULL);
437 _ASSERTE(pszClassName != NULL);
438
439 EEClassHashEntry_t * pNewEntry = FindItem(pszNamespace, pszClassName, IsNested, NULL);
440
441 if (pNewEntry)
442 {
443 *pData = pNewEntry->GetData();
444 *pbFound = TRUE;
445 return pNewEntry;
446 }
447
448 // Reached here implies that we didn't find the entry and need to insert it
449 *pbFound = FALSE;
450
451 pNewEntry = BaseAllocateEntry(pamTracker);
452
453 pNewEntry->SetData(*pData);
454 pNewEntry->SetEncloser(pEncloser);
455
456#ifdef _DEBUG
457 pNewEntry->DebugKey[0] = pszNamespace;
458 pNewEntry->DebugKey[1] = pszClassName;
459#endif
460
461 BaseInsertEntry(Hash(pszNamespace, pszClassName), pNewEntry);
462
463 return pNewEntry;
464}
465
466#endif // !DACCESS_COMPILE
467
468EEClassHashEntry_t *EEClassHashTable::FindItem(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, BOOL IsNested, LookupContext *pContext)
469{
470 CONTRACTL
471 {
472 if (m_bCaseInsensitive) THROWS; else NOTHROW;
473 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
474 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
475 MODE_ANY;
476 SUPPORTS_DAC;
477 }
478 CONTRACTL_END;
479
480 _ASSERTE(!m_pModule.IsNull());
481 _ASSERTE(pszNamespace != NULL);
482 _ASSERTE(pszClassName != NULL);
483
484 // It's legal for the caller not to pass us a LookupContext (when the type being queried is not nested
485 // there will never be any need to iterate over the search results). But we might need to iterate
486 // internally (since we lookup via hash and hashes may collide). So substitute our own private context if
487 // one was not provided.
488 LookupContext sAltContext;
489 if (pContext == NULL)
490 pContext = &sAltContext;
491
492 // The base class provides the ability to enumerate all entries with the same hash code. We call this and
493 // further check which of these entries actually match the full key (there can be multiple hits with
494 // nested types in the picture).
495 PTR_EEClassHashEntry pSearch = BaseFindFirstEntryByHash(Hash(pszNamespace, pszClassName), pContext);
496
497 while (pSearch)
498 {
499 LPCUTF8 rgKey[] = { pszNamespace, pszClassName };
500
501 if (CompareKeys(pSearch, rgKey))
502 {
503 // If (IsNested), then we're looking for a nested class
504 // If (pSearch->pEncloser), we've found a nested class
505 if ((IsNested != FALSE) == (pSearch->GetEncloser() != NULL))
506 {
507 if (m_bCaseInsensitive)
508 g_IBCLogger.LogClassHashTableAccess(dac_cast<PTR_EEClassHashEntry>(pSearch->GetData()));
509 else
510 g_IBCLogger.LogClassHashTableAccess(pSearch);
511
512 return pSearch;
513 }
514 }
515
516 pSearch = BaseFindNextEntryByHash(pContext);
517 }
518
519 return NULL;
520}
521
522EEClassHashEntry_t *EEClassHashTable::FindNextNestedClass(NameHandle* pName, PTR_VOID *pData, LookupContext *pContext)
523{
524 CONTRACTL
525 {
526 if (m_bCaseInsensitive) THROWS; else NOTHROW;
527 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
528 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
529 MODE_ANY;
530 SUPPORTS_DAC;
531 }
532 CONTRACTL_END;
533
534 _ASSERTE(!m_pModule.IsNull());
535 _ASSERTE(pName);
536
537 if (pName->GetNameSpace())
538 {
539 return FindNextNestedClass(pName->GetNameSpace(), pName->GetName(), pData, pContext);
540 }
541 else {
542#ifndef DACCESS_COMPILE
543 return FindNextNestedClass(pName->GetName(), pData, pContext); // this won't support dac--
544 // it allocates a new namespace string
545#else
546 DacNotImpl();
547 return NULL;
548#endif
549 }
550}
551
552
553EEClassHashEntry_t *EEClassHashTable::FindNextNestedClass(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID *pData, LookupContext *pContext)
554{
555 CONTRACTL
556 {
557 if (m_bCaseInsensitive) THROWS; else NOTHROW;
558 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
559 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
560 MODE_ANY;
561 SUPPORTS_DAC;
562 }
563 CONTRACTL_END;
564
565 _ASSERTE(!m_pModule.IsNull());
566
567 PTR_EEClassHashEntry pSearch = BaseFindNextEntryByHash(pContext);
568
569 while (pSearch)
570 {
571 LPCUTF8 rgKey[] = { pszNamespace, pszClassName };
572
573 if (pSearch->GetEncloser() && CompareKeys(pSearch, rgKey))
574 {
575 *pData = pSearch->GetData();
576 return pSearch;
577 }
578
579 pSearch = BaseFindNextEntryByHash(pContext);
580 }
581
582 return NULL;
583}
584
585const UTF8 Utf8Empty[] = { 0 };
586
587EEClassHashEntry_t *EEClassHashTable::FindNextNestedClass(LPCUTF8 pszFullyQualifiedName, PTR_VOID *pData, LookupContext *pContext)
588{
589 CONTRACTL
590 {
591 if (m_bCaseInsensitive) THROWS; else NOTHROW;
592 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
593 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
594 MODE_ANY;
595 }
596 CONTRACTL_END;
597
598 _ASSERTE(!m_pModule.IsNull());
599
600 CQuickBytes szNamespace;
601
602 LPCUTF8 pNamespace = Utf8Empty;
603 LPCUTF8 p;
604
605 if ((p = ns::FindSep(pszFullyQualifiedName)) != NULL)
606 {
607 SIZE_T d = p - pszFullyQualifiedName;
608
609 FAULT_NOT_FATAL();
610 pNamespace = szNamespace.SetStringNoThrow(pszFullyQualifiedName, d);
611
612 if (NULL == pNamespace)
613 {
614 return NULL;
615 }
616
617 p++;
618 }
619 else
620 {
621 p = pszFullyQualifiedName;
622 }
623
624 return FindNextNestedClass(pNamespace, p, pData, pContext);
625}
626
627
628EEClassHashEntry_t * EEClassHashTable::GetValue(LPCUTF8 pszFullyQualifiedName, PTR_VOID *pData, BOOL IsNested, LookupContext *pContext)
629{
630 CONTRACTL
631 {
632 if (m_bCaseInsensitive) THROWS; else NOTHROW;
633 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
634 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
635 MODE_ANY;
636 SUPPORTS_DAC;
637 }
638 CONTRACTL_END;
639
640 _ASSERTE(!m_pModule.IsNull());
641
642 CQuickBytes szNamespace;
643
644 LPCUTF8 pNamespace = Utf8Empty;
645
646 LPCUTF8 p = ns::FindSep(pszFullyQualifiedName);
647
648 if (p != NULL)
649 {
650 SIZE_T d = p - pszFullyQualifiedName;
651
652 FAULT_NOT_FATAL();
653 pNamespace = szNamespace.SetStringNoThrow(pszFullyQualifiedName, d);
654
655 if (NULL == pNamespace)
656 {
657 return NULL;
658 }
659
660 p++;
661 }
662 else
663 {
664 p = pszFullyQualifiedName;
665 }
666
667 EEClassHashEntry_t * ret = GetValue(pNamespace, p, pData, IsNested, pContext);
668
669 return ret;
670}
671
672
673EEClassHashEntry_t * EEClassHashTable::GetValue(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID *pData, BOOL IsNested, LookupContext *pContext)
674{
675 CONTRACTL
676 {
677 if (m_bCaseInsensitive) THROWS; else NOTHROW;
678 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
679 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
680 MODE_ANY;
681 SUPPORTS_DAC;
682 }
683 CONTRACTL_END;
684
685
686 _ASSERTE(!m_pModule.IsNull());
687 EEClassHashEntry_t *pItem = FindItem(pszNamespace, pszClassName, IsNested, pContext);
688 if (pItem)
689 *pData = pItem->GetData();
690
691 return pItem;
692}
693
694
695EEClassHashEntry_t * EEClassHashTable::GetValue(NameHandle* pName, PTR_VOID *pData, BOOL IsNested, LookupContext *pContext)
696{
697 CONTRACTL
698 {
699 // for DAC builds m_bCaseInsensitive should be false
700 if (m_bCaseInsensitive) THROWS; else NOTHROW;
701 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
702 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
703 MODE_ANY;
704 SUPPORTS_DAC;
705 }
706 CONTRACTL_END;
707
708
709 _ASSERTE(pName);
710 _ASSERTE(!m_pModule.IsNull());
711 if(pName->GetNameSpace() == NULL) {
712 return GetValue(pName->GetName(), pData, IsNested, pContext);
713 }
714 else {
715 return GetValue(pName->GetNameSpace(), pName->GetName(), pData, IsNested, pContext);
716 }
717}
718
719class ConstructKeyCallbackCompare : public EEClassHashTable::ConstructKeyCallback
720{
721public:
722 virtual void UseKeys(__in_ecount(2) LPUTF8 *pKey1)
723 {
724 LIMITED_METHOD_CONTRACT;
725 STATIC_CONTRACT_SO_TOLERANT;
726 SUPPORTS_DAC;
727
728 bReturn = (
729 ((pKey1[0] == pKey2[0]) && (pKey1[1] == pKey2[1])) ||
730 ((strcmp (pKey1[0], pKey2[0]) == 0) && (strcmp (pKey1[1], pKey2[1]) == 0))
731 );
732 }
733
734 LPCUTF8 *pKey2;
735 BOOL bReturn;
736};
737
738// Returns TRUE if two keys are the same string.
739//
740// The case-insensitive table can throw OOM out of this function. The case-sensitive table can't.
741BOOL EEClassHashTable::CompareKeys(PTR_EEClassHashEntry pEntry, LPCUTF8 * pKey2)
742{
743 CONTRACTL
744 {
745 if (m_bCaseInsensitive) THROWS; else NOTHROW;
746 if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER;
747 if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT;
748 MODE_ANY;
749 SUPPORTS_DAC;
750 }
751 CONTRACTL_END;
752
753
754 _ASSERTE(!m_pModule.IsNull());
755 _ASSERTE (pEntry);
756 _ASSERTE (pKey2);
757
758 ConstructKeyCallbackCompare cback;
759
760 cback.pKey2 = pKey2;
761
762 {
763 CONTRACT_VIOLATION(ThrowsViolation);
764 ConstructKeyFromData(pEntry, &cback);
765 }
766
767 return cback.bReturn;
768}
769
770
771#ifndef DACCESS_COMPILE
772
773#ifdef FEATURE_NATIVE_IMAGE_GENERATION
774void EEClassHashTable::Save(DataImage *image, CorProfileData *profileData)
775{
776 STANDARD_VM_CONTRACT;
777
778 // See comment on PrepareExportedTypesForSaving for what's going on here.
779 if (GetModule()->IsManifest())
780 PrepareExportedTypesForSaving(image);
781
782 // The base class handles most of the saving logic (it controls the layout of the hash memory). It will
783 // call us back for some per-entry related details (should we save this entry?, is this entry hot? etc.).
784 // See the methods immediately following this one.
785 BaseSave(image, profileData);
786}
787
788// Should a particular entry be persisted into the ngen image?
789bool EEClassHashTable::ShouldSave(DataImage *pImage, EEClassHashEntry_t *pEntry)
790{
791 LIMITED_METHOD_CONTRACT;
792
793 // We always save all entries.
794 return true;
795}
796
797// Does profile data indicate that this entry is hot (likely to be read at runtime)?
798bool EEClassHashTable::IsHotEntry(EEClassHashEntry_t *pEntry, CorProfileData *pProfileData)
799{
800 STANDARD_VM_CONTRACT;
801
802 PTR_VOID datum = pEntry->GetData();
803 mdToken token;
804
805 if (m_bCaseInsensitive)
806 datum = PTR_EEClassHashEntry((TADDR)datum)->GetData();
807
808 if ((((ULONG_PTR) datum) & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
809 {
810 TypeHandle t = TypeHandle::FromPtr(datum);
811 _ASSERTE(!t.IsNull());
812 MethodTable *pMT = t.GetMethodTable();
813 if (pMT == NULL)
814 return false;
815
816 token = pMT->GetCl();
817 }
818 else if (((ULONG_PTR)datum & EECLASSHASH_MDEXPORT_DISCR) == 0)
819 {
820 DWORD dwDatum = (DWORD)(DWORD_PTR)(datum);
821 token = ((dwDatum >> 1) & 0x00ffffff) | mdtTypeDef;
822 }
823 else
824 return false;
825
826 if (pProfileData->GetTypeProfilingFlagsOfToken(token) & (1 << ReadClassHashTable))
827 return true;
828
829 return false;
830}
831
832// This is our chance to fixup our hash entries before they're committed to the ngen image. Return true if the
833// entry will remain immutable at runtime (this might allow the entry to be stored in a read-only, shareable
834// portion of the image).
835bool EEClassHashTable::SaveEntry(DataImage *pImage, CorProfileData *pProfileData, EEClassHashEntry_t *pOldEntry, EEClassHashEntry_t *pNewEntry, EntryMappingTable *pMap)
836{
837 STANDARD_VM_CONTRACT;
838
839 // If we're a nested class we have a reference to the entry of our enclosing class. But this reference
840 // will have been broken by the saving process (the base class re-creates and re-orders all entries in
841 // order to optimize them for ngen images). So we read the old encloser address from the old version of
842 // our entry and, if there is one, we use the supplied map to transform that address into the new version.
843 // We can then write the updated address back into our encloser field in the new copy of the entry.
844 EEClassHashEntry_t *pOldEncloser = pOldEntry->GetEncloser();
845 if (pOldEncloser)
846 pNewEntry->SetEncloser(pMap->GetNewEntryAddress(pOldEncloser));
847
848 // We have to do something similar with our TypeHandle references (because they're stored as relative
849 // pointers which became invalid when the entry was moved). The following sequence is a no-op if the data
850 // field contains a token rather than a TypeHandle.
851 PTR_VOID oldData = pOldEntry->GetData();
852 pNewEntry->SetData(oldData);
853
854#ifdef _DEBUG
855 if (!pImage->IsStored(pNewEntry->DebugKey[0]))
856 pImage->StoreStructure(pNewEntry->DebugKey[0], (ULONG)(strlen(pNewEntry->DebugKey[0])+1), DataImage::ITEM_DEBUG);
857 if (!pImage->IsStored(pNewEntry->DebugKey[1]))
858 pImage->StoreStructure(pNewEntry->DebugKey[1], (ULONG)(strlen(pNewEntry->DebugKey[1])+1), DataImage::ITEM_DEBUG);
859#endif // _DEBUG
860
861 // The entry is immutable at runtime if it's encoded as a TypeHandle. If it's a token then it might be
862 // bashed into a TypeHandle later on.
863 return ((TADDR)pNewEntry->GetData() & EECLASSHASH_TYPEHANDLE_DISCR) == 0;
864}
865
866// The manifest module contains exported type entries in the EEClassHashTables. During ngen, these entries get
867// populated to the corresponding TypeHandle. However, at runtime, it is not guaranteed for the module
868// containing the exported type to be loaded. Hence, we cannot use the TypeHandle. Instead, we bash the
869// TypeHandle back to the export token.
870void EEClassHashTable::PrepareExportedTypesForSaving(DataImage *image)
871{
872 CONTRACTL
873 {
874 THROWS;
875 GC_TRIGGERS;
876 PRECONDITION(GetAppDomain()->IsCompilationDomain());
877 PRECONDITION(GetModule()->IsManifest());
878 }
879 CONTRACTL_END
880
881 IMDInternalImport *pImport = GetModule()->GetMDImport();
882
883 HENUMInternalHolder phEnum(pImport);
884 phEnum.EnumInit(mdtExportedType, mdTokenNil);
885 mdToken mdExportedType;
886
887 for (int i = 0; pImport->EnumNext(&phEnum, &mdExportedType); i++)
888 {
889 mdTypeDef typeDef;
890 LPCSTR pszNameSpace, pszName;
891 mdToken mdImpl;
892 DWORD dwFlags;
893 if (FAILED(pImport->GetExportedTypeProps(
894 mdExportedType,
895 &pszNameSpace,
896 &pszName,
897 &mdImpl,
898 &typeDef,
899 &dwFlags)))
900 {
901 THROW_BAD_FORMAT(BFA_NOFIND_EXPORTED_TYPE, GetModule());
902 continue;
903 }
904
905 CorTokenType tokenType = (CorTokenType) TypeFromToken(mdImpl);
906 CONSISTENCY_CHECK(tokenType == mdtFile ||
907 tokenType == mdtAssemblyRef ||
908 tokenType == mdtExportedType);
909
910 // If mdImpl is a file or an assembly, than it points to the location
911 // of the type. If mdImpl is another exported type, then it is the enclosing
912 // exported type for current (nested) type.
913 BOOL isNested = (tokenType == mdtExportedType);
914
915 // ilasm does not consistently set the dwFlags to correctly reflect nesting
916 //CONSISTENCY_CHECK(!isNested || IsTdNested(dwFlags));
917
918 EEClassHashEntry_t * pEntry = NULL;
919
920 if (!isNested)
921 {
922 pEntry = FindItem(pszNameSpace, pszName, FALSE/*nested*/, NULL);
923 }
924 else
925 {
926 PTR_VOID data;
927 LookupContext sContext;
928
929 // This following line finds the 1st "nested" class EEClassHashEntry_t.
930 if ((pEntry = FindItem(pszNameSpace, pszName, TRUE/*nested*/, &sContext)) != NULL)
931 {
932 // The (immediate) encloser of EEClassHashEntry_t (i.e. pEntry) is stored in pEntry->pEncloser.
933 // It needs to be a type of "mdImpl".
934 // "CompareNestedEntryWithExportedType" will check if "pEntry->pEncloser" is a type of "mdImpl",
935 // as well as walking up the enclosing chain.
936 _ASSERTE (TypeFromToken(mdImpl) == mdtExportedType);
937 while ((!GetModule()->GetClassLoader()->CompareNestedEntryWithExportedType(pImport,
938 mdImpl,
939 this,
940 pEntry->GetEncloser()))
941 && (pEntry = FindNextNestedClass(pszNameSpace, pszName, &data, &sContext)) != NULL)
942 {
943 ;
944 }
945 }
946 }
947
948 if (!pEntry) {
949 THROW_BAD_FORMAT(BFA_NOFIND_EXPORTED_TYPE, GetModule());
950 continue;
951 }
952
953 if (((ULONG_PTR)(pEntry->GetData())) & EECLASSHASH_TYPEHANDLE_DISCR)
954 continue;
955
956 TypeHandle th = TypeHandle::FromPtr(pEntry->GetData());
957
958#ifdef _DEBUG
959 MethodTable * pMT = th.GetMethodTable();
960 _ASSERTE(tokenType != mdtFile || pMT->GetModule()->GetModuleRef() == mdImpl);
961 // "typeDef" is just a hint for unsigned assemblies, and ILASM sets it to 0
962 // Hence, we need to relax this assert.
963 _ASSERTE(pMT->GetCl() == typeDef || typeDef == mdTokenNil);
964#endif
965
966 if (image->CanEagerBindToTypeHandle(th) && image->CanHardBindToZapModule(th.GetLoaderModule()))
967 continue;
968
969 // Bash the TypeHandle back to the export token
970 pEntry->SetData(EEClassHashTable::CompressClassDef(mdExportedType));
971 }
972}
973
974void EEClassHashTable::Fixup(DataImage *pImage)
975{
976 STANDARD_VM_CONTRACT;
977
978 // The base class does all the main fixup work. We're called back at FixupEntry below for each entry so we
979 // can fixup additional pointers.
980 BaseFixup(pImage);
981}
982
983void EEClassHashTable::FixupEntry(DataImage *pImage, EEClassHashEntry_t *pEntry, void *pFixupBase, DWORD cbFixupOffset)
984{
985 STANDARD_VM_CONTRACT;
986
987 // Cross-entry references require special fixup. Fortunately they know how to do this themselves.
988 pEntry->m_pEncloser.Fixup(pImage, this);
989
990#ifdef _DEBUG
991 pImage->FixupPointerField(pFixupBase, cbFixupOffset + offsetof(EEClassHashEntry_t, DebugKey[0]));
992 pImage->FixupPointerField(pFixupBase, cbFixupOffset + offsetof(EEClassHashEntry_t, DebugKey[1]));
993#endif // _DEBUG
994
995 // Case insensitive tables and normal hash entries pointing to TypeHandles contain relative pointers.
996 // These don't require runtime fixups (because relative pointers remain constant even in the presence of
997 // base relocations) but we still have to register a fixup of type IMAGE_REL_BASED_RELPTR. This does the
998 // work necessary to update the value of the relative pointer once the ngen image is finally layed out
999 // (the layout process can change the relationship between this field and the target it points to, so we
1000 // don't know the final delta until the image is just about to be written out).
1001 if (m_bCaseInsensitive || ((TADDR)pEntry->GetData() & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
1002 {
1003 pImage->FixupField(pFixupBase,
1004 cbFixupOffset + offsetof(EEClassHashEntry_t, m_Data),
1005 pEntry->GetData(),
1006 0,
1007 IMAGE_REL_BASED_RELPTR);
1008 }
1009}
1010#endif // FEATURE_NATIVE_IMAGE_GENERATION
1011
1012/*===========================MakeCaseInsensitiveTable===========================
1013**Action: Creates a case-insensitive lookup table for class names. We create a
1014** full path (namespace & class name) in lowercase and then use that as the
1015** key in our table. The hash datum is a pointer to the EEClassHashEntry in this
1016** table.
1017**
1018!! You MUST have already acquired the appropriate lock before calling this.!!
1019**
1020**Returns:The newly allocated and completed hashtable.
1021==============================================================================*/
1022
1023class ConstructKeyCallbackCaseInsensitive : public EEClassHashTable::ConstructKeyCallback
1024{
1025public:
1026 virtual void UseKeys(__in_ecount(2) LPUTF8 *key)
1027 {
1028 WRAPPER_NO_CONTRACT;
1029
1030 //Build the cannonical name (convert it to lowercase).
1031 //Key[0] is the namespace, Key[1] is class name.
1032
1033 pLoader->CreateCanonicallyCasedKey(key[0], key[1], ppszLowerNameSpace, ppszLowerClsName);
1034 }
1035
1036 ClassLoader *pLoader;
1037 LPUTF8 *ppszLowerNameSpace;
1038 LPUTF8 *ppszLowerClsName;
1039
1040};
1041
1042EEClassHashTable *EEClassHashTable::MakeCaseInsensitiveTable(Module *pModule, AllocMemTracker *pamTracker)
1043{
1044 EEClassHashEntry_t *pTempEntry;
1045 LPUTF8 pszLowerClsName = NULL;
1046 LPUTF8 pszLowerNameSpace = NULL;
1047
1048 CONTRACTL
1049 {
1050 THROWS;
1051 GC_TRIGGERS;
1052 MODE_ANY;
1053 INJECT_FAULT(COMPlusThrowOM(););
1054
1055 PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED());
1056 }
1057 CONTRACTL_END;
1058
1059
1060
1061 _ASSERTE(!m_pModule.IsNull());
1062 _ASSERTE(pModule == GetModule());
1063
1064 // Allocate the table and verify that we actually got one.
1065 EEClassHashTable * pCaseInsTable = EEClassHashTable::Create(pModule,
1066 max(BaseGetElementCount() / 2, 11),
1067 TRUE /* bCaseInsensitive */,
1068 pamTracker);
1069
1070 // Walk all of the buckets and insert them into our new case insensitive table
1071 BaseIterator sIter;
1072 BaseInitIterator(&sIter);
1073 while ((pTempEntry = sIter.Next()) != NULL)
1074 {
1075 ConstructKeyCallbackCaseInsensitive cback;
1076
1077 cback.pLoader = pModule->GetClassLoader();
1078 cback.ppszLowerNameSpace = &pszLowerNameSpace;
1079 cback.ppszLowerClsName = &pszLowerClsName;
1080 ConstructKeyFromData(pTempEntry, &cback);
1081
1082 //Add the newly created name to our hash table. The hash datum is a pointer
1083 //to the entry associated with that name in this hashtable.
1084 pCaseInsTable->InsertValue(pszLowerNameSpace, pszLowerClsName, (PTR_VOID)pTempEntry, pTempEntry->GetEncloser(), pamTracker);
1085 }
1086
1087 return pCaseInsTable;
1088}
1089#endif // !DACCESS_COMPILE
1090
1091#ifdef DACCESS_COMPILE
1092void EEClassHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
1093{
1094 SUPPORTS_DAC;
1095
1096 // Defer to the base class to do the bulk of this work. It calls EnumMemoryRegionsForEntry below for each
1097 // entry in case we want to enumerate anything extra.
1098 BaseEnumMemoryRegions(flags);
1099}
1100
1101void EEClassHashTable::EnumMemoryRegionsForEntry(EEClassHashEntry_t *pEntry, CLRDataEnumMemoryFlags flags)
1102{
1103 SUPPORTS_DAC;
1104
1105 // Nothing more to enumerate (the base class handles enumeration of the memory for the entry itself).
1106}
1107#endif // DACCESS_COMPILE
1108