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: typehash.cpp |
6 | // |
7 | |
8 | // |
9 | |
10 | #include "common.h" |
11 | #include "excep.h" |
12 | #include "typehash.h" |
13 | #include "eeconfig.h" |
14 | #include "generics.h" |
15 | #include "typestring.h" |
16 | #include "typedesc.h" |
17 | #include "typekey.h" |
18 | #ifdef FEATURE_PREJIT |
19 | #include "zapsig.h" |
20 | #include "compile.h" |
21 | #endif |
22 | #include "ngenhash.inl" |
23 | |
24 | #ifdef _MSC_VER |
25 | #pragma warning(push) |
26 | #pragma warning(disable:4244) |
27 | #endif // _MSC_VER |
28 | |
29 | #ifndef DACCESS_COMPILE |
30 | |
31 | // ============================================================================ |
32 | // Class hash table methods |
33 | // ============================================================================ |
34 | /* static */ |
35 | EETypeHashTable *EETypeHashTable::Create(LoaderAllocator* pAllocator, Module *pModule, DWORD dwNumBuckets, AllocMemTracker *pamTracker) |
36 | { |
37 | CONTRACTL |
38 | { |
39 | THROWS; |
40 | GC_NOTRIGGER; |
41 | MODE_ANY; |
42 | INJECT_FAULT(COMPlusThrowOM();); |
43 | } |
44 | CONTRACTL_END |
45 | |
46 | LoaderHeap *pHeap = pAllocator->GetLowFrequencyHeap(); |
47 | EETypeHashTable *pThis = (EETypeHashTable*)pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(EETypeHashTable))); |
48 | |
49 | new (pThis) EETypeHashTable(pModule, pHeap, dwNumBuckets); |
50 | |
51 | #ifdef _DEBUG |
52 | pThis->InitUnseal(); |
53 | #endif |
54 | |
55 | pThis->m_pAllocator = pAllocator; |
56 | |
57 | return pThis; |
58 | } |
59 | |
60 | LoaderAllocator *EETypeHashTable::GetLoaderAllocator() |
61 | { |
62 | WRAPPER_NO_CONTRACT; |
63 | |
64 | if (m_pAllocator) |
65 | { |
66 | return m_pAllocator; |
67 | } |
68 | else |
69 | { |
70 | _ASSERTE(!m_pModule.IsNull()); |
71 | return GetModule()->GetLoaderAllocator(); |
72 | } |
73 | } |
74 | |
75 | #endif // #ifdef DACCESS_COMPILE |
76 | |
77 | void EETypeHashTable::Iterator::Reset() |
78 | { |
79 | WRAPPER_NO_CONTRACT; |
80 | |
81 | if (m_pTable) |
82 | { |
83 | #ifdef _DEBUG |
84 | m_pTable->Unseal(); |
85 | #endif |
86 | m_pTable = NULL; |
87 | } |
88 | |
89 | Init(); |
90 | } |
91 | |
92 | void EETypeHashTable::Iterator::Init() |
93 | { |
94 | WRAPPER_NO_CONTRACT; |
95 | |
96 | #ifdef _DEBUG |
97 | if (m_pTable) |
98 | m_pTable->Seal(); // The table cannot be changing while it is being iterated |
99 | #endif |
100 | |
101 | m_fIterating = false; |
102 | } |
103 | |
104 | EETypeHashTable::Iterator::Iterator() |
105 | { |
106 | WRAPPER_NO_CONTRACT; |
107 | m_pTable = NULL; |
108 | Init(); |
109 | } |
110 | |
111 | EETypeHashTable::Iterator::Iterator(EETypeHashTable * pTable) |
112 | { |
113 | WRAPPER_NO_CONTRACT; |
114 | m_pTable = pTable; |
115 | Init(); |
116 | } |
117 | |
118 | EETypeHashTable::Iterator::~Iterator() |
119 | { |
120 | WRAPPER_NO_CONTRACT; |
121 | |
122 | #ifdef _DEBUG |
123 | if (m_pTable) |
124 | m_pTable->Unseal(); // Done with the iterator so we unseal |
125 | #endif |
126 | } |
127 | |
128 | BOOL EETypeHashTable::FindNext(Iterator *it, EETypeHashEntry **ppEntry) |
129 | { |
130 | LIMITED_METHOD_CONTRACT; |
131 | |
132 | if (!it->m_fIterating) |
133 | { |
134 | BaseInitIterator(&it->m_sIterator); |
135 | it->m_fIterating = true; |
136 | } |
137 | |
138 | *ppEntry = it->m_sIterator.Next(); |
139 | return *ppEntry ? TRUE : FALSE; |
140 | } |
141 | |
142 | DWORD EETypeHashTable::GetCount() |
143 | { |
144 | LIMITED_METHOD_CONTRACT; |
145 | |
146 | return BaseGetElementCount(); |
147 | } |
148 | |
149 | static DWORD HashTypeHandle(DWORD level, TypeHandle t); |
150 | |
151 | // Calculate hash value for a type def or instantiated type def |
152 | static DWORD HashPossiblyInstantiatedType(DWORD level, mdTypeDef token, Instantiation inst) |
153 | { |
154 | CONTRACTL |
155 | { |
156 | NOTHROW; |
157 | GC_NOTRIGGER; |
158 | MODE_ANY; |
159 | PRECONDITION(TypeFromToken(token) == mdtTypeDef); |
160 | SUPPORTS_DAC; |
161 | } |
162 | CONTRACTL_END |
163 | |
164 | INT_PTR dwHash = 5381; |
165 | |
166 | dwHash = ((dwHash << 5) + dwHash) ^ token; |
167 | if (!inst.IsEmpty()) |
168 | { |
169 | dwHash = ((dwHash << 5) + dwHash) ^ inst.GetNumArgs(); |
170 | |
171 | // Hash two levels of the hiearchy. A simple nesting of generics instantiations is |
172 | // pretty common in generic collections, e.g.: ICollection<KeyValuePair<TKey, TValue>> |
173 | if (level < 2) |
174 | { |
175 | // Hash n type parameters |
176 | for (DWORD i = 0; i < inst.GetNumArgs(); i++) |
177 | { |
178 | dwHash = ((dwHash << 5) + dwHash) ^ HashTypeHandle(level+1, inst[i]); |
179 | } |
180 | } |
181 | } |
182 | |
183 | return dwHash; |
184 | } |
185 | |
186 | // Calculate hash value for a function pointer type |
187 | static DWORD HashFnPtrType(DWORD level, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) |
188 | { |
189 | WRAPPER_NO_CONTRACT; |
190 | SUPPORTS_DAC; |
191 | INT_PTR dwHash = 5381; |
192 | |
193 | dwHash = ((dwHash << 5) + dwHash) ^ ELEMENT_TYPE_FNPTR; |
194 | dwHash = ((dwHash << 5) + dwHash) ^ callConv; |
195 | dwHash = ((dwHash << 5) + dwHash) ^ numArgs; |
196 | if (level < 1) |
197 | { |
198 | for (DWORD i = 0; i <= numArgs; i++) |
199 | { |
200 | dwHash = ((dwHash << 5) + dwHash) ^ HashTypeHandle(level+1, retAndArgTypes[i]); |
201 | } |
202 | } |
203 | |
204 | return dwHash; |
205 | } |
206 | |
207 | // Calculate hash value for an array/pointer/byref type |
208 | static DWORD HashParamType(DWORD level, CorElementType kind, TypeHandle typeParam) |
209 | { |
210 | WRAPPER_NO_CONTRACT; |
211 | INT_PTR dwHash = 5381; |
212 | |
213 | dwHash = ((dwHash << 5) + dwHash) ^ kind; |
214 | dwHash = ((dwHash << 5) + dwHash) ^ HashTypeHandle(level, typeParam); |
215 | |
216 | return dwHash; |
217 | } |
218 | |
219 | // Calculate hash value from type handle |
220 | static DWORD HashTypeHandle(DWORD level, TypeHandle t) |
221 | { |
222 | CONTRACTL |
223 | { |
224 | NOTHROW; |
225 | GC_NOTRIGGER; |
226 | MODE_ANY; |
227 | SO_TOLERANT; |
228 | PRECONDITION(CheckPointer(t)); |
229 | PRECONDITION(!t.IsEncodedFixup()); |
230 | SUPPORTS_DAC; |
231 | } |
232 | CONTRACTL_END; |
233 | |
234 | DWORD retVal = 0; |
235 | |
236 | INTERIOR_STACK_PROBE_NOTHROW_CHECK_THREAD(goto Exit;); |
237 | |
238 | if (t.HasTypeParam()) |
239 | { |
240 | retVal = HashParamType(level, t.GetInternalCorElementType(), t.GetTypeParam()); |
241 | } |
242 | else if (t.IsGenericVariable()) |
243 | { |
244 | retVal = (dac_cast<PTR_TypeVarTypeDesc>(t.AsTypeDesc())->GetToken()); |
245 | } |
246 | else if (t.HasInstantiation()) |
247 | { |
248 | retVal = HashPossiblyInstantiatedType(level, t.GetCl(), t.GetInstantiation()); |
249 | } |
250 | else if (t.IsFnPtrType()) |
251 | { |
252 | FnPtrTypeDesc* pTD = t.AsFnPtrType(); |
253 | retVal = HashFnPtrType(level, pTD->GetCallConv(), pTD->GetNumArgs(), pTD->GetRetAndArgTypesPointer()); |
254 | } |
255 | else |
256 | retVal = HashPossiblyInstantiatedType(level, t.GetCl(), Instantiation()); |
257 | |
258 | #if defined(FEATURE_STACK_PROBE) && !defined(DACCESS_COMPILE) |
259 | Exit: |
260 | ; |
261 | #endif |
262 | END_INTERIOR_STACK_PROBE; |
263 | |
264 | return retVal; |
265 | } |
266 | |
267 | // Calculate hash value from key |
268 | static DWORD HashTypeKey(TypeKey* pKey) |
269 | { |
270 | CONTRACTL |
271 | { |
272 | NOTHROW; |
273 | GC_NOTRIGGER; |
274 | MODE_ANY; |
275 | PRECONDITION(CheckPointer(pKey)); |
276 | SUPPORTS_DAC; |
277 | } |
278 | CONTRACTL_END; |
279 | |
280 | if (pKey->GetKind() == ELEMENT_TYPE_CLASS) |
281 | { |
282 | return HashPossiblyInstantiatedType(0, pKey->GetTypeToken(), pKey->GetInstantiation()); |
283 | } |
284 | else if (pKey->GetKind() == ELEMENT_TYPE_FNPTR) |
285 | { |
286 | return HashFnPtrType(0, pKey->GetCallConv(), pKey->GetNumArgs(), pKey->GetRetAndArgTypes()); |
287 | } |
288 | else |
289 | { |
290 | return HashParamType(0, pKey->GetKind(), pKey->GetElementType()); |
291 | } |
292 | } |
293 | |
294 | // Look up a value in the hash table |
295 | // |
296 | // The logic is subtle: type handles in the hash table may not be |
297 | // restored, but we need to compare components of the types (rank and |
298 | // element type for arrays, generic type and instantiation for |
299 | // instantiated types) against pKey |
300 | // |
301 | // We avoid restoring types during search by cracking the signature |
302 | // encoding used by the zapper for out-of-module types e.g. in the |
303 | // instantiation of an instantiated type. |
304 | EETypeHashEntry_t *EETypeHashTable::FindItem(TypeKey* pKey) |
305 | { |
306 | CONTRACTL |
307 | { |
308 | INSTANCE_CHECK; |
309 | NOTHROW; |
310 | GC_NOTRIGGER; |
311 | MODE_ANY; |
312 | PRECONDITION(CheckPointer(pKey)); |
313 | SUPPORTS_DAC; |
314 | } |
315 | CONTRACTL_END; |
316 | |
317 | EETypeHashEntry_t * result = NULL; |
318 | |
319 | DWORD dwHash = HashTypeKey(pKey); |
320 | EETypeHashEntry_t * pSearch; |
321 | CorElementType kind = pKey->GetKind(); |
322 | LookupContext sContext; |
323 | |
324 | if (kind == ELEMENT_TYPE_CLASS) |
325 | { |
326 | pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); |
327 | while (pSearch) |
328 | { |
329 | if (CompareInstantiatedType(pSearch->GetTypeHandle(), pKey->GetModule(), pKey->GetTypeToken(), pKey->GetInstantiation())) |
330 | { |
331 | result = pSearch; |
332 | break; |
333 | } |
334 | |
335 | pSearch = BaseFindNextEntryByHash(&sContext); |
336 | } |
337 | } |
338 | else if (kind == ELEMENT_TYPE_FNPTR) |
339 | { |
340 | BYTE callConv = pKey->GetCallConv(); |
341 | DWORD numArgs = pKey->GetNumArgs(); |
342 | TypeHandle *retAndArgTypes = pKey->GetRetAndArgTypes(); |
343 | |
344 | pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); |
345 | while (pSearch) |
346 | { |
347 | if (CompareFnPtrType(pSearch->GetTypeHandle(), callConv, numArgs, retAndArgTypes)) |
348 | { |
349 | result = pSearch; |
350 | break; |
351 | } |
352 | |
353 | pSearch = BaseFindNextEntryByHash(&sContext); |
354 | } |
355 | } |
356 | else |
357 | { |
358 | // Type parameters for array and pointer types are necessarily in the same loader module |
359 | // as the constructed type itself, so we can just do handle comparisons |
360 | // Unfortunately the rank of the array might live elsewhere |
361 | |
362 | for (pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); |
363 | pSearch != NULL; |
364 | pSearch = BaseFindNextEntryByHash(&sContext)) |
365 | { |
366 | if (!pSearch->GetTypeHandle().IsRestored()) |
367 | { |
368 | // workaround: If we encounter an unrestored MethodTable, then it |
369 | // isn't the type for which we are looking (plus, it will crash |
370 | // in GetSignatureCorElementType). However TypeDescs can be |
371 | // accessed when unrestored. Also they are accessed in that |
372 | // manner at startup when we're loading the global types |
373 | // (i.e. System.Object). |
374 | |
375 | if (!pSearch->GetTypeHandle().IsTypeDesc()) |
376 | { |
377 | // Not a match |
378 | continue; |
379 | } |
380 | else |
381 | { |
382 | // We have an unrestored TypeDesc |
383 | } |
384 | } |
385 | |
386 | if (pSearch->GetTypeHandle().GetSignatureCorElementType() != kind) |
387 | continue; |
388 | |
389 | if (pSearch->GetTypeHandle().GetTypeParam() != pKey->GetElementType()) |
390 | continue; |
391 | |
392 | if (pSearch->GetTypeHandle().IsTypeDesc() == pKey->IsTemplateMethodTable()) |
393 | continue; |
394 | |
395 | if (kind == ELEMENT_TYPE_ARRAY) |
396 | { |
397 | if (pKey->IsTemplateMethodTable()) |
398 | { |
399 | if (pSearch->GetTypeHandle().AsMethodTable()->GetRank() != pKey->GetRank()) |
400 | continue; |
401 | } |
402 | else |
403 | { |
404 | ArrayTypeDesc *pATD = pSearch->GetTypeHandle().AsArray(); |
405 | #ifdef FEATURE_PREJIT |
406 | // This ensures that GetAssemblyIfLoaded operations that may be triggered by signature walks will succeed if at all possible. |
407 | ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); |
408 | |
409 | TADDR fixup = pATD->GetTemplateMethodTableMaybeTagged(); |
410 | if (!CORCOMPILE_IS_POINTER_TAGGED(fixup)) |
411 | { |
412 | TADDR canonFixup = pATD->GetTemplateMethodTable()->GetCanonicalMethodTableFixup(); |
413 | if (CORCOMPILE_IS_POINTER_TAGGED(canonFixup)) |
414 | fixup = canonFixup; |
415 | } |
416 | |
417 | if (CORCOMPILE_IS_POINTER_TAGGED(fixup)) |
418 | { |
419 | Module *pDefiningModule; |
420 | PCCOR_SIGNATURE pSig = GetModule()->GetEncodedSigIfLoaded(CORCOMPILE_UNTAG_TOKEN(fixup), &pDefiningModule); |
421 | if (pDefiningModule == NULL) |
422 | break; |
423 | |
424 | _ASSERTE(*pSig == ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG); |
425 | pSig++; |
426 | _ASSERTE(*pSig == ELEMENT_TYPE_ARRAY); |
427 | pSig++; |
428 | SigPointer sp(pSig); |
429 | if (FAILED(sp.SkipExactlyOne())) |
430 | break; // return NULL; |
431 | |
432 | ULONG data; |
433 | if (FAILED(sp.GetData(&data))) |
434 | break; // return NULL; |
435 | |
436 | if (data != pKey->GetRank()) |
437 | continue; |
438 | } |
439 | else |
440 | #endif //FEATURE_PREJIT |
441 | { |
442 | if (pATD->GetRank() != pKey->GetRank()) |
443 | continue; |
444 | } |
445 | } |
446 | } |
447 | |
448 | result = pSearch; |
449 | break; |
450 | } |
451 | } |
452 | |
453 | return result; |
454 | } |
455 | |
456 | BOOL EETypeHashTable::CompareInstantiatedType(TypeHandle t, Module *pModule, mdTypeDef token, Instantiation inst) |
457 | { |
458 | CONTRACTL |
459 | { |
460 | INSTANCE_CHECK; |
461 | NOTHROW; |
462 | GC_NOTRIGGER; |
463 | MODE_ANY; |
464 | PRECONDITION(CheckPointer(t)); |
465 | PRECONDITION(CheckPointer(pModule)); |
466 | PRECONDITION(!inst.IsEmpty()); |
467 | SUPPORTS_DAC; |
468 | } |
469 | CONTRACTL_END |
470 | |
471 | if (t.IsTypeDesc()) |
472 | return FALSE; |
473 | |
474 | // Even the EEClass pointer might be encoded |
475 | MethodTable * pMT = t.AsMethodTable(); |
476 | |
477 | if (pMT->GetNumGenericArgs() != inst.GetNumArgs()) |
478 | return FALSE; |
479 | |
480 | #ifdef FEATURE_PREJIT |
481 | // This ensures that GetAssemblyIfLoaded operations that may be triggered by signature walks will succeed if at all possible. |
482 | ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); |
483 | |
484 | TADDR fixup = pMT->GetCanonicalMethodTableFixup(); |
485 | |
486 | // The EEClass pointer is actually an encoding. |
487 | if (CORCOMPILE_IS_POINTER_TAGGED(fixup)) |
488 | { |
489 | Module *pDefiningModule; |
490 | |
491 | PCCOR_SIGNATURE pSig = GetModule()->GetEncodedSigIfLoaded(CORCOMPILE_UNTAG_TOKEN(fixup), &pDefiningModule); |
492 | |
493 | // First check that the modules for the generic type defs match |
494 | if (dac_cast<TADDR>(pDefiningModule) != |
495 | dac_cast<TADDR>(pModule)) |
496 | return FALSE; |
497 | |
498 | // Now crack the signature encoding, expected to be an instantiated type |
499 | _ASSERTE(*pSig == ELEMENT_TYPE_GENERICINST); |
500 | pSig++; |
501 | _ASSERTE(*pSig == ELEMENT_TYPE_CLASS || *pSig == ELEMENT_TYPE_VALUETYPE); |
502 | pSig++; |
503 | |
504 | // Check that the tokens of the generic type def match |
505 | if (CorSigUncompressToken(pSig) != token) |
506 | return FALSE; |
507 | } |
508 | |
509 | // The EEClass pointer is a real pointer |
510 | else |
511 | #endif //FEATURE_PREJIT |
512 | { |
513 | // First check that the typedef tokens match |
514 | if (pMT->GetCl() != token) |
515 | return FALSE; |
516 | |
517 | // The class might not be restored, and its metadata module pointer might be encoded. |
518 | // This will return NULL if the module for the corresponding generic class |
519 | // is not loaded. |
520 | Module *pGenericModuleIfLoaded = pMT->GetModuleIfLoaded(); |
521 | |
522 | // Now check that the modules match |
523 | if (!pGenericModuleIfLoaded || |
524 | dac_cast<TADDR>(pGenericModuleIfLoaded) != |
525 | dac_cast<TADDR>(pModule)) |
526 | return FALSE; |
527 | |
528 | } |
529 | |
530 | Instantiation candidateInst = t.GetInstantiation(); |
531 | |
532 | // Now check the instantiations. Some type arguments might be encoded. |
533 | for (DWORD i = 0; i < inst.GetNumArgs(); i++) |
534 | { |
535 | // Fetch the type handle as TADDR. It may be may be encoded fixup - TypeHandle debug-only validation |
536 | // asserts on encoded fixups. |
537 | DACCOP_IGNORE(CastOfMarshalledType, "Dual mode DAC problem, but since the size is the same, the cast is safe" ); |
538 | TADDR candidateArg = ((FixupPointer<TADDR> *)candidateInst.GetRawArgs())[i].GetValue(); |
539 | |
540 | if (!ZapSig::CompareTaggedPointerToTypeHandle(GetModule(), candidateArg, inst[i])) |
541 | { |
542 | return FALSE; |
543 | } |
544 | } |
545 | |
546 | return TRUE; |
547 | } |
548 | |
549 | BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArgs, TypeHandle *retAndArgTypes) |
550 | { |
551 | CONTRACTL |
552 | { |
553 | INSTANCE_CHECK; |
554 | NOTHROW; |
555 | GC_NOTRIGGER; |
556 | MODE_ANY; |
557 | PRECONDITION(CheckPointer(t)); |
558 | PRECONDITION(CheckPointer(retAndArgTypes)); |
559 | SUPPORTS_DAC; |
560 | } |
561 | CONTRACTL_END |
562 | |
563 | if (!t.IsFnPtrType()) |
564 | return FALSE; |
565 | |
566 | #ifndef DACCESS_COMPILE |
567 | #ifdef FEATURE_PREJIT |
568 | // This ensures that GetAssemblyIfLoaded operations that may be triggered by signature walks will succeed if at all possible. |
569 | ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); |
570 | #endif |
571 | |
572 | FnPtrTypeDesc* pTD = t.AsFnPtrType(); |
573 | |
574 | if (pTD->GetNumArgs() != numArgs || pTD->GetCallConv() != callConv) |
575 | return FALSE; |
576 | |
577 | // Now check the return and argument types. Some type arguments might be encoded. |
578 | TypeHandle *retAndArgTypes2 = pTD->GetRetAndArgTypesPointer(); |
579 | for (DWORD i = 0; i <= numArgs; i++) |
580 | { |
581 | TADDR candidateArg = retAndArgTypes2[i].AsTAddr(); |
582 | if (!ZapSig::CompareTaggedPointerToTypeHandle(GetModule(), candidateArg, retAndArgTypes[i])) |
583 | { |
584 | return FALSE; |
585 | } |
586 | } |
587 | |
588 | return TRUE; |
589 | |
590 | #else |
591 | DacNotImpl(); |
592 | return FALSE; |
593 | #endif // #ifndef DACCESS_COMPILE |
594 | } |
595 | |
596 | TypeHandle EETypeHashTable::GetValue(TypeKey *pKey) |
597 | { |
598 | CONTRACTL |
599 | { |
600 | NOTHROW; |
601 | GC_NOTRIGGER; |
602 | MODE_ANY; |
603 | SUPPORTS_DAC; |
604 | } |
605 | CONTRACTL_END; |
606 | |
607 | EETypeHashEntry_t *pItem = FindItem(pKey); |
608 | |
609 | if (pItem) |
610 | { |
611 | TypeHandle th = pItem->GetTypeHandle(); |
612 | g_IBCLogger.LogTypeHashTableAccess(&th); |
613 | return pItem->GetTypeHandle(); |
614 | } |
615 | else |
616 | return TypeHandle(); |
617 | } |
618 | |
619 | #ifndef DACCESS_COMPILE |
620 | |
621 | BOOL EETypeHashTable::ContainsValue(TypeHandle th) |
622 | { |
623 | CONTRACTL |
624 | { |
625 | NOTHROW; |
626 | GC_NOTRIGGER; |
627 | SO_INTOLERANT; |
628 | MODE_ANY; |
629 | } |
630 | CONTRACTL_END; |
631 | |
632 | TypeKey typeKey = th.GetTypeKey(); |
633 | return !GetValue(&typeKey).IsNull(); |
634 | } |
635 | |
636 | // Insert a value not already in the hash table |
637 | VOID EETypeHashTable::InsertValue(TypeHandle data) |
638 | { |
639 | CONTRACTL |
640 | { |
641 | INSTANCE_CHECK; |
642 | THROWS; |
643 | GC_NOTRIGGER; |
644 | MODE_ANY; |
645 | INJECT_FAULT(COMPlusThrowOM();); |
646 | PRECONDITION(IsUnsealed()); // If we are sealed then we should not be adding to this hashtable |
647 | PRECONDITION(CheckPointer(data)); |
648 | PRECONDITION(!data.IsEncodedFixup()); |
649 | PRECONDITION(!data.IsGenericTypeDefinition()); // Generic type defs live in typedef table (availableClasses) |
650 | PRECONDITION(data.HasInstantiation() || data.HasTypeParam() || data.IsFnPtrType()); // It's an instantiated type or an array/ptr/byref type |
651 | PRECONDITION(m_pModule.IsNull() || GetModule()->IsTenured()); // Destruct won't destruct m_pAvailableParamTypes for non-tenured modules - so make sure no one tries to insert one before the Module has been tenured |
652 | } |
653 | CONTRACTL_END |
654 | |
655 | EETypeHashEntry_t * pNewEntry = (EETypeHashEntry_t*)BaseAllocateEntry(NULL); |
656 | |
657 | pNewEntry->SetTypeHandle(data); |
658 | |
659 | BaseInsertEntry(HashTypeHandle(0, data), pNewEntry); |
660 | } |
661 | |
662 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
663 | |
664 | #ifdef _DEBUG |
665 | void EETypeHashTableSeal(EETypeHashTable * pTable) { WRAPPER_NO_CONTRACT; pTable->Seal(); } |
666 | void EETypeHashTableUnseal(EETypeHashTable * pTable) { WRAPPER_NO_CONTRACT; pTable->Unseal(); } |
667 | typedef Wrapper<EETypeHashTable *, EETypeHashTableSeal, EETypeHashTableUnseal> EETypeHashTableSealHolder; |
668 | #endif |
669 | |
670 | // Save the hash table and any type descriptors referenced by it |
671 | // Method tables must be saved separately |
672 | void EETypeHashTable::Save(DataImage *image, Module *module, CorProfileData *profileData) |
673 | { |
674 | CONTRACTL |
675 | { |
676 | STANDARD_VM_CHECK; |
677 | PRECONDITION(image->GetModule() == GetModule()); |
678 | } |
679 | CONTRACTL_END; |
680 | |
681 | #ifdef _DEBUG |
682 | // The table should not change while we are walking the buckets |
683 | EETypeHashTableSealHolder h(this); |
684 | #endif |
685 | |
686 | // The base class will call us back for every entry to see if it's considered hot. To determine this we |
687 | // have to walk through the profiling data. It's very inefficient for us to do this every time. Instead |
688 | // we'll walk the data once just now and mark each hot entry as we find it. |
689 | CORBBTPROF_TOKEN_INFO * pTypeProfilingData = profileData->GetTokenFlagsData(TypeProfilingData); |
690 | DWORD cTypeProfilingData = profileData->GetTokenFlagsCount(TypeProfilingData); |
691 | |
692 | for (unsigned int i = 0; i < cTypeProfilingData; i++) |
693 | { |
694 | CORBBTPROF_TOKEN_INFO *entry = &pTypeProfilingData[i]; |
695 | mdToken token = entry->token; |
696 | DWORD flags = entry->flags; |
697 | |
698 | if (TypeFromToken(token) != ibcTypeSpec) |
699 | continue; |
700 | |
701 | if ((flags & (1 << ReadTypeHashTable)) == 0) |
702 | continue; |
703 | |
704 | CORBBTPROF_BLOB_ENTRY *pBlobEntry = profileData->GetBlobStream(); |
705 | if (pBlobEntry) |
706 | { |
707 | while (pBlobEntry->TypeIsValid()) |
708 | { |
709 | if (TypeFromToken(pBlobEntry->token) == ibcTypeSpec) |
710 | { |
711 | _ASSERTE(pBlobEntry->type == ParamTypeSpec); |
712 | |
713 | CORBBTPROF_BLOB_PARAM_SIG_ENTRY *pBlobSigEntry = (CORBBTPROF_BLOB_PARAM_SIG_ENTRY *) pBlobEntry; |
714 | |
715 | if (pBlobEntry->token == token) |
716 | { |
717 | if (flags & (1<<ReadTypeHashTable)) |
718 | { |
719 | TypeHandle th = GetModule()->LoadIBCTypeHelper(image, pBlobSigEntry); |
720 | #if defined(_DEBUG) && !defined(DACCESS_COMPILE) |
721 | g_pConfig->DebugCheckAndForceIBCFailure(EEConfig::CallSite_8); |
722 | #endif |
723 | if (!th.IsNull()) |
724 | { |
725 | // Found a hot type. See if we have it in our table. |
726 | DWORD dwHash = HashTypeHandle(0, th); |
727 | LookupContext sContext; |
728 | EETypeHashEntry_t *pSearch = BaseFindFirstEntryByHash(dwHash, &sContext); |
729 | while (pSearch) |
730 | { |
731 | if (pSearch->GetTypeHandle() == th) |
732 | { |
733 | // Found the corresponding entry in the table. Mark it as hot. |
734 | pSearch->MarkAsHot(); |
735 | break; |
736 | } |
737 | |
738 | pSearch = BaseFindNextEntryByHash(&sContext); |
739 | } |
740 | } |
741 | } |
742 | } |
743 | } |
744 | pBlobEntry = pBlobEntry->GetNextEntry(); |
745 | } |
746 | } |
747 | } |
748 | |
749 | BaseSave(image, profileData); |
750 | } |
751 | |
752 | bool EETypeHashTable::ShouldSave(DataImage *pImage, EETypeHashEntry_t *pEntry) |
753 | { |
754 | STANDARD_VM_CONTRACT; |
755 | |
756 | return !!pImage->GetPreloader()->IsTypeInTransitiveClosureOfInstantiations(CORINFO_CLASS_HANDLE(pEntry->GetTypeHandle().AsPtr())); |
757 | } |
758 | |
759 | bool EETypeHashTable::IsHotEntry(EETypeHashEntry_t *pEntry, CorProfileData *pProfileData) |
760 | { |
761 | STANDARD_VM_CONTRACT; |
762 | |
763 | // EETypeHashTable::Save() will have marked the entry as hot if the profile data indicated this. |
764 | return pEntry->IsHot(); |
765 | } |
766 | |
767 | bool EETypeHashTable::SaveEntry(DataImage *pImage, CorProfileData *pProfileData, EETypeHashEntry_t *pOldEntry, EETypeHashEntry_t *pNewEntry, EntryMappingTable *pMap) |
768 | { |
769 | LIMITED_METHOD_CONTRACT; |
770 | |
771 | return false; |
772 | } |
773 | |
774 | void EETypeHashTable::Fixup(DataImage *image) |
775 | { |
776 | STANDARD_VM_CONTRACT; |
777 | |
778 | BaseFixup(image); |
779 | |
780 | image->ZeroPointerField(this, offsetof(EETypeHashTable, m_pAllocator)); |
781 | |
782 | #ifdef _DEBUG |
783 | // The persisted table should be unsealed. |
784 | EETypeHashTable *pNewTable = (EETypeHashTable*) image->GetImagePointer(this); |
785 | pNewTable->InitUnseal(); |
786 | #endif |
787 | } |
788 | |
789 | void EETypeHashTable::FixupEntry(DataImage *pImage, EETypeHashEntry_t *pEntry, void *pFixupBase, DWORD cbFixupOffset) |
790 | { |
791 | STANDARD_VM_CONTRACT; |
792 | |
793 | TypeHandle pType = pEntry->GetTypeHandle(); |
794 | _ASSERTE(!pType.IsNull()); |
795 | |
796 | // Clear any hot entry marking in the data, it's not needed after the Save phase. |
797 | pEntry->SetTypeHandle(pType); |
798 | |
799 | if (pType.IsTypeDesc()) |
800 | { |
801 | pImage->FixupField(pFixupBase, cbFixupOffset + offsetof(EETypeHashEntry_t, m_data), |
802 | pType.AsTypeDesc(), 2, IMAGE_REL_BASED_RelativePointer); |
803 | |
804 | pType.AsTypeDesc()->Fixup(pImage); |
805 | } |
806 | else |
807 | { |
808 | pImage->FixupField(pFixupBase, cbFixupOffset + offsetof(EETypeHashEntry_t, m_data), |
809 | pType.AsMethodTable(), 0, IMAGE_REL_BASED_RelativePointer); |
810 | |
811 | pType.AsMethodTable()->Fixup(pImage); |
812 | } |
813 | } |
814 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
815 | |
816 | #endif // #ifndef DACCESS_COMPILE |
817 | |
818 | #ifdef DACCESS_COMPILE |
819 | |
820 | void |
821 | EETypeHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
822 | { |
823 | SUPPORTS_DAC; |
824 | |
825 | BaseEnumMemoryRegions(flags); |
826 | } |
827 | |
828 | void EETypeHashTable::EnumMemoryRegionsForEntry(EETypeHashEntry_t *pEntry, CLRDataEnumMemoryFlags flags) |
829 | { |
830 | SUPPORTS_DAC; |
831 | |
832 | pEntry->GetTypeHandle().EnumMemoryRegions(flags); |
833 | } |
834 | |
835 | #endif // #ifdef DACCESS_COMPILE |
836 | |
837 | TypeHandle EETypeHashEntry::GetTypeHandle() |
838 | { |
839 | LIMITED_METHOD_DAC_CONTRACT; |
840 | |
841 | // Remove any hot entry indicator bit that may have been set as the result of Ngen saving. |
842 | TADDR data = dac_cast<TADDR>(GetData()); |
843 | return TypeHandle::FromTAddr(data & ~0x1); |
844 | } |
845 | |
846 | #ifndef DACCESS_COMPILE |
847 | void EETypeHashEntry::SetTypeHandle(TypeHandle handle) |
848 | { |
849 | LIMITED_METHOD_DAC_CONTRACT; |
850 | |
851 | // We plan to steal the low-order bit of the handle for ngen purposes. |
852 | _ASSERTE((handle.AsTAddr() & 0x1) == 0); |
853 | m_data.SetValueMaybeNull(handle.AsPtr()); |
854 | } |
855 | #endif // !DACCESS_COMPILE |
856 | |
857 | #ifdef FEATURE_PREJIT |
858 | bool EETypeHashEntry::IsHot() |
859 | { |
860 | LIMITED_METHOD_CONTRACT; |
861 | |
862 | // Low order bit of data field indicates a hot entry. |
863 | TADDR data = dac_cast<TADDR>(GetData()); |
864 | return (data & 1) != 0; |
865 | } |
866 | |
867 | #ifndef DACCESS_COMPILE |
868 | void EETypeHashEntry::MarkAsHot() |
869 | { |
870 | LIMITED_METHOD_CONTRACT; |
871 | |
872 | // Low order bit of data field indicates a hot entry. |
873 | TADDR data = dac_cast<TADDR>(GetData()); |
874 | data |= 0x1; |
875 | m_data.SetValueMaybeNull(dac_cast<PTR_VOID>(data)); |
876 | } |
877 | #endif // !DACCESS_COMPILE |
878 | #endif // FEATURE_PREJIT |
879 | |
880 | #ifdef _MSC_VER |
881 | #pragma warning(pop) |
882 | #endif // _MSC_VER: warning C4244 |
883 | |