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: clsload.cpp
6//
7// ============================================================================
8
9#include "common.h"
10#include "winwrap.h"
11#include "ceeload.h"
12#include "siginfo.hpp"
13#include "vars.hpp"
14#include "clsload.hpp"
15#include "classhash.inl"
16#include "class.h"
17#include "method.hpp"
18#include "ecall.h"
19#include "stublink.h"
20#include "object.h"
21#include "excep.h"
22#include "threads.h"
23#include "comsynchronizable.h"
24#include "threads.h"
25#include "dllimport.h"
26#include "dbginterface.h"
27#include "log.h"
28#include "eeconfig.h"
29#include "fieldmarshaler.h"
30#include "jitinterface.h"
31#include "vars.hpp"
32#include "assembly.hpp"
33#include "perfcounters.h"
34#include "eeprofinterfaces.h"
35#include "eehash.h"
36#include "typehash.h"
37#include "comdelegate.h"
38#include "array.h"
39#include "stackprobe.h"
40#include "posterror.h"
41#include "wrappers.h"
42#include "generics.h"
43#include "typestring.h"
44#include "typedesc.h"
45#include "cgencpu.h"
46#include "eventtrace.h"
47#include "typekey.h"
48#include "pendingload.h"
49#include "proftoeeinterfaceimpl.h"
50#include "mdaassistants.h"
51#include "virtualcallstub.h"
52#include "stringarraylist.h"
53
54
55// This method determines the "loader module" for an instantiated type
56// or method. The rule must ensure that any types involved in the
57// instantiated type or method do not outlive the loader module itself
58// with respect to app-domain unloading (e.g. MyList<MyType> can't be
59// put in the module of MyList if MyList's assembly is
60// app-domain-neutral but MyType's assembly is app-domain-specific).
61// The rule we use is:
62//
63// * Pick the first type in the class instantiation, followed by
64// method instantiation, whose loader module is non-shared (app-domain-bound)
65// * If no type is app-domain-bound, return the module containing the generic type itself
66//
67// Some useful effects of this rule (for ngen purposes) are:
68//
69// * G<object,...,object> lives in the module defining G
70// * non-mscorlib instantiations of mscorlib-defined generic types live in the module
71// of the instantiation (when only one module is invloved in the instantiation)
72//
73
74/* static */
75PTR_Module ClassLoader::ComputeLoaderModuleWorker(
76 Module * pDefinitionModule, // the module that declares the generic type or method
77 mdToken token, // method or class token for this item
78 Instantiation classInst, // the type arguments to the type (if any)
79 Instantiation methodInst) // the type arguments to the method (if any)
80{
81 CONTRACT(Module*)
82 {
83 NOTHROW;
84 GC_NOTRIGGER;
85 FORBID_FAULT;
86 MODE_ANY;
87 PRECONDITION(CheckPointer(pDefinitionModule, NULL_OK));
88 POSTCONDITION(CheckPointer(RETVAL));
89 SO_INTOLERANT;
90 SUPPORTS_DAC;
91 }
92 CONTRACT_END
93
94 if (classInst.IsEmpty() && methodInst.IsEmpty())
95 RETURN PTR_Module(pDefinitionModule);
96
97#ifndef DACCESS_COMPILE
98#ifdef FEATURE_NATIVE_IMAGE_GENERATION
99 //
100 // Use special loader module placement during compilation of fragile native images.
101 //
102 // ComputeLoaderModuleForCompilation algorithm assumes that we are using fragile native image
103 // for CoreLib (or compiling CoreLib itself). It is not the case for ReadyToRun compilation because
104 // CoreLib as always treated as IL there (see code:PEFile::ShouldTreatNIAsMSIL for details).
105 //
106 if (IsCompilationProcess() && !IsReadyToRunCompilation())
107 {
108 RETURN(ComputeLoaderModuleForCompilation(pDefinitionModule, token, classInst, methodInst));
109 }
110#endif // FEATURE_PREJIT
111#endif // #ifndef DACCESS_COMPILE
112
113 Module *pLoaderModule = NULL;
114
115 if (pDefinitionModule)
116 {
117 if (pDefinitionModule->IsCollectible())
118 goto ComputeCollectibleLoaderModule;
119 pLoaderModule = pDefinitionModule;
120 }
121
122 for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
123 {
124 TypeHandle classArg = classInst[i];
125 _ASSERTE(!classArg.IsEncodedFixup());
126 Module* pModule = classArg.GetLoaderModule();
127 if (pModule->IsCollectible())
128 goto ComputeCollectibleLoaderModule;
129 if (pLoaderModule == NULL)
130 pLoaderModule = pModule;
131 }
132
133 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
134 {
135 TypeHandle methodArg = methodInst[i];
136 _ASSERTE(!methodArg.IsEncodedFixup());
137 Module *pModule = methodArg.GetLoaderModule();
138 if (pModule->IsCollectible())
139 goto ComputeCollectibleLoaderModule;
140 if (pLoaderModule == NULL)
141 pLoaderModule = pModule;
142 }
143
144 if (pLoaderModule == NULL)
145 {
146 CONSISTENCY_CHECK(MscorlibBinder::GetModule() && MscorlibBinder::GetModule()->IsSystem());
147
148 pLoaderModule = MscorlibBinder::GetModule();
149 }
150
151 if (FALSE)
152 {
153ComputeCollectibleLoaderModule:
154 LoaderAllocator *pLoaderAllocatorOfDefiningType = NULL;
155 LoaderAllocator *pOldestLoaderAllocator = NULL;
156 Module *pOldestLoaderModule = NULL;
157 UINT64 oldestFoundAge = 0;
158 DWORD classArgsCount = classInst.GetNumArgs();
159 DWORD totalArgsCount = classArgsCount + methodInst.GetNumArgs();
160
161 if (pDefinitionModule != NULL) pLoaderAllocatorOfDefiningType = pDefinitionModule->GetLoaderAllocator();
162
163 for (DWORD i = 0; i < totalArgsCount; i++) {
164
165 TypeHandle arg;
166
167 if (i < classArgsCount)
168 arg = classInst[i];
169 else
170 arg = methodInst[i - classArgsCount];
171
172 Module *pModuleCheck = arg.GetLoaderModule();
173 LoaderAllocator *pLoaderAllocatorCheck = pModuleCheck->GetLoaderAllocator();
174
175 if (pLoaderAllocatorCheck != pLoaderAllocatorOfDefiningType &&
176 pLoaderAllocatorCheck->IsCollectible() &&
177 pLoaderAllocatorCheck->GetCreationNumber() > oldestFoundAge)
178 {
179 pOldestLoaderModule = pModuleCheck;
180 pOldestLoaderAllocator = pLoaderAllocatorCheck;
181 oldestFoundAge = pLoaderAllocatorCheck->GetCreationNumber();
182 }
183 }
184
185 // Only if we didn't find a different loader allocator than the defining loader allocator do we
186 // use the defining loader allocator
187 if (pOldestLoaderModule != NULL)
188 pLoaderModule = pOldestLoaderModule;
189 else
190 pLoaderModule = pDefinitionModule;
191 }
192 RETURN PTR_Module(pLoaderModule);
193}
194
195#ifndef DACCESS_COMPILE
196#ifdef FEATURE_NATIVE_IMAGE_GENERATION
197/* static */
198PTR_Module ClassLoader::ComputeLoaderModuleForCompilation(
199 Module * pDefinitionModule, // the module that declares the generic type or method
200 mdToken token, // method or class token for this item
201 Instantiation classInst, // the type arguments to the type (if any)
202 Instantiation methodInst) // the type arguments to the method (if any)
203{
204 CONTRACT(Module*)
205 {
206 NOTHROW;
207 GC_NOTRIGGER;
208 FORBID_FAULT;
209 MODE_ANY;
210 PRECONDITION(CheckPointer(pDefinitionModule, NULL_OK));
211 POSTCONDITION(CheckPointer(RETVAL));
212 SO_INTOLERANT;
213 }
214 CONTRACT_END
215
216 // The NGEN rule for compiling constructed types and instantiated methods
217 // into modules other than their "natural" LoaderModule. This is at the heart of
218 // "full generics NGEN".
219 //
220 // If this instantiation doesn't have a unique home then use the ngen module
221
222 // OK, we're certainly NGEN'ing. And if we're NGEN'ing then we're not on the debugger thread.
223 CONSISTENCY_CHECK(((GetThread() && GetAppDomain()) || IsGCThread()) &&
224 "unexpected: running a load on debug thread but IsCompilationProcess() returned TRUE");
225
226 // Save it into its PreferredZapModule if it's always going to be saved there.
227 // This is a stable choice - no need to record it in the table (as we do for others below)
228 if (Module::IsAlwaysSavedInPreferredZapModule(classInst, methodInst))
229 {
230 RETURN (Module::ComputePreferredZapModule(pDefinitionModule, classInst, methodInst));
231 }
232
233 // Check if this compilation process has already decided on an adjustment. Once we decide
234 // on the LoaderModule for an item it must be stable for the duration of a
235 // compilation process, no matter how many modules get NGEN'd.
236
237 ZapperLoaderModuleTableKey key(pDefinitionModule,
238 token,
239 classInst,
240 methodInst);
241
242 Module * pZapperLoaderModule = g_pCEECompileInfo->LookupZapperLoaderModule(&key);
243 if (pZapperLoaderModule != NULL)
244 {
245 RETURN (pZapperLoaderModule);
246 }
247
248 // OK, we need to compute a non-standard zapping module.
249
250 Module * pPreferredZapModule = Module::ComputePreferredZapModule(pDefinitionModule, classInst, methodInst);
251
252 // Check if we're NGEN'ing but where perhaps the compilation domain
253 // isn't set up yet. This can happen in following situations:
254 // - Managed code running during startup before compilation domain is setup.
255 // - Exceptions (e.g. invalid program exceptions) thrown from compilation domain and caught in default domain
256
257 // We're a little stuck - we can't force the item into an NGEN image at this point. So just bail out
258 // and use the loader module we've computed without recording the choice. The loader module should always
259 // be mscorlib in this case.
260 AppDomain * pAppDomain = GetAppDomain();
261 if (!pAppDomain->IsCompilationDomain() ||
262 !pAppDomain->ToCompilationDomain()->GetTargetModule())
263 {
264 _ASSERTE(pPreferredZapModule->IsSystem() || IsNgenPDBCompilationProcess());
265 RETURN (pPreferredZapModule);
266 }
267
268 Module * pTargetModule = pAppDomain->ToCompilationDomain()->GetTargetModule();
269
270 // If it is multi-module assembly and we have not saved PZM yet, do not create
271 // speculative instantiation - just save it in PZM.
272 if (pTargetModule->GetAssembly() == pPreferredZapModule->GetAssembly() &&
273 !pPreferredZapModule->IsModuleSaved())
274 {
275 pZapperLoaderModule = pPreferredZapModule;
276 }
277 else
278 {
279 // Everything else can be saved into the current module.
280 pZapperLoaderModule = pTargetModule;
281 }
282
283 // If generating WinMD resilient code and we so far choose to use the target module,
284 // we need to check if the definition module or any of the instantiation type can
285 // cause version resilient problems.
286 if (g_fNGenWinMDResilient && pZapperLoaderModule == pTargetModule)
287 {
288 if (pDefinitionModule != NULL && !pDefinitionModule->IsInCurrentVersionBubble())
289 {
290 pZapperLoaderModule = pDefinitionModule;
291 goto ModuleAdjustedForVersionResiliency;
292 }
293
294 for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
295 {
296 Module * pModule = classInst[i].GetLoaderModule();
297 if (!pModule->IsInCurrentVersionBubble())
298 {
299 pZapperLoaderModule = pModule;
300 goto ModuleAdjustedForVersionResiliency;
301 }
302 }
303
304 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
305 {
306 Module * pModule = methodInst[i].GetLoaderModule();
307 if (!pModule->IsInCurrentVersionBubble())
308 {
309 pZapperLoaderModule = pModule;
310 goto ModuleAdjustedForVersionResiliency;
311 }
312 }
313ModuleAdjustedForVersionResiliency: ;
314 }
315
316 // Record this choice just in case we're NGEN'ing multiple modules
317 // to make sure we always do the same thing if we're asked to compute
318 // the loader module again.
319
320 // Note this whole code path only happens while NGEN'ing, so this violation
321 // is not so bad. It is needed since we allocate stuff on the heap.
322 CONTRACT_VIOLATION(ThrowsViolation|FaultViolation);
323
324 // Copy the instantiation arrays so they can escape the scope of this method.
325 // Since this is a permanent entry in a table for this compilation process
326 // we do not need to collect these. If we did have to we would do it when we deleteed the
327 // ZapperLoaderModuleTable.
328 NewArrayHolder<TypeHandle> pClassArgs = NULL;
329 if (!classInst.IsEmpty())
330 {
331 pClassArgs = new TypeHandle[classInst.GetNumArgs()];
332 for (unsigned int i = 0; i < classInst.GetNumArgs(); i++)
333 pClassArgs[i] = classInst[i];
334 }
335
336 NewArrayHolder<TypeHandle> pMethodArgs = NULL;
337 if (!methodInst.IsEmpty())
338 {
339 pMethodArgs = new TypeHandle[methodInst.GetNumArgs()];
340 for (unsigned int i = 0; i < methodInst.GetNumArgs(); i++)
341 pMethodArgs[i] = methodInst[i];
342 }
343
344 ZapperLoaderModuleTableKey key2(pDefinitionModule,
345 token,
346 Instantiation(pClassArgs, classInst.GetNumArgs()),
347 Instantiation(pMethodArgs, methodInst.GetNumArgs()));
348 g_pCEECompileInfo->RecordZapperLoaderModule(&key2, pZapperLoaderModule);
349
350 pClassArgs.SuppressRelease();
351 pMethodArgs.SuppressRelease();
352
353 RETURN (pZapperLoaderModule);
354}
355#endif // FEATURE_NATIVE_IMAGE_GENERATION
356#endif // #ifndef DACCESS_COMPILE
357
358/*static*/
359Module * ClassLoader::ComputeLoaderModule(MethodTable * pMT,
360 mdToken token,
361 Instantiation methodInst)
362{
363 CONTRACTL
364 {
365 NOTHROW;
366 GC_NOTRIGGER;
367 MODE_ANY;
368 SUPPORTS_DAC;
369 }
370 CONTRACTL_END;
371
372 return ComputeLoaderModuleWorker(pMT->GetModule(),
373 token,
374 pMT->GetInstantiation(),
375 methodInst);
376}
377/*static*/
378Module *ClassLoader::ComputeLoaderModule(TypeKey *typeKey)
379{
380 CONTRACTL
381 {
382 NOTHROW;
383 GC_NOTRIGGER;
384 MODE_ANY;
385 SUPPORTS_DAC;
386 }
387 CONTRACTL_END;
388
389
390 if (typeKey->GetKind() == ELEMENT_TYPE_CLASS)
391 return ComputeLoaderModuleWorker(typeKey->GetModule(),
392 typeKey->GetTypeToken(),
393 typeKey->GetInstantiation(),
394 Instantiation());
395 else if (typeKey->GetKind() == ELEMENT_TYPE_FNPTR)
396 return ComputeLoaderModuleForFunctionPointer(typeKey->GetRetAndArgTypes(), typeKey->GetNumArgs() + 1);
397 else
398 return ComputeLoaderModuleForParamType(typeKey->GetElementType());
399}
400
401/*static*/
402BOOL ClassLoader::IsTypicalInstantiation(Module *pModule, mdToken token, Instantiation inst)
403{
404 CONTRACTL
405 {
406 NOTHROW;
407 GC_NOTRIGGER;
408 FORBID_FAULT;
409 PRECONDITION(CheckPointer(pModule));
410 PRECONDITION(TypeFromToken(token) == mdtTypeDef || TypeFromToken(token) == mdtMethodDef);
411 SUPPORTS_DAC;
412 }
413 CONTRACTL_END
414
415 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
416 {
417 TypeHandle thArg = inst[i];
418
419 if (thArg.IsGenericVariable())
420 {
421 TypeVarTypeDesc* tyvar = thArg.AsGenericVariable();
422
423 PREFIX_ASSUME(tyvar!=NULL);
424 if ((tyvar->GetTypeOrMethodDef() != token) ||
425 (tyvar->GetModule() != dac_cast<PTR_Module>(pModule)) ||
426 (tyvar->GetIndex() != i))
427 return FALSE;
428 }
429 else
430 {
431 return FALSE;
432 }
433 }
434 return TRUE;
435}
436
437// External class loader entry point: load a type by name
438/*static*/
439TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly,
440 LPCUTF8 nameSpace,
441 LPCUTF8 name,
442 NotFoundAction fNotFound,
443 ClassLoader::LoadTypesFlag fLoadTypes,
444 ClassLoadLevel level)
445{
446 CONTRACT(TypeHandle)
447 {
448 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
449 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
450 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
451 MODE_ANY;
452
453 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
454
455 PRECONDITION(CheckPointer(pAssembly));
456 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
457 POSTCONDITION(CheckPointer(RETVAL,
458 (fNotFound == ThrowIfNotFound && fLoadTypes == LoadTypes )? NULL_NOT_OK : NULL_OK));
459 POSTCONDITION(RETVAL.IsNull() || RETVAL.CheckLoadLevel(level));
460 SUPPORTS_DAC;
461#ifdef DACCESS_COMPILE
462 PRECONDITION((fNotFound == ClassLoader::ReturnNullIfNotFound) && (fLoadTypes == DontLoadTypes));
463#endif
464 }
465 CONTRACT_END
466
467 NameHandle nameHandle(nameSpace, name);
468 if (fLoadTypes == DontLoadTypes)
469 nameHandle.SetTokenNotToLoad(tdAllTypes);
470 if (fNotFound == ThrowIfNotFound)
471 RETURN pAssembly->GetLoader()->LoadTypeHandleThrowIfFailed(&nameHandle, level);
472 else
473 RETURN pAssembly->GetLoader()->LoadTypeHandleThrowing(&nameHandle, level);
474}
475
476#ifndef DACCESS_COMPILE
477
478#define DAC_LOADS_TYPE(level, expression) \
479 if (FORBIDGC_LOADER_USE_ENABLED() || (expression)) \
480 { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
481#else
482
483#define DAC_LOADS_TYPE(level, expression) { LOADS_TYPE(CLASS_LOAD_BEGIN); }
484#endif // #ifndef DACCESS_COMPILE
485
486//
487// Find a class given name, using the classloader's global list of known classes.
488// If the type is found, it will be restored unless pName->GetTokenNotToLoad() prohibits that
489// Returns NULL if class not found AND pName->OKToLoad returns false
490TypeHandle ClassLoader::LoadTypeHandleThrowIfFailed(NameHandle* pName, ClassLoadLevel level,
491 Module* pLookInThisModuleOnly/*=NULL*/)
492{
493 CONTRACT(TypeHandle)
494 {
495 INSTANCE_CHECK;
496 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
497 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
498 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
499 DAC_LOADS_TYPE(level, !pName->OKToLoad());
500 MODE_ANY;
501 PRECONDITION(CheckPointer(pName));
502 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
503 POSTCONDITION(CheckPointer(RETVAL, pName->OKToLoad() ? NULL_NOT_OK : NULL_OK));
504 POSTCONDITION(RETVAL.IsNull() || RETVAL.CheckLoadLevel(level));
505 SUPPORTS_DAC;
506 }
507 CONTRACT_END;
508
509 // Lookup in the classes that this class loader knows about
510 TypeHandle typeHnd = LoadTypeHandleThrowing(pName, level, pLookInThisModuleOnly);
511
512 if(typeHnd.IsNull()) {
513
514 if ( pName->OKToLoad() ) {
515#ifdef _DEBUG_IMPL
516 {
517 LPCUTF8 szName = pName->GetName();
518 if (szName == NULL)
519 szName = "<UNKNOWN>";
520
521 StackSString codeBase;
522 GetAssembly()->GetCodeBase(codeBase);
523
524 LOG((LF_CLASSLOADER, LL_INFO10, "Failed to find class \"%s\" in the manifest for assembly \"%ws\"\n", szName, (LPCWSTR)codeBase));
525 }
526#endif
527
528#ifndef DACCESS_COMPILE
529 COUNTER_ONLY(GetPerfCounters().m_Loading.cLoadFailures++);
530
531 m_pAssembly->ThrowTypeLoadException(pName, IDS_CLASSLOAD_GENERAL);
532#else
533 DacNotImpl();
534#endif
535 }
536 }
537
538 RETURN(typeHnd);
539}
540
541#ifndef DACCESS_COMPILE
542
543//<TODO>@TODO: Need to allow exceptions to be thrown when classloader is cleaned up</TODO>
544EEClassHashEntry_t* ClassLoader::InsertValue(EEClassHashTable *pClassHash, EEClassHashTable *pClassCaseInsHash, LPCUTF8 pszNamespace, LPCUTF8 pszClassName, HashDatum Data, EEClassHashEntry_t *pEncloser, AllocMemTracker *pamTracker)
545{
546 CONTRACTL
547 {
548 INSTANCE_CHECK;
549 THROWS;
550 GC_NOTRIGGER;
551 MODE_ANY;
552 INJECT_FAULT(COMPlusThrowOM(););
553 }
554 CONTRACTL_END
555
556 LPUTF8 pszLowerCaseNS = NULL;
557 LPUTF8 pszLowerCaseName = NULL;
558 EEClassHashEntry_t *pCaseInsEntry = NULL;
559
560 EEClassHashEntry_t *pEntry = pClassHash->AllocNewEntry(pamTracker);
561
562 if (pClassCaseInsHash) {
563 CreateCanonicallyCasedKey(pszNamespace, pszClassName, &pszLowerCaseNS, &pszLowerCaseName);
564 pCaseInsEntry = pClassCaseInsHash->AllocNewEntry(pamTracker);
565 }
566
567
568 {
569 // ! We cannot fail after this point.
570 CANNOTTHROWCOMPLUSEXCEPTION();
571 FAULT_FORBID();
572
573
574 pClassHash->InsertValueUsingPreallocatedEntry(pEntry, pszNamespace, pszClassName, Data, pEncloser);
575
576 //If we're keeping a table for case-insensitive lookup, keep that up to date
577 if (pClassCaseInsHash)
578 pClassCaseInsHash->InsertValueUsingPreallocatedEntry(pCaseInsEntry, pszLowerCaseNS, pszLowerCaseName, pEntry, pEncloser);
579
580 return pEntry;
581 }
582
583}
584
585#endif // #ifndef DACCESS_COMPILE
586
587BOOL ClassLoader::CompareNestedEntryWithExportedType(IMDInternalImport * pImport,
588 mdExportedType mdCurrent,
589 EEClassHashTable * pClassHash,
590 PTR_EEClassHashEntry pEntry)
591{
592 CONTRACTL
593 {
594 INSTANCE_CHECK;
595 NOTHROW;
596 GC_NOTRIGGER;
597 MODE_ANY;
598 FORBID_FAULT;
599 SUPPORTS_DAC;
600 }
601 CONTRACTL_END;
602
603 LPCUTF8 Key[2];
604
605 do
606 {
607 if (FAILED(pImport->GetExportedTypeProps(
608 mdCurrent,
609 &Key[0],
610 &Key[1],
611 &mdCurrent,
612 NULL, //binding (type def)
613 NULL))) //flags
614 {
615 return FALSE;
616 }
617
618 if (pClassHash->CompareKeys(pEntry, Key))
619 {
620 // Reached top level class for mdCurrent - return whether
621 // or not pEntry is a top level class
622 // (pEntry is a top level class if its pEncloser is NULL)
623 if ((TypeFromToken(mdCurrent) != mdtExportedType) ||
624 (mdCurrent == mdExportedTypeNil))
625 {
626 return pEntry->GetEncloser() == NULL;
627 }
628 }
629 else // Keys don't match - wrong entry
630 {
631 return FALSE;
632 }
633 }
634 while ((pEntry = pEntry->GetEncloser()) != NULL);
635
636 // Reached the top level class for pEntry, but mdCurrent is nested
637 return FALSE;
638}
639
640
641BOOL ClassLoader::CompareNestedEntryWithTypeDef(IMDInternalImport * pImport,
642 mdTypeDef mdCurrent,
643 EEClassHashTable * pClassHash,
644 PTR_EEClassHashEntry pEntry)
645{
646 CONTRACTL
647 {
648 INSTANCE_CHECK;
649 NOTHROW;
650 GC_NOTRIGGER;
651 MODE_ANY;
652 FORBID_FAULT;
653 SUPPORTS_DAC;
654 }
655 CONTRACTL_END;
656
657 LPCUTF8 Key[2];
658
659 do {
660 if (FAILED(pImport->GetNameOfTypeDef(mdCurrent, &Key[1], &Key[0])))
661 {
662 return FALSE;
663 }
664
665 if (pClassHash->CompareKeys(pEntry, Key)) {
666 // Reached top level class for mdCurrent - return whether
667 // or not pEntry is a top level class
668 // (pEntry is a top level class if its pEncloser is NULL)
669 if (FAILED(pImport->GetNestedClassProps(mdCurrent, &mdCurrent)))
670 return pEntry->GetEncloser() == NULL;
671 }
672 else // Keys don't match - wrong entry
673 return FALSE;
674 }
675 while ((pEntry = pEntry->GetEncloser()) != NULL);
676
677 // Reached the top level class for pEntry, but mdCurrent is nested
678 return FALSE;
679}
680
681
682BOOL ClassLoader::CompareNestedEntryWithTypeRef(IMDInternalImport * pImport,
683 mdTypeRef mdCurrent,
684 EEClassHashTable * pClassHash,
685 PTR_EEClassHashEntry pEntry)
686{
687 CONTRACTL
688 {
689 INSTANCE_CHECK;
690 NOTHROW;
691 GC_NOTRIGGER;
692 MODE_ANY;
693 FORBID_FAULT;
694 SUPPORTS_DAC;
695 }
696 CONTRACTL_END;
697
698 LPCUTF8 Key[2];
699
700 do {
701 if (FAILED(pImport->GetNameOfTypeRef(mdCurrent, &Key[0], &Key[1])))
702 {
703 return FALSE;
704 }
705
706 if (pClassHash->CompareKeys(pEntry, Key))
707 {
708 if (FAILED(pImport->GetResolutionScopeOfTypeRef(mdCurrent, &mdCurrent)))
709 {
710 return FALSE;
711 }
712 // Reached top level class for mdCurrent - return whether
713 // or not pEntry is a top level class
714 // (pEntry is a top level class if its pEncloser is NULL)
715 if ((TypeFromToken(mdCurrent) != mdtTypeRef) ||
716 (mdCurrent == mdTypeRefNil))
717 return pEntry->GetEncloser() == NULL;
718 }
719 else // Keys don't match - wrong entry
720 return FALSE;
721 }
722 while ((pEntry = pEntry->GetEncloser())!=NULL);
723
724 // Reached the top level class for pEntry, but mdCurrent is nested
725 return FALSE;
726}
727
728
729/*static*/
730BOOL ClassLoader::IsNested(Module *pModule, mdToken token, mdToken *mdEncloser)
731{
732 CONTRACTL
733 {
734 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
735 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
736 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
737 MODE_ANY;
738 SUPPORTS_DAC;
739 }
740 CONTRACTL_END;
741
742 switch(TypeFromToken(token)) {
743 case mdtTypeDef:
744 return (SUCCEEDED(pModule->GetMDImport()->GetNestedClassProps(token, mdEncloser)));
745
746 case mdtTypeRef:
747 IfFailThrow(pModule->GetMDImport()->GetResolutionScopeOfTypeRef(token, mdEncloser));
748 return ((TypeFromToken(*mdEncloser) == mdtTypeRef) &&
749 (*mdEncloser != mdTypeRefNil));
750
751 case mdtExportedType:
752 IfFailThrow(pModule->GetAssembly()->GetManifestImport()->GetExportedTypeProps(
753 token,
754 NULL, // namespace
755 NULL, // name
756 mdEncloser,
757 NULL, //binding (type def)
758 NULL)); //flags
759 return ((TypeFromToken(*mdEncloser) == mdtExportedType) &&
760 (*mdEncloser != mdExportedTypeNil));
761
762 default:
763 ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
764 }
765}
766
767BOOL ClassLoader::IsNested(NameHandle* pName, mdToken *mdEncloser)
768{
769 CONTRACTL
770 {
771 INSTANCE_CHECK;
772 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
773 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
774 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
775 MODE_ANY;
776 SUPPORTS_DAC;
777 }
778 CONTRACTL_END;
779
780 if (pName->GetTypeModule()) {
781 if (TypeFromToken(pName->GetTypeToken()) == mdtBaseType)
782 {
783 if (!pName->GetBucket().IsNull())
784 return TRUE;
785 return FALSE;
786 }
787 else
788 return IsNested(pName->GetTypeModule(), pName->GetTypeToken(), mdEncloser);
789 }
790 else
791 return FALSE;
792}
793
794void ClassLoader::GetClassValue(NameHandleTable nhTable,
795 NameHandle *pName,
796 HashDatum *pData,
797 EEClassHashTable **ppTable,
798 Module* pLookInThisModuleOnly,
799 HashedTypeEntry* pFoundEntry,
800 Loader::LoadFlag loadFlag,
801 BOOL& needsToBuildHashtable)
802{
803 CONTRACTL
804 {
805 INSTANCE_CHECK;
806 MODE_ANY;
807 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
808 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
809 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
810 PRECONDITION(CheckPointer(pName));
811 SUPPORTS_DAC;
812 }
813 CONTRACTL_END
814
815
816 mdToken mdEncloser;
817 EEClassHashEntry_t *pBucket = NULL;
818
819 needsToBuildHashtable = FALSE;
820
821#if _DEBUG
822 if (pName->GetName()) {
823 if (pName->GetNameSpace() == NULL)
824 LOG((LF_CLASSLOADER, LL_INFO1000, "Looking up %s by name.\n",
825 pName->GetName()));
826 else
827 LOG((LF_CLASSLOADER, LL_INFO1000, "Looking up %s.%s by name.\n",
828 pName->GetNameSpace(), pName->GetName()));
829 }
830#endif
831
832 BOOL isNested = IsNested(pName, &mdEncloser);
833
834 PTR_Assembly assembly = GetAssembly();
835 PREFIX_ASSUME(assembly != NULL);
836 ModuleIterator i = assembly->IterateModules();
837
838 while (i.Next())
839 {
840 Module * pCurrentClsModule = i.GetModule();
841 PREFIX_ASSUME(pCurrentClsModule != NULL);
842
843 if (pCurrentClsModule->IsResource())
844 continue;
845 if (pLookInThisModuleOnly && (pCurrentClsModule != pLookInThisModuleOnly))
846 continue;
847
848#ifdef FEATURE_READYTORUN
849 if (nhTable == nhCaseSensitive && pCurrentClsModule->IsReadyToRun() && pCurrentClsModule->GetReadyToRunInfo()->HasHashtableOfTypes())
850 {
851 // For R2R modules, we only search the hashtable of token types stored in the module's image, and don't fallback
852 // to searching m_pAvailableClasses or m_pAvailableClassesCaseIns (in fact, we don't even allocate them for R2R modules).
853 // Also note that type lookups in R2R modules only support case sensitive lookups.
854
855 mdToken mdFoundTypeToken;
856 if (pCurrentClsModule->GetReadyToRunInfo()->TryLookupTypeTokenFromName(pName, &mdFoundTypeToken))
857 {
858 if (TypeFromToken(mdFoundTypeToken) == mdtExportedType)
859 {
860 mdToken mdUnused;
861 Module * pTargetModule = GetAssembly()->FindModuleByExportedType(mdFoundTypeToken, loadFlag, mdTypeDefNil, &mdUnused);
862
863 pFoundEntry->SetTokenBasedEntryValue(mdFoundTypeToken, pTargetModule);
864 }
865 else
866 {
867 pFoundEntry->SetTokenBasedEntryValue(mdFoundTypeToken, pCurrentClsModule);
868 }
869
870 return; // Return on the first success
871 }
872 }
873 else
874#endif
875 {
876 EEClassHashTable* pTable = NULL;
877 if (nhTable == nhCaseSensitive)
878 {
879 *ppTable = pTable = pCurrentClsModule->GetAvailableClassHash();
880
881#ifdef FEATURE_READYTORUN
882 if (pTable == NULL && pCurrentClsModule->IsReadyToRun() && !pCurrentClsModule->GetReadyToRunInfo()->HasHashtableOfTypes())
883 {
884 // Old R2R image generated without the hashtable of types.
885 // We fallback to the slow path of creating the hashtable dynamically
886 // at execution time in that scenario. The caller will handle
887 pFoundEntry->SetClassHashBasedEntryValue(NULL);
888 needsToBuildHashtable = TRUE;
889 return;
890 }
891#endif
892 }
893 else
894 {
895 // currently we expect only these two kinds--for DAC builds, nhTable will be nhCaseSensitive
896 _ASSERTE(nhTable == nhCaseInsensitive);
897 *ppTable = pTable = pCurrentClsModule->GetAvailableClassCaseInsHash();
898
899 if (pTable == NULL)
900 {
901 // We have not built the table yet - the caller will handle
902 pFoundEntry->SetClassHashBasedEntryValue(NULL);
903 needsToBuildHashtable = TRUE;
904 return;
905 }
906 }
907 _ASSERTE(pTable);
908
909 if (isNested)
910 {
911 Module *pNameModule = pName->GetTypeModule();
912 PREFIX_ASSUME(pNameModule != NULL);
913
914 EEClassHashTable::LookupContext sContext;
915 if ((pBucket = pTable->GetValue(pName, pData, TRUE, &sContext)) != NULL)
916 {
917 switch (TypeFromToken(pName->GetTypeToken()))
918 {
919 case mdtTypeDef:
920 while ((!CompareNestedEntryWithTypeDef(pNameModule->GetMDImport(),
921 mdEncloser,
922 pCurrentClsModule->GetAvailableClassHash(),
923 pBucket->GetEncloser())) &&
924 (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
925 break;
926 case mdtTypeRef:
927 while ((!CompareNestedEntryWithTypeRef(pNameModule->GetMDImport(),
928 mdEncloser,
929 pCurrentClsModule->GetAvailableClassHash(),
930 pBucket->GetEncloser())) &&
931 (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
932 break;
933 case mdtExportedType:
934 while ((!CompareNestedEntryWithExportedType(pNameModule->GetAssembly()->GetManifestImport(),
935 mdEncloser,
936 pCurrentClsModule->GetAvailableClassHash(),
937 pBucket->GetEncloser())) &&
938 (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
939 break;
940 default:
941 while ((pBucket->GetEncloser() != pName->GetBucket().GetClassHashBasedEntryValue()) &&
942 (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL);
943 }
944 }
945 }
946 else
947 {
948 pBucket = pTable->GetValue(pName, pData, FALSE, NULL);
949 }
950
951 if (pBucket) // Return on the first success
952 {
953 pFoundEntry->SetClassHashBasedEntryValue(pBucket);
954 return;
955 }
956 }
957 }
958
959 // No results found: default to a NULL EEClassHashEntry_t result
960 pFoundEntry->SetClassHashBasedEntryValue(NULL);
961}
962
963#ifndef DACCESS_COMPILE
964
965VOID ClassLoader::PopulateAvailableClassHashTable(Module* pModule,
966 AllocMemTracker *pamTracker)
967{
968 CONTRACTL
969 {
970 INSTANCE_CHECK;
971 THROWS;
972 GC_TRIGGERS;
973 MODE_ANY;
974 INJECT_FAULT(COMPlusThrowOM(););
975 }
976 CONTRACTL_END;
977
978 mdTypeDef td;
979 HENUMInternal hTypeDefEnum;
980 IMDInternalImport * pImport = pModule->GetMDImport();
981
982 LPCSTR szWinRtNamespacePrefix = NULL;
983 DWORD cchWinRtNamespacePrefix = 0;
984
985#ifdef FEATURE_COMINTEROP
986 SString ssFileName;
987 StackScratchBuffer ssFileNameBuffer;
988
989 if (pModule->GetAssembly()->IsWinMD())
990 { // WinMD file in execution context (not ReflectionOnly context) - use its file name as WinRT namespace prefix
991 // (Windows requirement)
992 // Note: Reflection can work on 'unfinished' WinMD files where the types are in 'wrong' WinMD file (i.e.
993 // type namespace does not start with the file name)
994
995 _ASSERTE(pModule->GetFile()->IsAssembly()); // No multi-module WinMD file support
996 _ASSERTE(!pModule->GetFile()->GetPath().IsEmpty());
997
998 SplitPath(
999 pModule->GetFile()->GetPath(),
1000 NULL, // Drive
1001 NULL, // Directory
1002 &ssFileName,
1003 NULL); // Extension
1004
1005 szWinRtNamespacePrefix = ssFileName.GetUTF8(ssFileNameBuffer);
1006 cchWinRtNamespacePrefix = (DWORD)strlen(szWinRtNamespacePrefix);
1007 }
1008#endif //FEATURE_COMINTEROP
1009
1010 IfFailThrow(pImport->EnumTypeDefInit(&hTypeDefEnum));
1011
1012 // Now loop through all the classdefs adding the CVID and scope to the hash
1013 while(pImport->EnumTypeDefNext(&hTypeDefEnum, &td)) {
1014
1015 AddAvailableClassHaveLock(pModule,
1016 td,
1017 pamTracker,
1018 szWinRtNamespacePrefix,
1019 cchWinRtNamespacePrefix);
1020 }
1021 pImport->EnumTypeDefClose(&hTypeDefEnum);
1022}
1023
1024
1025void ClassLoader::LazyPopulateCaseSensitiveHashTables()
1026{
1027 CONTRACTL
1028 {
1029 INSTANCE_CHECK;
1030 THROWS;
1031 GC_TRIGGERS;
1032 MODE_ANY;
1033 INJECT_FAULT(COMPlusThrowOM());
1034 }
1035 CONTRACTL_END;
1036
1037 AllocMemTracker amTracker;
1038 ModuleIterator i = GetAssembly()->IterateModules();
1039
1040 // Create a case-sensitive hashtable for each module, and fill it with the module's typedef entries
1041 while (i.Next())
1042 {
1043 Module *pModule = i.GetModule();
1044 PREFIX_ASSUME(pModule != NULL);
1045 if (pModule->IsResource())
1046 continue;
1047
1048 // Lazy construction of the case-sensitive hashtable of types is *only* a scenario for ReadyToRun images
1049 // (either images compiled with an old version of crossgen, or for case-insensitive type lookups in R2R modules)
1050 _ASSERT(pModule->IsReadyToRun());
1051
1052 EEClassHashTable * pNewClassHash = EEClassHashTable::Create(pModule, AVAILABLE_CLASSES_HASH_BUCKETS, FALSE /* bCaseInsensitive */, &amTracker);
1053 pModule->SetAvailableClassHash(pNewClassHash);
1054
1055 PopulateAvailableClassHashTable(pModule, &amTracker);
1056 }
1057
1058 // Add exported types of the manifest module to the hashtable
1059 if (!GetAssembly()->GetManifestModule()->IsResource())
1060 {
1061 IMDInternalImport * pManifestImport = GetAssembly()->GetManifestImport();
1062 HENUMInternalHolder phEnum(pManifestImport);
1063 phEnum.EnumInit(mdtExportedType, mdTokenNil);
1064
1065 mdToken mdExportedType;
1066 while (pManifestImport->EnumNext(&phEnum, &mdExportedType))
1067 AddExportedTypeHaveLock(GetAssembly()->GetManifestModule(), mdExportedType, &amTracker);
1068 }
1069
1070 amTracker.SuppressRelease();
1071}
1072
1073void ClassLoader::LazyPopulateCaseInsensitiveHashTables()
1074{
1075 CONTRACTL
1076 {
1077 INSTANCE_CHECK;
1078 THROWS;
1079 GC_TRIGGERS;
1080 MODE_ANY;
1081 INJECT_FAULT(COMPlusThrowOM());
1082 }
1083 CONTRACTL_END;
1084
1085 if (!GetAssembly()->GetManifestModule()->IsResource() && GetAssembly()->GetManifestModule()->GetAvailableClassHash() == NULL)
1086 {
1087 // This is a R2R assembly, and a case insensitive type lookup was triggered.
1088 // Construct the case-sensitive table first, since the case-insensitive table
1089 // create piggy-backs on the first.
1090 LazyPopulateCaseSensitiveHashTables();
1091 }
1092
1093 // Add any unhashed modules into our hash tables, and try again.
1094
1095 AllocMemTracker amTracker;
1096 ModuleIterator i = GetAssembly()->IterateModules();
1097
1098 while (i.Next())
1099 {
1100 Module *pModule = i.GetModule();
1101 if (pModule->IsResource())
1102 continue;
1103
1104 if (pModule->GetAvailableClassCaseInsHash() == NULL)
1105 {
1106 EEClassHashTable *pNewClassCaseInsHash = pModule->GetAvailableClassHash()->MakeCaseInsensitiveTable(pModule, &amTracker);
1107
1108 LOG((LF_CLASSLOADER, LL_INFO10, "%s's classes being added to case insensitive hash table\n",
1109 pModule->GetSimpleName()));
1110
1111 {
1112 CANNOTTHROWCOMPLUSEXCEPTION();
1113 FAULT_FORBID();
1114
1115 amTracker.SuppressRelease();
1116 pModule->SetAvailableClassCaseInsHash(pNewClassCaseInsHash);
1117 FastInterlockDecrement((LONG*)&m_cUnhashedModules);
1118 }
1119 }
1120 }
1121}
1122
1123/*static*/
1124void DECLSPEC_NORETURN ClassLoader::ThrowTypeLoadException(TypeKey *pKey,
1125 UINT resIDWhy)
1126{
1127 STATIC_CONTRACT_THROWS;
1128
1129 StackSString fullName;
1130 StackSString assemblyName;
1131 TypeString::AppendTypeKey(fullName, pKey);
1132 pKey->GetModule()->GetAssembly()->GetDisplayName(assemblyName);
1133 ::ThrowTypeLoadException(fullName, assemblyName, NULL, resIDWhy);
1134}
1135
1136#endif
1137
1138
1139TypeHandle ClassLoader::LoadConstructedTypeThrowing(TypeKey *pKey,
1140 LoadTypesFlag fLoadTypes /*= LoadTypes*/,
1141 ClassLoadLevel level /*=CLASS_LOADED*/,
1142 const InstantiationContext *pInstContext /*=NULL*/)
1143{
1144 CONTRACT(TypeHandle)
1145 {
1146 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1147 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1148 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1149 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
1150 if (fLoadTypes == DontLoadTypes) SO_TOLERANT; else SO_INTOLERANT;
1151 PRECONDITION(CheckPointer(pKey));
1152 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
1153 PRECONDITION(CheckPointer(pInstContext, NULL_OK));
1154 POSTCONDITION(CheckPointer(RETVAL, fLoadTypes==DontLoadTypes ? NULL_OK : NULL_NOT_OK));
1155 POSTCONDITION(RETVAL.IsNull() || RETVAL.GetLoadLevel() >= level);
1156 MODE_ANY;
1157 SUPPORTS_DAC;
1158 }
1159 CONTRACT_END
1160
1161 TypeHandle typeHnd;
1162 ClassLoadLevel existingLoadLevel = CLASS_LOAD_BEGIN;
1163
1164 // Lookup in the classes that this class loader knows about
1165
1166 if (pKey->HasInstantiation() && ClassLoader::IsTypicalSharedInstantiation(pKey->GetInstantiation()))
1167 {
1168 _ASSERTE(pKey->GetModule() == ComputeLoaderModule(pKey));
1169 typeHnd = pKey->GetModule()->LookupFullyCanonicalInstantiation(pKey->GetTypeToken(), &existingLoadLevel);
1170 }
1171
1172 if (typeHnd.IsNull())
1173 {
1174 typeHnd = LookupTypeHandleForTypeKey(pKey);
1175 if (!typeHnd.IsNull())
1176 {
1177 existingLoadLevel = typeHnd.GetLoadLevel();
1178 if (existingLoadLevel >= level)
1179 g_IBCLogger.LogTypeHashTableAccess(&typeHnd);
1180 }
1181 }
1182
1183 // If something has been published in the tables, and it's at the right level, just return it
1184 if (!typeHnd.IsNull() && existingLoadLevel >= level)
1185 {
1186 RETURN typeHnd;
1187 }
1188
1189#ifndef DACCESS_COMPILE
1190 if (typeHnd.IsNull() && pKey->HasInstantiation())
1191 {
1192 if (!Generics::CheckInstantiation(pKey->GetInstantiation()))
1193 pKey->GetModule()->GetAssembly()->ThrowTypeLoadException(pKey->GetModule()->GetMDImport(), pKey->GetTypeToken(), IDS_CLASSLOAD_INVALIDINSTANTIATION);
1194 }
1195#endif
1196
1197 // If we're not loading any types at all, then we're not creating
1198 // instantiations either because we're in FORBIDGC_LOADER_USE mode, so
1199 // we should bail out here.
1200 if (fLoadTypes == DontLoadTypes)
1201 RETURN TypeHandle();
1202
1203#ifndef DACCESS_COMPILE
1204 // If we got here, we now have to allocate a new parameterized type.
1205 // By definition, forbidgc-users aren't allowed to reach this point.
1206 CONSISTENCY_CHECK(!FORBIDGC_LOADER_USE_ENABLED());
1207
1208 Module *pLoaderModule = ComputeLoaderModule(pKey);
1209 RETURN(pLoaderModule->GetClassLoader()->LoadTypeHandleForTypeKey(pKey, typeHnd, level, pInstContext));
1210#else
1211 DacNotImpl();
1212 RETURN(typeHnd);
1213#endif
1214}
1215
1216
1217/*static*/
1218void ClassLoader::EnsureLoaded(TypeHandle typeHnd, ClassLoadLevel level)
1219{
1220 CONTRACTL
1221 {
1222 PRECONDITION(CheckPointer(typeHnd));
1223 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
1224 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1225 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1226 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1227 if (FORBIDGC_LOADER_USE_ENABLED()) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
1228 SUPPORTS_DAC;
1229
1230 MODE_ANY;
1231 }
1232 CONTRACTL_END
1233
1234#ifndef DACCESS_COMPILE // Nothing to do for the DAC case
1235
1236 if (typeHnd.GetLoadLevel() < level)
1237 {
1238 INTERIOR_STACK_PROBE_CHECK_THREAD;
1239
1240#ifdef FEATURE_PREJIT
1241 if (typeHnd.GetLoadLevel() == CLASS_LOAD_UNRESTOREDTYPEKEY)
1242 {
1243 typeHnd.DoRestoreTypeKey();
1244 }
1245#endif
1246 if (level > CLASS_LOAD_UNRESTORED)
1247 {
1248 TypeKey typeKey = typeHnd.GetTypeKey();
1249
1250 Module *pLoaderModule = ComputeLoaderModule(&typeKey);
1251 pLoaderModule->GetClassLoader()->LoadTypeHandleForTypeKey(&typeKey, typeHnd, level);
1252 }
1253
1254 END_INTERIOR_STACK_PROBE;
1255 }
1256
1257#endif // DACCESS_COMPILE
1258}
1259
1260/*static*/
1261void ClassLoader::TryEnsureLoaded(TypeHandle typeHnd, ClassLoadLevel level)
1262{
1263 WRAPPER_NO_CONTRACT;
1264
1265#ifndef DACCESS_COMPILE // Nothing to do for the DAC case
1266
1267 EX_TRY
1268 {
1269 ClassLoader::EnsureLoaded(typeHnd, level);
1270 }
1271 EX_CATCH
1272 {
1273 // Some type may not load successfully. For eg. generic instantiations
1274 // that do not satisfy the constraints of the type arguments.
1275 }
1276 EX_END_CATCH(RethrowTerminalExceptions);
1277
1278#endif // DACCESS_COMPILE
1279}
1280
1281// This is separated out to avoid the overhead of C++ exception handling in the non-locking case.
1282/* static */
1283TypeHandle ClassLoader::LookupTypeKeyUnderLock(TypeKey *pKey,
1284 EETypeHashTable *pTable,
1285 CrstBase *pLock)
1286{
1287 WRAPPER_NO_CONTRACT;
1288 SUPPORTS_DAC;
1289
1290 // m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC
1291 GCX_MAYBE_COOP_NO_THREAD_BROKEN(!IsGCThread());
1292
1293 CrstHolder ch(pLock);
1294 return pTable->GetValue(pKey);
1295}
1296
1297/* static */
1298TypeHandle ClassLoader::LookupTypeKey(TypeKey *pKey,
1299 EETypeHashTable *pTable,
1300 CrstBase *pLock,
1301 BOOL fCheckUnderLock)
1302{
1303 CONTRACTL {
1304 NOTHROW;
1305 GC_NOTRIGGER;
1306 FORBID_FAULT;
1307 PRECONDITION(CheckPointer(pKey));
1308 PRECONDITION(pKey->IsConstructed());
1309 PRECONDITION(CheckPointer(pTable));
1310 PRECONDITION(!fCheckUnderLock || CheckPointer(pLock));
1311 MODE_ANY;
1312 SUPPORTS_DAC;
1313 } CONTRACTL_END;
1314
1315 TypeHandle th;
1316
1317 if (fCheckUnderLock)
1318 {
1319 th = LookupTypeKeyUnderLock(pKey, pTable, pLock);
1320 }
1321 else
1322 {
1323 th = pTable->GetValue(pKey);
1324 }
1325 return th;
1326}
1327
1328
1329#ifdef FEATURE_PREJIT
1330/* static */
1331TypeHandle ClassLoader::LookupInPreferredZapModule(TypeKey *pKey, BOOL fCheckUnderLock)
1332{
1333 CONTRACTL {
1334 NOTHROW;
1335 GC_NOTRIGGER;
1336 FORBID_FAULT;
1337 PRECONDITION(CheckPointer(pKey));
1338 PRECONDITION(pKey->IsConstructed());
1339 MODE_ANY;
1340 SUPPORTS_DAC;
1341 } CONTRACTL_END;
1342
1343 // First look for an NGEN'd type in the preferred ngen module
1344 TypeHandle th;
1345 PTR_Module pPreferredZapModule = Module::ComputePreferredZapModule(pKey);
1346
1347 if (pPreferredZapModule != NULL && pPreferredZapModule->HasNativeImage())
1348 {
1349 th = LookupTypeKey(pKey,
1350 pPreferredZapModule->GetAvailableParamTypes(),
1351 &pPreferredZapModule->GetClassLoader()->m_AvailableTypesLock,
1352 fCheckUnderLock);
1353 }
1354
1355 return th;
1356}
1357#endif // FEATURE_PREJIT
1358
1359
1360/* static */
1361TypeHandle ClassLoader::LookupInLoaderModule(TypeKey *pKey, BOOL fCheckUnderLock)
1362{
1363 CONTRACTL {
1364 NOTHROW;
1365 GC_NOTRIGGER;
1366 FORBID_FAULT;
1367 PRECONDITION(CheckPointer(pKey));
1368 PRECONDITION(pKey->IsConstructed());
1369 MODE_ANY;
1370 SUPPORTS_DAC;
1371 } CONTRACTL_END;
1372
1373 Module *pLoaderModule = ComputeLoaderModule(pKey);
1374 PREFIX_ASSUME(pLoaderModule!=NULL);
1375
1376 return LookupTypeKey(pKey,
1377 pLoaderModule->GetAvailableParamTypes(),
1378 &pLoaderModule->GetClassLoader()->m_AvailableTypesLock,
1379 fCheckUnderLock);
1380}
1381
1382
1383/* static */
1384TypeHandle ClassLoader::LookupTypeHandleForTypeKey(TypeKey *pKey)
1385{
1386 WRAPPER_NO_CONTRACT;
1387 SUPPORTS_DAC;
1388
1389 // Make an initial lookup without taking any locks.
1390 TypeHandle th = LookupTypeHandleForTypeKeyInner(pKey, FALSE);
1391
1392 // A non-null TypeHandle for the above lookup indicates success
1393 // A null TypeHandle only indicates "well, it might have been there,
1394 // try again with a lock". This kind of negative result will
1395 // only happen while accessing the underlying EETypeHashTable
1396 // during a resize, i.e. very rarely. In such a case, we just
1397 // perform the lookup again, but indicate that appropriate locks
1398 // should be taken.
1399
1400 if (th.IsNull())
1401 {
1402 th = LookupTypeHandleForTypeKeyInner(pKey, TRUE);
1403 }
1404
1405 return th;
1406}
1407/* static */
1408TypeHandle ClassLoader::LookupTypeHandleForTypeKeyInner(TypeKey *pKey, BOOL fCheckUnderLock)
1409{
1410 CONTRACTL
1411 {
1412 NOTHROW;
1413 GC_NOTRIGGER;
1414 FORBID_FAULT;
1415 PRECONDITION(CheckPointer(pKey));
1416 MODE_ANY;
1417 SUPPORTS_DAC;
1418 }
1419 CONTRACTL_END
1420
1421 // Check if it's the typical instantiation. In this case it's not stored in the same
1422 // way as other constructed types.
1423 if (!pKey->IsConstructed() ||
1424 (pKey->GetKind() == ELEMENT_TYPE_CLASS && ClassLoader::IsTypicalInstantiation(pKey->GetModule(),
1425 pKey->GetTypeToken(),
1426 pKey->GetInstantiation())))
1427 {
1428 return TypeHandle(pKey->GetModule()->LookupTypeDef(pKey->GetTypeToken()));
1429 }
1430
1431#ifdef FEATURE_PREJIT
1432 // The following ways of finding a constructed type should be mutually exclusive!
1433 // 1. Look for a zapped item in the PreferredZapModule
1434 // 2. Look for a unzapped (JIT-loaded) item in the LoaderModule
1435
1436 TypeHandle thPZM = LookupInPreferredZapModule(pKey, fCheckUnderLock);
1437 if (!thPZM.IsNull())
1438 {
1439 return thPZM;
1440 }
1441#endif // FEATURE_PREJIT
1442
1443 // Next look in the loader module. This is where the item is guaranteed to live if
1444 // it is not latched from an NGEN image, i.e. if it is JIT loaded.
1445 // If the thing is not NGEN'd then this may
1446 // be different to pPreferredZapModule. If they are the same then
1447 // we can reuse the results of the lookup above.
1448 TypeHandle thLM = LookupInLoaderModule(pKey, fCheckUnderLock);
1449 if (!thLM.IsNull())
1450 {
1451 return thLM;
1452 }
1453
1454 return TypeHandle();
1455}
1456
1457// FindClassModuleThrowing discovers which module the type you're looking for is in and loads the Module if necessary.
1458// Basically, it iterates through all of the assembly's modules until a name match is found in a module's
1459// AvailableClassHashTable.
1460//
1461// The possible outcomes are:
1462//
1463// - Function returns TRUE - class exists and we successfully found/created the containing Module. See below
1464// for how to deconstruct the results.
1465// - Function returns FALSE - class affirmatively NOT found (that means it doesn't exist as a regular type although
1466// it could also be a parameterized type)
1467// - Function throws - OOM or some other reason we couldn't do the job (if it's a case-sensitive search
1468// and you're looking for already loaded type or you've set the TokenNotToLoad.
1469// we are guaranteed not to find a reason to throw.)
1470//
1471//
1472// If it succeeds (returns TRUE), one of the following will occur. Check (*pType)->IsNull() to discriminate.
1473//
1474// 1. *pType: set to the null TypeHandle()
1475// *ppModule: set to the owning Module
1476// *pmdClassToken: set to the typedef
1477// *pmdFoundExportedType: if this name bound to an ExportedType, this contains the mdtExportedType token (otherwise,
1478// it's set to mdTokenNil.) You need this because in this case, *pmdClassToken is just
1479// a best guess and you need to verify it. (The division of labor between this
1480// and LoadTypeHandle could definitely be better!)
1481//
1482// 2. *pType: set to non-null TypeHandle()
1483// This means someone else had already done this same lookup before you and caused the actual
1484// TypeHandle to be cached. Since we know that's what you *really* wanted, we'll just forget the
1485// Module/typedef stuff and give you the actual TypeHandle.
1486//
1487//
1488BOOL ClassLoader::FindClassModuleThrowing(
1489 const NameHandle * pOriginalName,
1490 TypeHandle * pType,
1491 mdToken * pmdClassToken,
1492 Module ** ppModule,
1493 mdToken * pmdFoundExportedType,
1494 HashedTypeEntry * pFoundEntry,
1495 Module * pLookInThisModuleOnly,
1496 Loader::LoadFlag loadFlag)
1497{
1498 CONTRACTL
1499 {
1500 INSTANCE_CHECK;
1501 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1502 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1503 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1504 PRECONDITION(CheckPointer(pOriginalName));
1505 PRECONDITION(CheckPointer(ppModule));
1506 MODE_ANY;
1507 SUPPORTS_DAC;
1508 }
1509 CONTRACTL_END
1510
1511 NameHandleTable nhTable = nhCaseSensitive; // just to initialize this ...
1512
1513 // Make a copy of the original name which we can modify (to lowercase)
1514 NameHandle localName = *pOriginalName;
1515 NameHandle * pName = &localName;
1516
1517 switch (pName->GetTable())
1518 {
1519 case nhCaseInsensitive:
1520 {
1521#ifndef DACCESS_COMPILE
1522 // GC-type users should only be loading types through tokens.
1523#ifdef _DEBUG_IMPL
1524 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
1525#endif
1526
1527 // Use the case insensitive table
1528 nhTable = nhCaseInsensitive;
1529
1530 // Create a low case version of the namespace and name
1531 LPUTF8 pszLowerNameSpace = NULL;
1532 LPUTF8 pszLowerClassName = NULL;
1533 int allocLen;
1534
1535 if (pName->GetNameSpace())
1536 {
1537 allocLen = InternalCasingHelper::InvariantToLower(
1538 NULL,
1539 0,
1540 pName->GetNameSpace());
1541 if (allocLen == 0)
1542 {
1543 return FALSE;
1544 }
1545
1546 pszLowerNameSpace = (LPUTF8)_alloca(allocLen);
1547 if (allocLen == 1)
1548 {
1549 *pszLowerNameSpace = '\0';
1550 }
1551 else if (!InternalCasingHelper::InvariantToLower(
1552 pszLowerNameSpace,
1553 allocLen,
1554 pName->GetNameSpace()))
1555 {
1556 return FALSE;
1557 }
1558 }
1559
1560 _ASSERTE(pName->GetName() != NULL);
1561 allocLen = InternalCasingHelper::InvariantToLower(NULL, 0, pName->GetName());
1562 if (allocLen == 0)
1563 {
1564 return FALSE;
1565 }
1566
1567 pszLowerClassName = (LPUTF8)_alloca(allocLen);
1568 if (!InternalCasingHelper::InvariantToLower(
1569 pszLowerClassName,
1570 allocLen,
1571 pName->GetName()))
1572 {
1573 return FALSE;
1574 }
1575
1576 // Substitute the lower case version of the name.
1577 // The field are will be released when we leave this scope
1578 pName->SetName(pszLowerNameSpace, pszLowerClassName);
1579 break;
1580#else
1581 DacNotImpl();
1582 break;
1583#endif // #ifndef DACCESS_COMPILE
1584 }
1585 case nhCaseSensitive:
1586 nhTable = nhCaseSensitive;
1587 break;
1588 }
1589
1590 // Remember if there are any unhashed modules. We must do this before
1591 // the actual look to avoid a race condition with other threads doing lookups.
1592#ifdef LOGGING
1593 BOOL incomplete = (m_cUnhashedModules > 0);
1594#endif
1595
1596 HashDatum Data;
1597 EEClassHashTable * pTable = NULL;
1598 HashedTypeEntry foundEntry;
1599 BOOL needsToBuildHashtable;
1600 GetClassValue(nhTable, pName, &Data, &pTable, pLookInThisModuleOnly, &foundEntry, loadFlag, needsToBuildHashtable);
1601
1602 // In the case of R2R modules, the search is only performed in the hashtable saved in the
1603 // R2R image, and this is why we return (whether we found a valid typedef token or not).
1604 // Note: case insensitive searches are not used/supported in R2R images.
1605 if (foundEntry.GetEntryType() == HashedTypeEntry::EntryType::IsHashedTokenEntry)
1606 {
1607 *pType = TypeHandle();
1608 HashedTypeEntry::TokenTypeEntry tokenAndModulePair = foundEntry.GetTokenBasedEntryValue();
1609 switch (TypeFromToken(tokenAndModulePair.m_TypeToken))
1610 {
1611 case mdtTypeDef:
1612 *pmdClassToken = tokenAndModulePair.m_TypeToken;
1613 *pmdFoundExportedType = mdTokenNil;
1614 break;
1615 case mdtExportedType:
1616 *pmdClassToken = mdTokenNil;
1617 *pmdFoundExportedType = tokenAndModulePair.m_TypeToken;
1618 break;
1619 default:
1620 _ASSERT(false);
1621 return FALSE;
1622 }
1623 *ppModule = tokenAndModulePair.m_pModule;
1624 if (pFoundEntry != NULL)
1625 *pFoundEntry = foundEntry;
1626
1627 return TRUE;
1628 }
1629
1630 EEClassHashEntry_t * pBucket = foundEntry.GetClassHashBasedEntryValue();
1631
1632 if (pBucket == NULL && needsToBuildHashtable)
1633 {
1634 AvailableClasses_LockHolder lh(this);
1635
1636 // Try again with the lock. This will protect against another thread reallocating
1637 // the hash table underneath us
1638 GetClassValue(nhTable, pName, &Data, &pTable, pLookInThisModuleOnly, &foundEntry, loadFlag, needsToBuildHashtable);
1639 pBucket = foundEntry.GetClassHashBasedEntryValue();
1640
1641#ifndef DACCESS_COMPILE
1642 if ((pBucket == NULL) && (m_cUnhashedModules > 0))
1643 {
1644 _ASSERT(needsToBuildHashtable);
1645
1646 if (nhTable == nhCaseInsensitive)
1647 {
1648 LazyPopulateCaseInsensitiveHashTables();
1649 }
1650 else
1651 {
1652 // Note: This codepath is only valid for R2R scenarios
1653 LazyPopulateCaseSensitiveHashTables();
1654 }
1655
1656 // Try yet again with the new classes added
1657 GetClassValue(nhTable, pName, &Data, &pTable, pLookInThisModuleOnly, &foundEntry, loadFlag, needsToBuildHashtable);
1658 pBucket = foundEntry.GetClassHashBasedEntryValue();
1659 _ASSERT(!needsToBuildHashtable);
1660 }
1661#endif
1662 }
1663
1664 if (pBucket == NULL)
1665 {
1666#if defined(_DEBUG_IMPL) && !defined(DACCESS_COMPILE)
1667 LPCUTF8 szName = pName->GetName();
1668 if (szName == NULL)
1669 szName = "<UNKNOWN>";
1670 LOG((LF_CLASSLOADER, LL_INFO10, "Failed to find type \"%s\", assembly \"%ws\" in hash table. Incomplete = %d\n",
1671 szName, GetAssembly()->GetDebugName(), incomplete));
1672#endif
1673 return FALSE;
1674 }
1675
1676 if (pName->GetTable() == nhCaseInsensitive)
1677 {
1678 _ASSERTE(Data);
1679 pBucket = PTR_EEClassHashEntry(Data);
1680 Data = pBucket->GetData();
1681 }
1682
1683 // Lower bit is a discriminator. If the lower bit is NOT SET, it means we have
1684 // a TypeHandle. Otherwise, we have a Module/CL.
1685 if ((dac_cast<TADDR>(Data) & EECLASSHASH_TYPEHANDLE_DISCR) == 0)
1686 {
1687 TypeHandle t = TypeHandle::FromPtr(Data);
1688 _ASSERTE(!t.IsNull());
1689
1690 *pType = t;
1691 if (pFoundEntry != NULL)
1692 {
1693 pFoundEntry->SetClassHashBasedEntryValue(pBucket);
1694 }
1695 return TRUE;
1696 }
1697
1698 // We have a Module/CL
1699 if (!pTable->UncompressModuleAndClassDef(Data,
1700 loadFlag,
1701 ppModule,
1702 pmdClassToken,
1703 pmdFoundExportedType))
1704 {
1705 _ASSERTE(loadFlag != Loader::Load);
1706 return FALSE;
1707 }
1708
1709 *pType = TypeHandle();
1710 if (pFoundEntry != NULL)
1711 {
1712 pFoundEntry->SetClassHashBasedEntryValue(pBucket);
1713 }
1714 return TRUE;
1715} // ClassLoader::FindClassModuleThrowing
1716
1717#ifndef DACCESS_COMPILE
1718// Returns true if the full name (namespace+name) of pName matches that
1719// of typeHnd; otherwise false. Because this is nothrow, it will default
1720// to false for all exceptions (such as OOM).
1721bool CompareNameHandleWithTypeHandleNoThrow(
1722 const NameHandle * pName,
1723 TypeHandle typeHnd)
1724{
1725 bool fRet = false;
1726
1727 EX_TRY
1728 {
1729 // This block is specifically designed to handle transient faults such
1730 // as OOM exceptions.
1731 CONTRACT_VIOLATION(FaultViolation | ThrowsViolation);
1732 StackSString ssBuiltName;
1733 ns::MakePath(ssBuiltName,
1734 StackSString(SString::Utf8, pName->GetNameSpace()),
1735 StackSString(SString::Utf8, pName->GetName()));
1736 StackSString ssName;
1737 typeHnd.GetName(ssName);
1738 fRet = ssName.Equals(ssBuiltName) == TRUE;
1739 }
1740 EX_CATCH
1741 {
1742 // Technically, the above operations should never result in a non-OOM
1743 // exception, but we'll put the rethrow line in there just in case.
1744 CONSISTENCY_CHECK(!GET_EXCEPTION()->IsTerminal());
1745 RethrowTerminalExceptions;
1746 }
1747 EX_END_CATCH(SwallowAllExceptions);
1748
1749 return fRet;
1750}
1751#endif // #ifndef DACCESS_COMPILE
1752
1753// 1024 seems like a good bet at detecting a loop in the type forwarding.
1754static const UINT32 const_cMaxTypeForwardingChainSize = 1024;
1755
1756// Does not throw an exception if the type was not found. Use LoadTypeHandleThrowIfFailed()
1757// instead if you need that.
1758//
1759// Returns:
1760// pName->m_pBucket
1761// Will be set to the 'final' TypeDef bucket if pName->GetTokenType() is mdtBaseType.
1762//
1763TypeHandle
1764ClassLoader::LoadTypeHandleThrowing(
1765 NameHandle * pName,
1766 ClassLoadLevel level,
1767 Module * pLookInThisModuleOnly /*=NULL*/)
1768{
1769 CONTRACT(TypeHandle) {
1770 INSTANCE_CHECK;
1771 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1772 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1773 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1774 DAC_LOADS_TYPE(level, !pName->OKToLoad());
1775 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
1776 PRECONDITION(CheckPointer(pName));
1777 POSTCONDITION(RETVAL.IsNull() || RETVAL.GetLoadLevel() >= level);
1778 MODE_ANY;
1779 SUPPORTS_DAC;
1780 } CONTRACT_END
1781
1782 TypeHandle typeHnd;
1783 INTERIOR_STACK_PROBE_NOTHROW_CHECK_THREAD(RETURN_FROM_INTERIOR_PROBE(TypeHandle()));
1784
1785 Module * pFoundModule = NULL;
1786 mdToken FoundCl;
1787 HashedTypeEntry foundEntry;
1788 mdExportedType FoundExportedType = mdTokenNil;
1789
1790 UINT32 cLoopIterations = 0;
1791
1792 ClassLoader * pClsLdr = this;
1793
1794 while (true)
1795 {
1796 if (cLoopIterations++ >= const_cMaxTypeForwardingChainSize)
1797 { // If we've looped too many times due to type forwarding, return null TypeHandle
1798 // Would prefer to return a format exception, but the original behaviour
1799 // was to detect a stack overflow possibility and return a null, and
1800 // so we need to maintain this.
1801 typeHnd = TypeHandle();
1802 break;
1803 }
1804
1805 // Look outside the lock (though we're actually still a long way from the
1806 // lock at this point...). This may discover that the type is actually
1807 // defined in another module...
1808
1809 if (!pClsLdr->FindClassModuleThrowing(
1810 pName,
1811 &typeHnd,
1812 &FoundCl,
1813 &pFoundModule,
1814 &FoundExportedType,
1815 &foundEntry,
1816 pLookInThisModuleOnly,
1817 pName->OKToLoad() ? Loader::Load
1818 : Loader::DontLoad))
1819 { // Didn't find anything, no point looping indefinitely
1820 break;
1821 }
1822 _ASSERTE(!foundEntry.IsNull());
1823
1824 if (pName->GetTypeToken() == mdtBaseType)
1825 { // We should return the found bucket in the pName
1826 pName->SetBucket(foundEntry);
1827 }
1828
1829 if (!typeHnd.IsNull())
1830 { // Found the cached value, or a constructedtype
1831 if (typeHnd.GetLoadLevel() < level)
1832 {
1833 typeHnd = pClsLdr->LoadTypeDefThrowing(
1834 typeHnd.GetModule(),
1835 typeHnd.GetCl(),
1836 ClassLoader::ReturnNullIfNotFound,
1837 ClassLoader::PermitUninstDefOrRef, // When loading by name we always permit naked type defs/refs
1838 pName->GetTokenNotToLoad(),
1839 level);
1840 }
1841 break;
1842 }
1843
1844 // Found a cl, pModule pair
1845
1846 // If the found module's class loader is not the same as the current class loader,
1847 // then this is a forwarded type and we want to do something else (see
1848 // code:#LoadTypeHandle_TypeForwarded).
1849 if (pFoundModule->GetClassLoader() == pClsLdr)
1850 {
1851 BOOL fTrustTD = TRUE;
1852#ifndef DACCESS_COMPILE
1853 CONTRACT_VIOLATION(ThrowsViolation);
1854 BOOL fVerifyTD = FALSE;
1855
1856 // If this is an exported type with a mdTokenNil class token, then then
1857 // exported type did not give a typedefID hint. We won't be able to trust the typedef
1858 // here.
1859 if ((FoundExportedType != mdTokenNil) && (FoundCl == mdTokenNil))
1860 {
1861 fVerifyTD = TRUE;
1862 fTrustTD = FALSE;
1863 }
1864 // verify that FoundCl is a valid token for pFoundModule, because
1865 // it may be just the hint saved in an ExportedType in another scope
1866 else if (fVerifyTD)
1867 {
1868 fTrustTD = pFoundModule->GetMDImport()->IsValidToken(FoundCl);
1869 }
1870#endif // #ifndef DACCESS_COMPILE
1871
1872 if (fTrustTD)
1873 {
1874 typeHnd = pClsLdr->LoadTypeDefThrowing(
1875 pFoundModule,
1876 FoundCl,
1877 ClassLoader::ReturnNullIfNotFound,
1878 ClassLoader::PermitUninstDefOrRef, // when loading by name we always permit naked type defs/refs
1879 pName->GetTokenNotToLoad(),
1880 level);
1881 }
1882#ifndef DACCESS_COMPILE
1883 // If we used a TypeDef saved in a ExportedType, if we didn't verify
1884 // the hash for this internal module, don't trust the TD value.
1885 if (fVerifyTD)
1886 {
1887 if (typeHnd.IsNull() || !CompareNameHandleWithTypeHandleNoThrow(pName, typeHnd))
1888 {
1889 if (SUCCEEDED(pClsLdr->FindTypeDefByExportedType(
1890 pClsLdr->GetAssembly()->GetManifestImport(),
1891 FoundExportedType,
1892 pFoundModule->GetMDImport(),
1893 &FoundCl)))
1894 {
1895 typeHnd = pClsLdr->LoadTypeDefThrowing(
1896 pFoundModule,
1897 FoundCl,
1898 ClassLoader::ReturnNullIfNotFound,
1899 ClassLoader::PermitUninstDefOrRef,
1900 pName->GetTokenNotToLoad(),
1901 level);
1902 }
1903 else
1904 {
1905 typeHnd = TypeHandle();
1906 }
1907 }
1908 }
1909#endif // #ifndef DACCESS_COMPILE
1910 break;
1911 }
1912 else
1913 { //#LoadTypeHandle_TypeForwarded
1914 // pName is a host instance so it's okay to set fields in it in a DAC build
1915 HashedTypeEntry& bucket = pName->GetBucket();
1916
1917 // Reset pName's bucket entry
1918 if (bucket.GetEntryType() == HashedTypeEntry::IsHashedClassEntry && bucket.GetClassHashBasedEntryValue()->GetEncloser())
1919 {
1920 // We will be searching for the type name again, so set the nesting/context type to the
1921 // encloser of just found type
1922 pName->SetBucket(HashedTypeEntry().SetClassHashBasedEntryValue(bucket.GetClassHashBasedEntryValue()->GetEncloser()));
1923 }
1924 else
1925 {
1926 pName->SetBucket(HashedTypeEntry());
1927 }
1928
1929 // Update the class loader for the new module/token pair.
1930 pClsLdr = pFoundModule->GetClassLoader();
1931 pLookInThisModuleOnly = NULL;
1932 }
1933
1934#ifndef DACCESS_COMPILE
1935 // Replace AvailableClasses Module entry with found TypeHandle
1936 if (!typeHnd.IsNull() &&
1937 typeHnd.IsRestored() &&
1938 foundEntry.GetEntryType() == HashedTypeEntry::EntryType::IsHashedClassEntry &&
1939 (foundEntry.GetClassHashBasedEntryValue() != NULL) &&
1940 (foundEntry.GetClassHashBasedEntryValue()->GetData() != typeHnd.AsPtr()))
1941 {
1942 foundEntry.GetClassHashBasedEntryValue()->SetData(typeHnd.AsPtr());
1943 }
1944#endif // !DACCESS_COMPILE
1945 }
1946
1947 END_INTERIOR_STACK_PROBE;
1948 RETURN typeHnd;
1949} // ClassLoader::LoadTypeHandleThrowing
1950
1951/* static */
1952TypeHandle ClassLoader::LoadPointerOrByrefTypeThrowing(CorElementType typ,
1953 TypeHandle baseType,
1954 LoadTypesFlag fLoadTypes/*=LoadTypes*/,
1955 ClassLoadLevel level/*=CLASS_LOADED*/)
1956{
1957 CONTRACT(TypeHandle)
1958 {
1959 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1960 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1961 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1962 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
1963 MODE_ANY;
1964 PRECONDITION(CheckPointer(baseType));
1965 PRECONDITION(typ == ELEMENT_TYPE_BYREF || typ == ELEMENT_TYPE_PTR);
1966 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
1967 POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
1968 SUPPORTS_DAC;
1969 }
1970 CONTRACT_END
1971
1972 TypeKey key(typ, baseType);
1973 RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level));
1974}
1975
1976/* static */
1977TypeHandle ClassLoader::LoadNativeValueTypeThrowing(TypeHandle baseType,
1978 LoadTypesFlag fLoadTypes/*=LoadTypes*/,
1979 ClassLoadLevel level/*=CLASS_LOADED*/)
1980{
1981 CONTRACT(TypeHandle)
1982 {
1983 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1984 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1985 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1986 MODE_ANY;
1987 PRECONDITION(CheckPointer(baseType));
1988 PRECONDITION(baseType.AsMethodTable()->IsValueType());
1989 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
1990 POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
1991 }
1992 CONTRACT_END
1993
1994 TypeKey key(ELEMENT_TYPE_VALUETYPE, baseType);
1995 RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level));
1996}
1997
1998/* static */
1999TypeHandle ClassLoader::LoadFnptrTypeThrowing(BYTE callConv,
2000 DWORD ntypars,
2001 TypeHandle* inst,
2002 LoadTypesFlag fLoadTypes/*=LoadTypes*/,
2003 ClassLoadLevel level/*=CLASS_LOADED*/)
2004{
2005 CONTRACT(TypeHandle)
2006 {
2007 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2008 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2009 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2010 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
2011 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
2012 POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
2013 MODE_ANY;
2014 SUPPORTS_DAC;
2015 }
2016 CONTRACT_END
2017
2018 TypeKey key(callConv, ntypars, inst);
2019 RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level));
2020}
2021
2022// Find an instantiation of a generic type if it has already been created.
2023// If typeDef is not a generic type or is already instantiated then throw an exception.
2024// If its arity does not match ntypars then throw an exception.
2025// Value will be non-null if we're loading types.
2026/* static */
2027TypeHandle ClassLoader::LoadGenericInstantiationThrowing(Module *pModule,
2028 mdTypeDef typeDef,
2029 Instantiation inst,
2030 LoadTypesFlag fLoadTypes/*=LoadTypes*/,
2031 ClassLoadLevel level/*=CLASS_LOADED*/,
2032 const InstantiationContext *pInstContext/*=NULL*/,
2033 BOOL fFromNativeImage /*=FALSE*/)
2034{
2035 // This can be called in FORBIDGC_LOADER_USE mode by the debugger to find
2036 // a particular generic type instance that is already loaded.
2037 CONTRACT(TypeHandle)
2038 {
2039 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2040 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2041 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
2042 PRECONDITION(CheckPointer(pModule));
2043 MODE_ANY;
2044 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
2045 PRECONDITION(CheckPointer(pInstContext, NULL_OK));
2046 POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
2047 SUPPORTS_DAC;
2048 }
2049 CONTRACT_END
2050
2051 // Essentially all checks to determine if a generic instantiation of a type
2052 // is well-formed go in this method, i.e. this is the
2053 // "choke" point through which all attempts
2054 // to create an instantiation flow. There is a similar choke point for generic
2055 // methods in genmeth.cpp.
2056
2057 if (inst.IsEmpty() || ClassLoader::IsTypicalInstantiation(pModule, typeDef, inst))
2058 {
2059 TypeHandle th = LoadTypeDefThrowing(pModule, typeDef,
2060 ThrowIfNotFound,
2061 PermitUninstDefOrRef,
2062 fLoadTypes == DontLoadTypes ? tdAllTypes : tdNoTypes,
2063 level,
2064 fFromNativeImage ? NULL : &inst);
2065 _ASSERTE(th.GetNumGenericArgs() == inst.GetNumArgs());
2066 RETURN th;
2067 }
2068
2069 if (!fFromNativeImage)
2070 {
2071 TypeHandle th = ClassLoader::LoadTypeDefThrowing(pModule, typeDef,
2072 ThrowIfNotFound,
2073 PermitUninstDefOrRef,
2074 fLoadTypes == DontLoadTypes ? tdAllTypes : tdNoTypes,
2075 level,
2076 fFromNativeImage ? NULL : &inst);
2077 _ASSERTE(th.GetNumGenericArgs() == inst.GetNumArgs());
2078 }
2079
2080 TypeKey key(pModule, typeDef, inst);
2081
2082#ifndef DACCESS_COMPILE
2083 // To avoid loading useless shared instantiations, normalize shared instantiations to the canonical form
2084 // (e.g. Dictionary<String,_Canon> -> Dictionary<_Canon,_Canon>)
2085 // The denormalized shared instantiations should be needed only during JITing, so it is fine to skip this
2086 // for DACCESS_COMPILE.
2087 if (TypeHandle::IsCanonicalSubtypeInstantiation(inst) && !IsCanonicalGenericInstantiation(inst))
2088 {
2089 RETURN(ClassLoader::LoadCanonicalGenericInstantiation(&key, fLoadTypes, level));
2090 }
2091#endif
2092
2093 RETURN(LoadConstructedTypeThrowing(&key, fLoadTypes, level, pInstContext));
2094}
2095
2096// For non-nested classes, gets the ExportedType name and finds the corresponding
2097// TypeDef.
2098// For nested classes, gets the name of the ExportedType and its encloser.
2099// Recursively gets and keeps the name for each encloser until we have the top
2100// level one. Gets the TypeDef token for that. Then, returns from the
2101// recursion, using the last found TypeDef token in order to find the
2102// next nested level down TypeDef token. Finally, returns the TypeDef
2103// token for the type we care about.
2104/*static*/
2105HRESULT ClassLoader::FindTypeDefByExportedType(IMDInternalImport *pCTImport, mdExportedType mdCurrent,
2106 IMDInternalImport *pTDImport, mdTypeDef *mtd)
2107{
2108 CONTRACTL
2109 {
2110 NOTHROW;
2111 GC_NOTRIGGER;
2112 FORBID_FAULT;
2113 MODE_ANY;
2114 SUPPORTS_DAC;
2115 }
2116 CONTRACTL_END
2117
2118 mdToken mdImpl;
2119 LPCSTR szcNameSpace;
2120 LPCSTR szcName;
2121 HRESULT hr;
2122
2123 IfFailRet(pCTImport->GetExportedTypeProps(
2124 mdCurrent,
2125 &szcNameSpace,
2126 &szcName,
2127 &mdImpl,
2128 NULL, //binding
2129 NULL)); //flags
2130
2131 if ((TypeFromToken(mdImpl) == mdtExportedType) &&
2132 (mdImpl != mdExportedTypeNil)) {
2133 // mdCurrent is a nested ExportedType
2134 IfFailRet(FindTypeDefByExportedType(pCTImport, mdImpl, pTDImport, mtd));
2135
2136 // Get TypeDef token for this nested type
2137 return pTDImport->FindTypeDef(szcNameSpace, szcName, *mtd, mtd);
2138 }
2139
2140 // Get TypeDef token for this top-level type
2141 return pTDImport->FindTypeDef(szcNameSpace, szcName, mdTokenNil, mtd);
2142}
2143
2144#ifndef DACCESS_COMPILE
2145
2146VOID ClassLoader::CreateCanonicallyCasedKey(LPCUTF8 pszNameSpace, LPCUTF8 pszName, __out LPUTF8 *ppszOutNameSpace, __out LPUTF8 *ppszOutName)
2147{
2148 CONTRACTL
2149 {
2150 INSTANCE_CHECK;
2151 THROWS;
2152 GC_NOTRIGGER;
2153 INJECT_FAULT(COMPlusThrowOM(););
2154 MODE_ANY;
2155 }
2156 CONTRACTL_END
2157
2158 // We can use the NoThrow versions here because we only call this routine if we're maintaining
2159 // a case-insensitive hash table, and the creation of that table initialized the
2160 // CasingHelper system.
2161 INT32 iNSLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszNameSpace);
2162 if (!iNSLength)
2163 {
2164 COMPlusThrowOM();
2165 }
2166
2167 INT32 iNameLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszName);
2168 if (!iNameLength)
2169 {
2170 COMPlusThrowOM();
2171 }
2172
2173 {
2174 //Calc & allocate path length
2175 //Includes terminating null
2176 S_SIZE_T allocSize = S_SIZE_T(iNSLength) + S_SIZE_T(iNameLength);
2177 if (allocSize.IsOverflow())
2178 {
2179 ThrowHR(COR_E_OVERFLOW);
2180 }
2181
2182 AllocMemHolder<char> pszOutNameSpace (GetAssembly()->GetHighFrequencyHeap()->AllocMem(allocSize));
2183 *ppszOutNameSpace = pszOutNameSpace;
2184
2185 if (iNSLength == 1)
2186 {
2187 **ppszOutNameSpace = '\0';
2188 }
2189 else
2190 {
2191 if (!InternalCasingHelper::InvariantToLowerNoThrow(*ppszOutNameSpace, iNSLength, pszNameSpace))
2192 {
2193 COMPlusThrowOM();
2194 }
2195 }
2196
2197 *ppszOutName = *ppszOutNameSpace + iNSLength;
2198
2199 if (!InternalCasingHelper::InvariantToLowerNoThrow(*ppszOutName, iNameLength, pszName))
2200 {
2201 COMPlusThrowOM();
2202 }
2203
2204 pszOutNameSpace.SuppressRelease();
2205 }
2206}
2207
2208#endif // #ifndef DACCESS_COMPILE
2209
2210
2211//
2212// Return a class that is already loaded
2213// Only for type refs and type defs (not type specs)
2214//
2215/*static*/
2216TypeHandle ClassLoader::LookupTypeDefOrRefInModule(Module *pModule, mdToken cl, ClassLoadLevel *pLoadLevel)
2217{
2218 CONTRACT(TypeHandle)
2219 {
2220 NOTHROW;
2221 GC_NOTRIGGER;
2222 FORBID_FAULT;
2223 MODE_ANY;
2224 PRECONDITION(CheckPointer(pModule));
2225 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
2226 SUPPORTS_DAC;
2227 }
2228 CONTRACT_END
2229
2230 BAD_FORMAT_NOTHROW_ASSERT((TypeFromToken(cl) == mdtTypeRef ||
2231 TypeFromToken(cl) == mdtTypeDef ||
2232 TypeFromToken(cl) == mdtTypeSpec));
2233
2234 TypeHandle typeHandle;
2235
2236 if (TypeFromToken(cl) == mdtTypeDef)
2237 typeHandle = pModule->LookupTypeDef(cl, pLoadLevel);
2238 else if (TypeFromToken(cl) == mdtTypeRef)
2239 {
2240 typeHandle = pModule->LookupTypeRef(cl);
2241
2242 if (pLoadLevel && !typeHandle.IsNull())
2243 {
2244 *pLoadLevel = typeHandle.GetLoadLevel();
2245 }
2246 }
2247
2248 RETURN(typeHandle);
2249}
2250
2251DomainAssembly *ClassLoader::GetDomainAssembly(AppDomain *pDomain/*=NULL*/)
2252{
2253 WRAPPER_NO_CONTRACT;
2254 return GetAssembly()->GetDomainAssembly(pDomain);
2255}
2256
2257#ifndef DACCESS_COMPILE
2258
2259//
2260// Free all modules associated with this loader
2261//
2262void ClassLoader::FreeModules()
2263{
2264 CONTRACTL
2265 {
2266 INSTANCE_CHECK;
2267 NOTHROW;
2268 GC_TRIGGERS;
2269 MODE_PREEMPTIVE;
2270 DISABLED(FORBID_FAULT); //Lots of crud to clean up to make this work
2271 }
2272 CONTRACTL_END;
2273
2274 Module *pManifest = NULL;
2275 if (GetAssembly() && (NULL != (pManifest = GetAssembly()->GetManifestModule()))) {
2276 // Unload the manifest last, since it contains the module list in its rid map
2277 ModuleIterator i = GetAssembly()->IterateModules();
2278 while (i.Next()) {
2279 // Have the module free its various tables and some of the EEClass links
2280 if (i.GetModule() != pManifest)
2281 i.GetModule()->Destruct();
2282 }
2283
2284 // Now do the manifest module.
2285 pManifest->Destruct();
2286 }
2287
2288}
2289
2290ClassLoader::~ClassLoader()
2291{
2292 CONTRACTL
2293 {
2294 NOTHROW;
2295 DESTRUCTOR_CHECK;
2296 GC_TRIGGERS;
2297 MODE_PREEMPTIVE;
2298 DISABLED(FORBID_FAULT); //Lots of crud to clean up to make this work
2299 }
2300 CONTRACTL_END
2301
2302#ifdef _DEBUG
2303 // Do not walk m_pUnresolvedClassHash at destruct time as it is loaderheap allocated memory
2304 // and may already have been deallocated via an AllocMemTracker.
2305 m_pUnresolvedClassHash = (PendingTypeLoadTable*)(UINT_PTR)0xcccccccc;
2306#endif
2307
2308#ifdef _DEBUG
2309// LOG((
2310// LF_CLASSLOADER,
2311// INFO3,
2312// "Deleting classloader %x\n"
2313// " >EEClass data: %10d bytes\n"
2314// " >Classname hash: %10d bytes\n"
2315// " >FieldDesc data: %10d bytes\n"
2316// " >MethodDesc data: %10d bytes\n"
2317// " >GCInfo: %10d bytes\n"
2318// " >Interface maps: %10d bytes\n"
2319// " >MethodTables: %10d bytes\n"
2320// " >Vtables: %10d bytes\n"
2321// " >Static fields: %10d bytes\n"
2322// "# methods: %10d\n"
2323// "# field descs: %10d\n"
2324// "# classes: %10d\n"
2325// "# dup intf slots: %10d\n"
2326// "# array classrefs: %10d\n"
2327// "Array class overhead:%10d bytes\n",
2328// this,
2329// m_dwEEClassData,
2330// m_pAvailableClasses->m_dwDebugMemory,
2331// m_dwFieldDescData,
2332// m_dwMethodDescData,
2333// m_dwGCSize,
2334// m_dwInterfaceMapSize,
2335// m_dwMethodTableSize,
2336// m_dwVtableData,
2337// m_dwStaticFieldData,
2338// m_dwDebugMethods,
2339// m_dwDebugFieldDescs,
2340// m_dwDebugClasses,
2341// m_dwDebugDuplicateInterfaceSlots,
2342// ));
2343#endif
2344
2345 FreeModules();
2346
2347 m_UnresolvedClassLock.Destroy();
2348 m_AvailableClassLock.Destroy();
2349 m_AvailableTypesLock.Destroy();
2350}
2351
2352
2353//----------------------------------------------------------------------------
2354// The constructor should only initialize enough to ensure that the destructor doesn't
2355// crash. It cannot allocate or do anything that might fail as that would leave
2356// the ClassLoader undestructable. Any such tasks should be done in ClassLoader::Init().
2357//----------------------------------------------------------------------------
2358ClassLoader::ClassLoader(Assembly *pAssembly)
2359{
2360 CONTRACTL
2361 {
2362 CONSTRUCTOR_CHECK;
2363 NOTHROW;
2364 GC_NOTRIGGER;
2365 MODE_ANY;
2366 FORBID_FAULT;
2367 }
2368 CONTRACTL_END
2369
2370 m_pAssembly = pAssembly;
2371
2372 m_pUnresolvedClassHash = NULL;
2373 m_cUnhashedModules = 0;
2374
2375#ifdef _DEBUG
2376 m_dwDebugMethods = 0;
2377 m_dwDebugFieldDescs = 0;
2378 m_dwDebugClasses = 0;
2379 m_dwDebugDuplicateInterfaceSlots = 0;
2380 m_dwGCSize = 0;
2381 m_dwInterfaceMapSize = 0;
2382 m_dwMethodTableSize = 0;
2383 m_dwVtableData = 0;
2384 m_dwStaticFieldData = 0;
2385 m_dwFieldDescData = 0;
2386 m_dwMethodDescData = 0;
2387 m_dwEEClassData = 0;
2388#endif
2389}
2390
2391
2392//----------------------------------------------------------------------------
2393// This function completes the initialization of the ClassLoader. It can
2394// assume the constructor is run and that the function is entered with
2395// ClassLoader in a safely destructable state. This function can throw
2396// but whether it throws or succeeds, it must leave the ClassLoader in a safely
2397// destructable state.
2398//----------------------------------------------------------------------------
2399VOID ClassLoader::Init(AllocMemTracker *pamTracker)
2400{
2401 STANDARD_VM_CONTRACT;
2402
2403 m_pUnresolvedClassHash = PendingTypeLoadTable::Create(GetAssembly()->GetLowFrequencyHeap(),
2404 UNRESOLVED_CLASS_HASH_BUCKETS,
2405 pamTracker);
2406
2407 m_UnresolvedClassLock.Init(CrstUnresolvedClassLock);
2408
2409 // This lock is taken within the classloader whenever we have to enter a
2410 // type in one of the modules governed by the loader.
2411 // The process of creating these types may be reentrant. The ordering has
2412 // not yet been sorted out, and when we sort it out we should also modify the
2413 // ordering for m_AvailableTypesLock in BaseDomain.
2414 m_AvailableClassLock.Init(
2415 CrstAvailableClass,
2416 CRST_REENTRANCY);
2417
2418 // This lock is taken within the classloader whenever we have to insert a new param. type into the table
2419 // This lock also needs to be taken for a read operation in a GC_NOTRIGGER scope, thus the ANYMODE flag.
2420 m_AvailableTypesLock.Init(
2421 CrstAvailableParamTypes,
2422 (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD));
2423
2424#ifdef _DEBUG
2425 CorTypeInfo::CheckConsistency();
2426#endif
2427
2428}
2429
2430#endif // #ifndef DACCESS_COMPILE
2431
2432/*static*/
2433TypeHandle ClassLoader::LoadTypeDefOrRefOrSpecThrowing(Module *pModule,
2434 mdToken typeDefOrRefOrSpec,
2435 const SigTypeContext *pTypeContext,
2436 NotFoundAction fNotFoundAction /* = ThrowIfNotFound */ ,
2437 PermitUninstantiatedFlag fUninstantiated /* = FailIfUninstDefOrRef */,
2438 LoadTypesFlag fLoadTypes/*=LoadTypes*/ ,
2439 ClassLoadLevel level /* = CLASS_LOADED */,
2440 BOOL dropGenericArgumentLevel /* = FALSE */,
2441 const Substitution *pSubst)
2442{
2443 CONTRACT(TypeHandle)
2444 {
2445 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2446 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2447 MODE_ANY;
2448 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2449 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
2450 PRECONDITION(CheckPointer(pModule));
2451 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
2452 PRECONDITION(FORBIDGC_LOADER_USE_ENABLED() || GetAppDomain()->CheckCanLoadTypes(pModule->GetAssembly()));
2453 POSTCONDITION(CheckPointer(RETVAL, (fNotFoundAction == ThrowIfNotFound)? NULL_NOT_OK : NULL_OK));
2454 }
2455 CONTRACT_END
2456
2457 if (TypeFromToken(typeDefOrRefOrSpec) == mdtTypeSpec)
2458 {
2459 ULONG cSig;
2460 PCCOR_SIGNATURE pSig;
2461
2462 IMDInternalImport *pInternalImport = pModule->GetMDImport();
2463 if (FAILED(pInternalImport->GetTypeSpecFromToken(typeDefOrRefOrSpec, &pSig, &cSig)))
2464 {
2465#ifndef DACCESS_COMPILE
2466 if (fNotFoundAction == ThrowIfNotFound)
2467 {
2468 pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDefOrRefOrSpec, IDS_CLASSLOAD_BADFORMAT);
2469 }
2470#endif //!DACCESS_COMPILE
2471 RETURN (TypeHandle());
2472 }
2473 SigPointer sigptr(pSig, cSig);
2474 TypeHandle typeHnd = sigptr.GetTypeHandleThrowing(pModule, pTypeContext, fLoadTypes,
2475 level, dropGenericArgumentLevel, pSubst);
2476#ifndef DACCESS_COMPILE
2477 if ((fNotFoundAction == ThrowIfNotFound) && typeHnd.IsNull())
2478 pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDefOrRefOrSpec,
2479 IDS_CLASSLOAD_GENERAL);
2480#endif
2481 RETURN (typeHnd);
2482 }
2483 else
2484 {
2485 RETURN (LoadTypeDefOrRefThrowing(pModule, typeDefOrRefOrSpec,
2486 fNotFoundAction,
2487 fUninstantiated,
2488 ((fLoadTypes == LoadTypes) ? tdNoTypes : tdAllTypes),
2489 level));
2490 }
2491} // ClassLoader::LoadTypeDefOrRefOrSpecThrowing
2492
2493// Given a token specifying a typeDef, and a module in which to
2494// interpret that token, find or load the corresponding type handle.
2495//
2496//
2497/*static*/
2498TypeHandle ClassLoader::LoadTypeDefThrowing(Module *pModule,
2499 mdToken typeDef,
2500 NotFoundAction fNotFoundAction /* = ThrowIfNotFound */ ,
2501 PermitUninstantiatedFlag fUninstantiated /* = FailIfUninstDefOrRef */,
2502 mdToken tokenNotToLoad,
2503 ClassLoadLevel level,
2504 Instantiation * pTargetInstantiation)
2505{
2506
2507 CONTRACT(TypeHandle)
2508 {
2509 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2510 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2511 MODE_ANY;
2512 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2513 DAC_LOADS_TYPE(level, !NameHandle::OKToLoad(typeDef, tokenNotToLoad));
2514 PRECONDITION(CheckPointer(pModule));
2515 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
2516 PRECONDITION(FORBIDGC_LOADER_USE_ENABLED()
2517 || GetAppDomain()->CheckCanLoadTypes(pModule->GetAssembly()));
2518
2519 POSTCONDITION(CheckPointer(RETVAL, NameHandle::OKToLoad(typeDef, tokenNotToLoad) && (fNotFoundAction == ThrowIfNotFound) ? NULL_NOT_OK : NULL_OK));
2520 POSTCONDITION(RETVAL.IsNull() || RETVAL.GetCl() == typeDef);
2521 SUPPORTS_DAC;
2522 }
2523 CONTRACT_END;
2524
2525 TypeHandle typeHnd;
2526
2527 // First, attempt to find the class if it is already loaded
2528 ClassLoadLevel existingLoadLevel = CLASS_LOAD_BEGIN;
2529 typeHnd = pModule->LookupTypeDef(typeDef, &existingLoadLevel);
2530 if (!typeHnd.IsNull())
2531 {
2532#ifndef DACCESS_COMPILE
2533 // If the type is loaded, we can do cheap arity verification
2534 if (pTargetInstantiation != NULL && pTargetInstantiation->GetNumArgs() != typeHnd.AsMethodTable()->GetNumGenericArgs())
2535 pModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(), typeDef, IDS_CLASSLOAD_TYPEWRONGNUMGENERICARGS);
2536#endif
2537
2538 if (existingLoadLevel >= level)
2539 RETURN(typeHnd);
2540 }
2541
2542 // We don't want to probe on any threads except for those with a managed thread. This function
2543 // can be called from the GC thread etc. so need to control how we probe.
2544 INTERIOR_STACK_PROBE_NOTHROW_CHECK_THREAD(goto Exit;);
2545
2546 IMDInternalImport *pInternalImport = pModule->GetMDImport();
2547
2548#ifndef DACCESS_COMPILE
2549 if (typeHnd.IsNull() && pTargetInstantiation != NULL)
2550 {
2551 // If the type is not loaded yet, we have to do heavy weight arity verification based on metadata
2552 HENUMInternal hEnumGenericPars;
2553 HRESULT hr = pInternalImport->EnumInit(mdtGenericParam, typeDef, &hEnumGenericPars);
2554 if (FAILED(hr))
2555 pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDef, IDS_CLASSLOAD_BADFORMAT);
2556 DWORD nGenericClassParams = pInternalImport->EnumGetCount(&hEnumGenericPars);
2557 pInternalImport->EnumClose(&hEnumGenericPars);
2558
2559 if (pTargetInstantiation->GetNumArgs() != nGenericClassParams)
2560 pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, typeDef, IDS_CLASSLOAD_TYPEWRONGNUMGENERICARGS);
2561 }
2562#endif
2563
2564 if (IsNilToken(typeDef) || TypeFromToken(typeDef) != mdtTypeDef || !pInternalImport->IsValidToken(typeDef) )
2565 {
2566 LOG((LF_CLASSLOADER, LL_INFO10, "Bogus class token to load: 0x%08x\n", typeDef));
2567 typeHnd = TypeHandle();
2568 }
2569 else
2570 {
2571 // *****************************************************************************
2572 //
2573 // Important invariant:
2574 //
2575 // The rule here is that we never go to LoadTypeHandleForTypeKey if a Find should succeed.
2576 // This is vital, because otherwise a stack crawl will open up opportunities for
2577 // GC. Since operations like setting up a GCFrame will trigger a crawl in stress
2578 // mode, a GC at that point would be disastrous. We can't assert this, because
2579 // of race conditions. (In other words, the type could suddently be find-able
2580 // because another thread loaded it while we were in this method.
2581
2582 // Not found - try to load it unless we are told not to
2583
2584#ifndef DACCESS_COMPILE
2585 if ( !NameHandle::OKToLoad(typeDef, tokenNotToLoad) )
2586 {
2587 typeHnd = TypeHandle();
2588 }
2589 else
2590 {
2591 // Anybody who puts himself in a FORBIDGC_LOADER state has promised
2592 // to use us only for resolving, not loading. We are now transitioning into
2593 // loading.
2594#ifdef _DEBUG_IMPL
2595 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
2596#endif
2597 TRIGGERSGC();
2598
2599 if (pModule->IsReflection())
2600 {
2601 // Don't try to load types that are not in available table, when this
2602 // is an in-memory module. Raise the type-resolve event instead.
2603 typeHnd = TypeHandle();
2604
2605 // Avoid infinite recursion
2606 if (tokenNotToLoad != tdAllAssemblies)
2607 {
2608 AppDomain* pDomain = SystemDomain::GetCurrentDomain();
2609
2610 LPUTF8 pszFullName;
2611 LPCUTF8 className;
2612 LPCUTF8 nameSpace;
2613 if (FAILED(pInternalImport->GetNameOfTypeDef(typeDef, &className, &nameSpace)))
2614 {
2615 LOG((LF_CLASSLOADER, LL_INFO10, "Bogus TypeDef record while loading: 0x%08x\n", typeDef));
2616 typeHnd = TypeHandle();
2617 }
2618 else
2619 {
2620 MAKE_FULL_PATH_ON_STACK_UTF8(pszFullName,
2621 nameSpace,
2622 className);
2623 GCX_COOP();
2624 ASSEMBLYREF asmRef = NULL;
2625 DomainAssembly *pDomainAssembly = NULL;
2626 GCPROTECT_BEGIN(asmRef);
2627
2628 pDomainAssembly = pDomain->RaiseTypeResolveEventThrowing(
2629 pModule->GetAssembly()->GetDomainAssembly(),
2630 pszFullName, &asmRef);
2631
2632 if (asmRef != NULL)
2633 {
2634 _ASSERTE(pDomainAssembly != NULL);
2635 if (pDomainAssembly->GetAssembly()->GetLoaderAllocator()->IsCollectible())
2636 {
2637 if (!pModule->GetLoaderAllocator()->IsCollectible())
2638 {
2639 LOG((LF_CLASSLOADER, LL_INFO10, "Bad result from TypeResolveEvent while loader TypeDef record: 0x%08x\n", typeDef));
2640 COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible"));
2641 }
2642
2643 pModule->GetLoaderAllocator()->EnsureReference(pDomainAssembly->GetAssembly()->GetLoaderAllocator());
2644 }
2645 }
2646 GCPROTECT_END();
2647 if (pDomainAssembly != NULL)
2648 {
2649 Assembly *pAssembly = pDomainAssembly->GetAssembly();
2650
2651 NameHandle name(nameSpace, className);
2652 name.SetTypeToken(pModule, typeDef);
2653 name.SetTokenNotToLoad(tdAllAssemblies);
2654 typeHnd = pAssembly->GetLoader()->LoadTypeHandleThrowing(&name, level);
2655 }
2656 }
2657 }
2658 }
2659 else
2660 {
2661 TypeKey typeKey(pModule, typeDef);
2662 typeHnd = pModule->GetClassLoader()->LoadTypeHandleForTypeKey(&typeKey,
2663 typeHnd,
2664 level);
2665 }
2666 }
2667#endif // !DACCESS_COMPILE
2668 }
2669
2670// If stack guards are disabled, then this label is unreferenced and produces a compile error.
2671#if defined(FEATURE_STACK_PROBE) && !defined(DACCESS_COMPILE)
2672Exit:
2673#endif
2674
2675#ifndef DACCESS_COMPILE
2676 if ((fUninstantiated == FailIfUninstDefOrRef) && !typeHnd.IsNull() && typeHnd.IsGenericTypeDefinition())
2677 {
2678 typeHnd = TypeHandle();
2679 }
2680
2681 if ((fNotFoundAction == ThrowIfNotFound) && typeHnd.IsNull() && (tokenNotToLoad != tdAllTypes))
2682 {
2683 pModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(),
2684 typeDef,
2685 IDS_CLASSLOAD_GENERAL);
2686 }
2687#endif
2688 ;
2689 END_INTERIOR_STACK_PROBE;
2690
2691 RETURN(typeHnd);
2692}
2693
2694// Given a token specifying a typeDef or typeRef, and a module in
2695// which to interpret that token, find or load the corresponding type
2696// handle.
2697//
2698/*static*/
2699TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule,
2700 mdToken typeDefOrRef,
2701 NotFoundAction fNotFoundAction /* = ThrowIfNotFound */ ,
2702 PermitUninstantiatedFlag fUninstantiated /* = FailIfUninstDefOrRef */,
2703 mdToken tokenNotToLoad,
2704 ClassLoadLevel level)
2705{
2706
2707 CONTRACT(TypeHandle)
2708 {
2709 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2710 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2711 MODE_ANY;
2712 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2713 PRECONDITION(CheckPointer(pModule));
2714 PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
2715 PRECONDITION(FORBIDGC_LOADER_USE_ENABLED()
2716 || GetAppDomain()->CheckCanLoadTypes(pModule->GetAssembly()));
2717
2718 POSTCONDITION(CheckPointer(RETVAL, NameHandle::OKToLoad(typeDefOrRef, tokenNotToLoad) && (fNotFoundAction == ThrowIfNotFound) ? NULL_NOT_OK : NULL_OK));
2719 POSTCONDITION(level <= CLASS_LOAD_UNRESTORED || RETVAL.IsNull() || RETVAL.IsRestored());
2720 SUPPORTS_DAC;
2721 }
2722 CONTRACT_END;
2723
2724 // NotFoundAction could be the bizarre 'ThrowButNullV11McppWorkaround',
2725 // which means ThrowIfNotFound EXCEPT if this might be the Everett MCPP
2726 // Nil-token ResolutionScope for value type. In that case, it means
2727 // ReturnNullIfNotFound.
2728 // If we have ThrowButNullV11McppWorkaround, remember that NULL *might*
2729 // be OK if there is no resolution scope, but change the value to
2730 // ThrowIfNotFound.
2731 BOOLEAN bReturnNullOkWhenNoResolutionScope = false;
2732 if (fNotFoundAction == ThrowButNullV11McppWorkaround)
2733 {
2734 bReturnNullOkWhenNoResolutionScope = true;
2735 fNotFoundAction = ThrowIfNotFound;
2736 }
2737
2738 // First, attempt to find the class if it is already loaded
2739 ClassLoadLevel existingLoadLevel = CLASS_LOAD_BEGIN;
2740 TypeHandle typeHnd = LookupTypeDefOrRefInModule(pModule, typeDefOrRef, &existingLoadLevel);
2741 if (!typeHnd.IsNull())
2742 {
2743 if (existingLoadLevel < level)
2744 {
2745 pModule = typeHnd.GetModule();
2746 typeDefOrRef = typeHnd.GetCl();
2747 }
2748 }
2749
2750 if (!typeHnd.IsNull() && existingLoadLevel >= level)
2751 {
2752 // perform the check that it's not an uninstantiated TypeDef/TypeRef
2753 // being used inappropriately.
2754 if (!((fUninstantiated == FailIfUninstDefOrRef) && !typeHnd.IsNull() && typeHnd.IsGenericTypeDefinition()))
2755 {
2756 RETURN(typeHnd);
2757 }
2758 }
2759 else
2760 {
2761 // otherwise try to resolve the TypeRef and/or load the corresponding TypeDef
2762 IMDInternalImport *pInternalImport = pModule->GetMDImport();
2763 mdToken tokType = TypeFromToken(typeDefOrRef);
2764
2765 if (IsNilToken(typeDefOrRef) || ((tokType != mdtTypeDef)&&(tokType != mdtTypeRef))
2766 || !pInternalImport->IsValidToken(typeDefOrRef) )
2767 {
2768#ifdef _DEBUG
2769 LOG((LF_CLASSLOADER, LL_INFO10, "Bogus class token to load: 0x%08x\n", typeDefOrRef));
2770#endif
2771
2772 typeHnd = TypeHandle();
2773 }
2774
2775 else if (tokType == mdtTypeRef)
2776 {
2777 BOOL fNoResolutionScope;
2778 Module *pFoundModule = Assembly::FindModuleByTypeRef(pModule, typeDefOrRef,
2779 tokenNotToLoad==tdAllTypes ?
2780 Loader::DontLoad :
2781 Loader::Load,
2782 &fNoResolutionScope);
2783
2784 if (pFoundModule != NULL)
2785 {
2786
2787 // Not in my module, have to look it up by name. This is the primary path
2788 // taken by the TypeRef case, i.e. we've resolve a TypeRef to a TypeDef/Module
2789 // pair.
2790 LPCUTF8 pszNameSpace;
2791 LPCUTF8 pszClassName;
2792 if (FAILED(pInternalImport->GetNameOfTypeRef(
2793 typeDefOrRef,
2794 &pszNameSpace,
2795 &pszClassName)))
2796 {
2797 typeHnd = TypeHandle();
2798 }
2799 else
2800 {
2801 if (fNoResolutionScope)
2802 {
2803 // Everett C++ compiler can generate a TypeRef with RS=0
2804 // without respective TypeDef for unmanaged valuetypes,
2805 // referenced only by pointers to them,
2806 // so we can fail to load legally w/ no exception
2807 typeHnd = ClassLoader::LoadTypeByNameThrowing(pFoundModule->GetAssembly(),
2808 pszNameSpace,
2809 pszClassName,
2810 ClassLoader::ReturnNullIfNotFound,
2811 tokenNotToLoad==tdAllTypes ? ClassLoader::DontLoadTypes : ClassLoader::LoadTypes,
2812 level);
2813
2814 if(typeHnd.IsNull() && bReturnNullOkWhenNoResolutionScope)
2815 {
2816 fNotFoundAction = ReturnNullIfNotFound;
2817 RETURN(typeHnd);
2818 }
2819 }
2820 else
2821 {
2822 NameHandle nameHandle(pModule, typeDefOrRef);
2823 nameHandle.SetName(pszNameSpace, pszClassName);
2824 nameHandle.SetTokenNotToLoad(tokenNotToLoad);
2825 typeHnd = pFoundModule->GetClassLoader()->
2826 LoadTypeHandleThrowIfFailed(&nameHandle, level,
2827 pFoundModule->IsReflection() ? NULL : pFoundModule);
2828 }
2829 }
2830
2831#ifndef DACCESS_COMPILE
2832 if (!(typeHnd.IsNull()))
2833 pModule->StoreTypeRef(typeDefOrRef, typeHnd);
2834#endif
2835 }
2836 }
2837 else
2838 {
2839 // This is the mdtTypeDef case...
2840 typeHnd = LoadTypeDefThrowing(pModule, typeDefOrRef,
2841 fNotFoundAction,
2842 fUninstantiated,
2843 tokenNotToLoad,
2844 level);
2845 }
2846 }
2847 TypeHandle thRes = typeHnd;
2848
2849 // reject the load if it's an uninstantiated TypeDef/TypeRef
2850 // being used inappropriately.
2851 if ((fUninstantiated == FailIfUninstDefOrRef) && !typeHnd.IsNull() && typeHnd.IsGenericTypeDefinition())
2852 thRes = TypeHandle();
2853
2854 // perform the check to throw when the thing is not found
2855 if ((fNotFoundAction == ThrowIfNotFound) && thRes.IsNull() && (tokenNotToLoad != tdAllTypes))
2856 {
2857#ifndef DACCESS_COMPILE
2858 pModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(),
2859 typeDefOrRef,
2860 IDS_CLASSLOAD_GENERAL);
2861#else
2862 DacNotImpl();
2863#endif
2864 }
2865
2866 RETURN(thRes);
2867}
2868
2869/*static*/
2870BOOL
2871ClassLoader::ResolveTokenToTypeDefThrowing(
2872 Module * pTypeRefModule,
2873 mdTypeRef typeRefToken,
2874 Module ** ppTypeDefModule,
2875 mdTypeDef * pTypeDefToken,
2876 Loader::LoadFlag loadFlag,
2877 BOOL * pfUsesTypeForwarder) // The semantic of this parameter: TRUE if a type forwarder is found. It is never set to FALSE.
2878{
2879 CONTRACT(BOOL)
2880 {
2881 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2882 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2883 MODE_ANY;
2884 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2885 PRECONDITION(CheckPointer(pTypeRefModule));
2886 SUPPORTS_DAC;
2887 }
2888 CONTRACT_END;
2889
2890 // It's a TypeDef already
2891 if (TypeFromToken(typeRefToken) == mdtTypeDef)
2892 {
2893 if (ppTypeDefModule != NULL)
2894 *ppTypeDefModule = pTypeRefModule;
2895 if (pTypeDefToken != NULL)
2896 *pTypeDefToken = typeRefToken;
2897 RETURN TRUE;
2898 }
2899
2900 TypeHandle typeHnd = pTypeRefModule->LookupTypeRef(typeRefToken);
2901
2902 // Type is already (partially) loaded and cached in the module's TypeRef table
2903 // Do not return here if we are checking for type forwarders
2904 if (!typeHnd.IsNull() && (pfUsesTypeForwarder == NULL))
2905 {
2906 if (ppTypeDefModule != NULL)
2907 *ppTypeDefModule = typeHnd.GetModule();
2908 if (pTypeDefToken != NULL)
2909 *pTypeDefToken = typeHnd.GetCl();
2910 RETURN TRUE;
2911 }
2912
2913 BOOL fNoResolutionScope; //not used
2914 Module * pFoundRefModule = Assembly::FindModuleByTypeRef(
2915 pTypeRefModule,
2916 typeRefToken,
2917 loadFlag,
2918 &fNoResolutionScope);
2919
2920 if (pFoundRefModule == NULL)
2921 { // We didn't find the TypeRef anywhere
2922 RETURN FALSE;
2923 }
2924
2925 // If checking for type forwarders, then we can see if a type forwarder was used based on the output of
2926 // pFoundRefModule and typeHnd (if typeHnd is set)
2927 if (!typeHnd.IsNull() && (pfUsesTypeForwarder != NULL))
2928 {
2929 if (typeHnd.GetModule() != pFoundRefModule)
2930 {
2931 *pfUsesTypeForwarder = TRUE;
2932 }
2933
2934 if (ppTypeDefModule != NULL)
2935 *ppTypeDefModule = typeHnd.GetModule();
2936 if (pTypeDefToken != NULL)
2937 *pTypeDefToken = typeHnd.GetCl();
2938 RETURN TRUE;
2939 }
2940
2941 // Not in my module, have to look it up by name
2942 LPCUTF8 pszNameSpace;
2943 LPCUTF8 pszClassName;
2944 if (FAILED(pTypeRefModule->GetMDImport()->GetNameOfTypeRef(typeRefToken, &pszNameSpace, &pszClassName)))
2945 {
2946 RETURN FALSE;
2947 }
2948 NameHandle nameHandle(pTypeRefModule, typeRefToken);
2949 nameHandle.SetName(pszNameSpace, pszClassName);
2950 if (loadFlag != Loader::Load)
2951 {
2952 nameHandle.SetTokenNotToLoad(tdAllTypes);
2953 }
2954
2955 return ResolveNameToTypeDefThrowing(pFoundRefModule, &nameHandle, ppTypeDefModule, pTypeDefToken, loadFlag, pfUsesTypeForwarder);
2956}
2957
2958/*static*/
2959BOOL
2960ClassLoader::ResolveNameToTypeDefThrowing(
2961 Module * pModule,
2962 NameHandle * pName,
2963 Module ** ppTypeDefModule,
2964 mdTypeDef * pTypeDefToken,
2965 Loader::LoadFlag loadFlag,
2966 BOOL * pfUsesTypeForwarder) // The semantic of this parameter: TRUE if a type forwarder is found. It is never set to FALSE.
2967{
2968 CONTRACT(BOOL)
2969 {
2970 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2971 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2972 MODE_ANY;
2973 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2974 PRECONDITION(CheckPointer(pModule));
2975 PRECONDITION(CheckPointer(pName));
2976 SUPPORTS_DAC;
2977 }
2978 CONTRACT_END;
2979
2980 TypeHandle typeHnd;
2981 mdToken foundTypeDef;
2982 Module * pFoundModule;
2983 mdExportedType foundExportedType;
2984 Module * pSourceModule = pModule;
2985 Module * pFoundRefModule = pModule;
2986
2987 for (UINT32 nTypeForwardingChainSize = 0; nTypeForwardingChainSize < const_cMaxTypeForwardingChainSize; nTypeForwardingChainSize++)
2988 {
2989 foundTypeDef = mdTokenNil;
2990 pFoundModule = NULL;
2991 foundExportedType = mdTokenNil;
2992 if (!pSourceModule->GetClassLoader()->FindClassModuleThrowing(
2993 pName,
2994 &typeHnd,
2995 &foundTypeDef,
2996 &pFoundModule,
2997 &foundExportedType,
2998 NULL,
2999 pSourceModule->IsReflection() ? NULL : pSourceModule,
3000 loadFlag))
3001 {
3002 RETURN FALSE;
3003 }
3004
3005 // Type is already loaded and cached in the loader's by-name table
3006 if (!typeHnd.IsNull())
3007 {
3008 if ((typeHnd.GetModule() != pFoundRefModule) && (pfUsesTypeForwarder != NULL))
3009 { // We followed at least one type forwarder to resolve the type
3010 *pfUsesTypeForwarder = TRUE;
3011 }
3012 if (ppTypeDefModule != NULL)
3013 *ppTypeDefModule = typeHnd.GetModule();
3014 if (pTypeDefToken != NULL)
3015 *pTypeDefToken = typeHnd.GetCl();
3016 RETURN TRUE;
3017 }
3018
3019 if (pFoundModule == NULL)
3020 { // Module was probably not loaded
3021 RETURN FALSE;
3022 }
3023
3024 if (TypeFromToken(foundExportedType) != mdtExportedType)
3025 { // It's not exported type
3026 _ASSERTE(foundExportedType == mdTokenNil);
3027
3028 if ((pFoundModule != pFoundRefModule) && (pfUsesTypeForwarder != NULL))
3029 { // We followed at least one type forwarder to resolve the type
3030 *pfUsesTypeForwarder = TRUE;
3031 }
3032 if (pTypeDefToken != NULL)
3033 *pTypeDefToken = foundTypeDef;
3034 if (ppTypeDefModule != NULL)
3035 *ppTypeDefModule = pFoundModule;
3036 RETURN TRUE;
3037 }
3038 // It's exported type
3039
3040 // Repeat the search for the type in the newly found module
3041 pSourceModule = pFoundModule;
3042 }
3043 // Type forwarding chain is too long
3044 RETURN FALSE;
3045} // ClassLoader::ResolveTokenToTypeDefThrowing
3046
3047#ifndef DACCESS_COMPILE
3048
3049//---------------------------------------------------------------------------------------
3050//
3051//static
3052VOID
3053ClassLoader::GetEnclosingClassThrowing(
3054 IMDInternalImport * pInternalImport,
3055 Module * pModule,
3056 mdTypeDef cl,
3057 mdTypeDef * tdEnclosing)
3058{
3059 CONTRACTL
3060 {
3061 THROWS;
3062 GC_TRIGGERS;
3063 INJECT_FAULT(COMPlusThrowOM());
3064 MODE_ANY;
3065 }
3066 CONTRACTL_END;
3067
3068 _ASSERTE(tdEnclosing);
3069 *tdEnclosing = mdTypeDefNil;
3070
3071 HRESULT hr = pInternalImport->GetNestedClassProps(cl, tdEnclosing);
3072
3073 if (FAILED(hr))
3074 {
3075 if (hr != CLDB_E_RECORD_NOTFOUND)
3076 COMPlusThrowHR(hr);
3077 return;
3078 }
3079
3080 if (TypeFromToken(*tdEnclosing) != mdtTypeDef)
3081 pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_ENCLOSING);
3082} // ClassLoader::GetEnclosingClassThrowing
3083
3084
3085//---------------------------------------------------------------------------------------
3086//
3087// Load a parent type or implemented interface type.
3088//
3089// If this is an instantiated type represented by a type spec, then instead of attempting to load the
3090// exact type, load an approximate instantiation in which all reference types are replaced by Object.
3091// The exact instantiated types will be loaded later by LoadInstantiatedInfo.
3092// We do this to avoid cycles early in class loading caused by definitions such as
3093// struct M : ICloneable<M> // load ICloneable<object>
3094// class C<T> : D<C<T>,int> for any T // load D<object,int>
3095//
3096//static
3097TypeHandle
3098ClassLoader::LoadApproxTypeThrowing(
3099 Module * pModule,
3100 mdToken tok,
3101 SigPointer * pSigInst,
3102 const SigTypeContext * pClassTypeContext)
3103{
3104 CONTRACT(TypeHandle)
3105 {
3106 THROWS;
3107 GC_TRIGGERS;
3108 INJECT_FAULT(COMPlusThrowOM());
3109 MODE_ANY;
3110 PRECONDITION(CheckPointer(pSigInst, NULL_OK));
3111 PRECONDITION(CheckPointer(pModule));
3112 POSTCONDITION(CheckPointer(RETVAL));
3113 }
3114 CONTRACT_END;
3115
3116 IMDInternalImport * pInternalImport = pModule->GetMDImport();
3117
3118 if (TypeFromToken(tok) == mdtTypeSpec)
3119 {
3120 ULONG cSig;
3121 PCCOR_SIGNATURE pSig;
3122 IfFailThrowBF(pInternalImport->GetTypeSpecFromToken(tok, &pSig, &cSig), BFA_METADATA_CORRUPT, pModule);
3123
3124 SigPointer sigptr = SigPointer(pSig, cSig);
3125 CorElementType type = ELEMENT_TYPE_END;
3126 IfFailThrowBF(sigptr.GetElemType(&type), BFA_BAD_SIGNATURE, pModule);
3127
3128 // The only kind of type specs that we recognise are instantiated types
3129 if (type != ELEMENT_TYPE_GENERICINST)
3130 pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, tok, IDS_CLASSLOAD_GENERAL);
3131
3132 // Of these, we outlaw instantiated value classes (they can't be interfaces and can't be subclassed)
3133 IfFailThrowBF(sigptr.GetElemType(&type), BFA_BAD_SIGNATURE, pModule);
3134
3135 if (type != ELEMENT_TYPE_CLASS)
3136 pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, tok, IDS_CLASSLOAD_GENERAL);
3137
3138 mdToken genericTok = 0;
3139 IfFailThrowBF(sigptr.GetToken(&genericTok), BFA_BAD_SIGNATURE, pModule);
3140 IfFailThrowBF(sigptr.GetData(NULL), BFA_BAD_SIGNATURE, pModule);
3141
3142 if (pSigInst != NULL)
3143 *pSigInst = sigptr;
3144
3145 // Try to load the generic type itself
3146 THROW_BAD_FORMAT_MAYBE(
3147 ((TypeFromToken(genericTok) == mdtTypeRef) || (TypeFromToken(genericTok) == mdtTypeDef)),
3148 BFA_UNEXPECTED_GENERIC_TOKENTYPE,
3149 pModule);
3150 TypeHandle genericTypeTH = LoadTypeDefOrRefThrowing(
3151 pModule,
3152 genericTok,
3153 ClassLoader::ThrowIfNotFound,
3154 ClassLoader::PermitUninstDefOrRef,
3155 tdNoTypes,
3156 CLASS_LOAD_APPROXPARENTS);
3157
3158 // We load interfaces at very approximate types - the generic
3159 // interface itself. We fix this up in LoadInstantiatedInfo.
3160 // This allows us to load recursive interfaces on structs such
3161 // as "struct VC : I<VC>". The details of the interface
3162 // are not currently needed during the first phase
3163 // of setting up the method table.
3164 if (genericTypeTH.IsInterface())
3165 {
3166 RETURN genericTypeTH;
3167 }
3168 else
3169 {
3170 // approxTypes, i.e. approximate reference types by Object, i.e. load the canonical type
3171 RETURN SigPointer(pSig, cSig).GetTypeHandleThrowing(
3172 pModule,
3173 pClassTypeContext,
3174 ClassLoader::LoadTypes,
3175 CLASS_LOAD_APPROXPARENTS,
3176 TRUE /*dropGenericArgumentLevel*/);
3177 }
3178 }
3179 else
3180 {
3181 if (pSigInst != NULL)
3182 *pSigInst = SigPointer();
3183 RETURN LoadTypeDefOrRefThrowing(
3184 pModule,
3185 tok,
3186 ClassLoader::ThrowIfNotFound,
3187 ClassLoader::FailIfUninstDefOrRef,
3188 tdNoTypes,
3189 CLASS_LOAD_APPROXPARENTS);
3190 }
3191} // ClassLoader::LoadApproxTypeThrowing
3192
3193
3194//---------------------------------------------------------------------------------------
3195//
3196//static
3197MethodTable *
3198ClassLoader::LoadApproxParentThrowing(
3199 Module * pModule,
3200 mdToken cl,
3201 SigPointer * pParentInst,
3202 const SigTypeContext * pClassTypeContext)
3203{
3204 CONTRACTL
3205 {
3206 THROWS;
3207 GC_TRIGGERS;
3208 INJECT_FAULT(COMPlusThrowOM());
3209 MODE_ANY;
3210 }
3211 CONTRACTL_END;
3212
3213 mdTypeRef crExtends;
3214 MethodTable * pParentMethodTable = NULL;
3215 TypeHandle parentType;
3216 DWORD dwAttrClass;
3217 Assembly * pAssembly = pModule->GetAssembly();
3218 IMDInternalImport * pInternalImport = pModule->GetMDImport();
3219
3220 // Initialize the return value;
3221 *pParentInst = SigPointer();
3222
3223 // Now load all dependencies of this class
3224 if (FAILED(pInternalImport->GetTypeDefProps(
3225 cl,
3226 &dwAttrClass, // AttrClass
3227 &crExtends)))
3228 {
3229 pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT);
3230 }
3231
3232 if (RidFromToken(crExtends) != mdTokenNil)
3233 {
3234 // Do an "approximate" load of the parent, replacing reference types in the instantiation by Object
3235 // This is to avoid cycles in the loader e.g. on class C : D<C> or class C<T> : D<C<T>>
3236 // We fix up the exact parent later in LoadInstantiatedInfo
3237 parentType = LoadApproxTypeThrowing(pModule, crExtends, pParentInst, pClassTypeContext);
3238
3239 pParentMethodTable = parentType.GetMethodTable();
3240
3241 if (pParentMethodTable == NULL)
3242 pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_PARENTNULL);
3243
3244 // cannot inherit from an interface
3245 if (pParentMethodTable->IsInterface())
3246 pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_PARENTINTERFACE);
3247
3248 if (IsTdInterface(dwAttrClass))
3249 {
3250 // Interfaces must extend from Object
3251 if (! pParentMethodTable->IsObjectClass())
3252 pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_INTERFACEOBJECT);
3253 }
3254 }
3255
3256 return pParentMethodTable;
3257} // ClassLoader::LoadApproxParentThrowing
3258
3259// Perform a single phase of class loading
3260// It is the caller's responsibility to lock
3261/*static*/
3262TypeHandle ClassLoader::DoIncrementalLoad(TypeKey *pTypeKey, TypeHandle typeHnd, ClassLoadLevel currentLevel)
3263{
3264 CONTRACTL
3265 {
3266 STANDARD_VM_CHECK;
3267 PRECONDITION(CheckPointer(pTypeKey));
3268 PRECONDITION(currentLevel >= CLASS_LOAD_BEGIN && currentLevel < CLASS_LOADED);
3269 MODE_ANY;
3270 }
3271 CONTRACTL_END;
3272
3273#ifdef _DEBUG
3274 if (LoggingOn(LF_CLASSLOADER, LL_INFO10000))
3275 {
3276 SString name;
3277 TypeString::AppendTypeKeyDebug(name, pTypeKey);
3278 LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: About to do incremental load of type %S (%p) from level %s\n", name.GetUnicode(), typeHnd.AsPtr(), classLoadLevelName[currentLevel]));
3279 }
3280#endif
3281
3282 // Level is BEGIN if and only if type handle is null
3283 CONSISTENCY_CHECK((currentLevel == CLASS_LOAD_BEGIN) == typeHnd.IsNull());
3284
3285 switch (currentLevel)
3286 {
3287 // Attain at least level CLASS_LOAD_UNRESTORED (if just locating type in ngen image)
3288 // or at least level CLASS_LOAD_APPROXPARENTS (if creating type for the first time)
3289 case CLASS_LOAD_BEGIN :
3290 {
3291 IBCLoggerAwareAllocMemTracker amTracker;
3292 typeHnd = CreateTypeHandleForTypeKey(pTypeKey, &amTracker);
3293 CONSISTENCY_CHECK(!typeHnd.IsNull());
3294 TypeHandle published = PublishType(pTypeKey, typeHnd);
3295 if (published == typeHnd)
3296 amTracker.SuppressRelease();
3297 typeHnd = published;
3298 }
3299 break;
3300
3301 case CLASS_LOAD_UNRESTOREDTYPEKEY :
3302#ifdef FEATURE_PREJIT
3303 typeHnd.DoRestoreTypeKey();
3304#endif
3305 break;
3306
3307 // Attain level CLASS_LOAD_APPROXPARENTS, starting with unrestored class
3308 case CLASS_LOAD_UNRESTORED :
3309#ifdef FEATURE_PREJIT
3310 {
3311 CONSISTENCY_CHECK(!typeHnd.IsRestored_NoLogging());
3312 if (typeHnd.IsTypeDesc())
3313 typeHnd.AsTypeDesc()->Restore();
3314 else
3315 typeHnd.AsMethodTable()->Restore();
3316 }
3317#endif
3318 break;
3319
3320 // Attain level CLASS_LOAD_EXACTPARENTS
3321 case CLASS_LOAD_APPROXPARENTS :
3322 if (!typeHnd.IsTypeDesc())
3323 {
3324 LoadExactParents(typeHnd.AsMethodTable());
3325 }
3326 break;
3327
3328 case CLASS_LOAD_EXACTPARENTS :
3329 case CLASS_DEPENDENCIES_LOADED :
3330 case CLASS_LOADED :
3331 break;
3332
3333 }
3334
3335 if (typeHnd.GetLoadLevel() >= CLASS_LOAD_EXACTPARENTS)
3336 {
3337 Notify(typeHnd);
3338 }
3339
3340 return typeHnd;
3341}
3342
3343/*static*/
3344// For non-canonical instantiations of generic types, create a fresh type by replicating the canonical instantiation
3345// For canonical instantiations of generic types, create a brand new method table
3346// For other constructed types, create a type desc and template method table if necessary
3347// For all other types, create a method table
3348TypeHandle ClassLoader::CreateTypeHandleForTypeKey(TypeKey* pKey, AllocMemTracker* pamTracker)
3349{
3350 CONTRACT(TypeHandle)
3351 {
3352 STANDARD_VM_CHECK;
3353 PRECONDITION(CheckPointer(pKey));
3354
3355 POSTCONDITION(RETVAL.CheckMatchesKey(pKey));
3356 MODE_ANY;
3357 }
3358 CONTRACT_END
3359
3360 TypeHandle typeHnd = TypeHandle();
3361
3362 if (!pKey->IsConstructed())
3363 {
3364 typeHnd = CreateTypeHandleForTypeDefThrowing(pKey->GetModule(),
3365 pKey->GetTypeToken(),
3366 pKey->GetInstantiation(),
3367 pamTracker);
3368 }
3369 else if (pKey->HasInstantiation())
3370 {
3371 if (IsCanonicalGenericInstantiation(pKey->GetInstantiation()))
3372 {
3373 typeHnd = CreateTypeHandleForTypeDefThrowing(pKey->GetModule(),
3374 pKey->GetTypeToken(),
3375 pKey->GetInstantiation(),
3376 pamTracker);
3377 }
3378 else
3379 {
3380 typeHnd = CreateTypeHandleForNonCanonicalGenericInstantiation(pKey,
3381 pamTracker);
3382 }
3383#if defined(_DEBUG) && !defined(CROSSGEN_COMPILE)
3384 if (Nullable::IsNullableType(typeHnd))
3385 Nullable::CheckFieldOffsets(typeHnd);
3386#endif
3387 }
3388 else if (pKey->GetKind() == ELEMENT_TYPE_FNPTR)
3389 {
3390 Module *pLoaderModule = ComputeLoaderModule(pKey);
3391 pLoaderModule->GetLoaderAllocator()->EnsureInstantiation(NULL, Instantiation(pKey->GetRetAndArgTypes(), pKey->GetNumArgs() + 1));
3392
3393 PREFIX_ASSUME(pLoaderModule!=NULL);
3394 DWORD numArgs = pKey->GetNumArgs();
3395 BYTE* mem = (BYTE*) pamTracker->Track(pLoaderModule->GetAssembly()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(FnPtrTypeDesc)) + S_SIZE_T(sizeof(TypeHandle)) * S_SIZE_T(numArgs)));
3396
3397 typeHnd = TypeHandle(new(mem) FnPtrTypeDesc(pKey->GetCallConv(), numArgs, pKey->GetRetAndArgTypes()));
3398 }
3399 else
3400 {
3401 Module *pLoaderModule = ComputeLoaderModule(pKey);
3402 PREFIX_ASSUME(pLoaderModule!=NULL);
3403
3404 CorElementType kind = pKey->GetKind();
3405 TypeHandle paramType = pKey->GetElementType();
3406 MethodTable *templateMT;
3407
3408 // Create a new type descriptor and insert into constructed type table
3409 if (CorTypeInfo::IsArray(kind))
3410 {
3411 DWORD rank = pKey->GetRank();
3412 THROW_BAD_FORMAT_MAYBE((kind != ELEMENT_TYPE_ARRAY) || rank > 0, BFA_MDARRAY_BADRANK, pLoaderModule);
3413 THROW_BAD_FORMAT_MAYBE((kind != ELEMENT_TYPE_SZARRAY) || rank == 1, BFA_SDARRAY_BADRANK, pLoaderModule);
3414
3415 // Arrays of BYREFS not allowed
3416 if (paramType.GetInternalCorElementType() == ELEMENT_TYPE_BYREF)
3417 {
3418 ThrowTypeLoadException(pKey, IDS_CLASSLOAD_BYREFARRAY);
3419 }
3420
3421 // Arrays of ByRefLike types not allowed
3422 MethodTable* pMT = paramType.GetMethodTable();
3423 if (pMT != NULL)
3424 {
3425 if (pMT->IsByRefLike())
3426 {
3427 ThrowTypeLoadException(pKey, IDS_CLASSLOAD_BYREFLIKEARRAY);
3428 }
3429 }
3430
3431 // We really don't need this check anymore.
3432 if (rank > MAX_RANK)
3433 {
3434 ThrowTypeLoadException(pKey, IDS_CLASSLOAD_RANK_TOOLARGE);
3435 }
3436
3437 templateMT = pLoaderModule->CreateArrayMethodTable(paramType, kind, rank, pamTracker);
3438
3439 BYTE* mem = (BYTE*) pamTracker->Track(pLoaderModule->GetAssembly()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(ArrayTypeDesc))));
3440 typeHnd = TypeHandle(new(mem) ArrayTypeDesc(templateMT, paramType));
3441 }
3442 else
3443 {
3444 // no parameterized type allowed on a reference
3445 if (paramType.GetInternalCorElementType() == ELEMENT_TYPE_BYREF ||
3446 paramType.GetInternalCorElementType() == ELEMENT_TYPE_TYPEDBYREF)
3447 {
3448 ThrowTypeLoadException(pKey, IDS_CLASSLOAD_GENERAL);
3449 }
3450
3451 // We do allow parametrized types of ByRefLike types. Languages may restrict them to produce safe or verifiable code,
3452 // but there is not a good reason for restricting them in the runtime.
3453
3454 // let <Type>* type have a method table
3455 // System.UIntPtr's method table is used for types like int*, void *, string * etc.
3456 if (kind == ELEMENT_TYPE_PTR)
3457 templateMT = MscorlibBinder::GetElementType(ELEMENT_TYPE_U);
3458 else
3459 templateMT = NULL;
3460
3461 BYTE* mem = (BYTE*) pamTracker->Track(pLoaderModule->GetAssembly()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(ParamTypeDesc))));
3462 typeHnd = TypeHandle(new(mem) ParamTypeDesc(kind, templateMT, paramType));
3463 }
3464 }
3465
3466 RETURN typeHnd;
3467}
3468
3469// Publish a type (and possibly member information) in the loader's
3470// tables Types are published before they are fully loaded. In
3471// particular, exact parent info (base class and interfaces) is loaded
3472// in a later phase
3473/*static*/
3474TypeHandle ClassLoader::PublishType(TypeKey *pTypeKey, TypeHandle typeHnd)
3475{
3476 CONTRACTL
3477 {
3478 STANDARD_VM_CHECK;
3479 PRECONDITION(CheckPointer(typeHnd));
3480 PRECONDITION(CheckPointer(pTypeKey));
3481
3482 // Key must match that of the handle
3483 PRECONDITION(typeHnd.CheckMatchesKey(pTypeKey));
3484
3485 // Don't publish array template method tables; these are accessed only through type descs
3486 PRECONDITION(typeHnd.IsTypeDesc() || !typeHnd.AsMethodTable()->IsArray());
3487 }
3488 CONTRACTL_END;
3489
3490
3491 if (pTypeKey->IsConstructed())
3492 {
3493 Module *pLoaderModule = ComputeLoaderModule(pTypeKey);
3494 EETypeHashTable *pTable = pLoaderModule->GetAvailableParamTypes();
3495
3496 // m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC
3497 GCX_COOP();
3498
3499 CrstHolder ch(&pLoaderModule->GetClassLoader()->m_AvailableTypesLock);
3500
3501 // The type could have been loaded by a different thread as side-effect of avoiding deadlocks caused by LoadsTypeViolation
3502 TypeHandle existing = pTable->GetValue(pTypeKey);
3503 if (!existing.IsNull())
3504 return existing;
3505
3506 pTable->InsertValue(typeHnd);
3507
3508#ifdef _DEBUG
3509 // Checks to help ensure that the CoreLib in the ngen process does not get contaminated with pointers to the compilation domains.
3510 if (pLoaderModule->IsSystem() && IsCompilationProcess() && pLoaderModule->HasNativeImage())
3511 {
3512 CorElementType kind = pTypeKey->GetKind();
3513 MethodTable *typeHandleMethodTable = typeHnd.GetMethodTable();
3514 if ((typeHandleMethodTable != NULL) && (typeHandleMethodTable->GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator()))
3515 {
3516 _ASSERTE(!"MethodTable of type loaded into CoreLib during NGen is not from CoreLib!");
3517 }
3518 if ((kind != ELEMENT_TYPE_FNPTR) && (kind != ELEMENT_TYPE_VAR) && (kind != ELEMENT_TYPE_MVAR))
3519 {
3520 if ((kind == ELEMENT_TYPE_SZARRAY) || (kind == ELEMENT_TYPE_ARRAY) || (kind == ELEMENT_TYPE_BYREF) || (kind == ELEMENT_TYPE_PTR) || (kind == ELEMENT_TYPE_VALUETYPE))
3521 {
3522 // Check to ensure param value is also part of CoreLib.
3523 if (pTypeKey->GetElementType().GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator())
3524 {
3525 _ASSERTE(!"Param value of type key used to load type during NGEN not located within CoreLib yet type is placed into CoreLib");
3526 }
3527 }
3528 else if (kind == ELEMENT_TYPE_FNPTR)
3529 {
3530 // Check to ensure the parameter types of fnptr are in CoreLib
3531 for (DWORD i = 0; i <= pTypeKey->GetNumArgs(); i++)
3532 {
3533 if (pTypeKey->GetRetAndArgTypes()[i].GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator())
3534 {
3535 _ASSERTE(!"Ret or Arg type of function pointer type key used to load type during NGEN not located within CoreLib yet type is placed into CoreLib");
3536 }
3537 }
3538 }
3539 else if (kind == ELEMENT_TYPE_CLASS)
3540 {
3541 // Check to ensure that the generic parameters are all within CoreLib
3542 for (DWORD i = 0; i < pTypeKey->GetNumGenericArgs(); i++)
3543 {
3544 if (pTypeKey->GetInstantiation()[i].GetLoaderAllocator() != pLoaderModule->GetLoaderAllocator())
3545 {
3546 _ASSERTE(!"Instantiation parameter of generic class type key used to load type during NGEN not located within CoreLib yet type is placed into CoreLib");
3547 }
3548 }
3549 }
3550 else
3551 {
3552 // Should not be able to get here
3553 _ASSERTE(!"Unknown type key type");
3554 }
3555 }
3556 }
3557#endif // DEBUG
3558 }
3559 else
3560 {
3561 Module *pModule = pTypeKey->GetModule();
3562 mdTypeDef typeDef = pTypeKey->GetTypeToken();
3563
3564 // m_AvailableTypesLock has to be taken in cooperative mode to avoid deadlocks during GC
3565 GCX_COOP();
3566
3567 CrstHolder ch(&pModule->GetClassLoader()->m_AvailableTypesLock);
3568
3569 // ! We cannot fail after this point.
3570 CANNOTTHROWCOMPLUSEXCEPTION();
3571 FAULT_FORBID();
3572
3573 // The type could have been loaded by a different thread as side-effect of avoiding deadlocks caused by LoadsTypeViolation
3574 TypeHandle existing = pModule->LookupTypeDef(typeDef);
3575 if (!existing.IsNull())
3576 return existing;
3577
3578 MethodTable *pMT = typeHnd.AsMethodTable();
3579
3580 MethodTable::IntroducedMethodIterator it(pMT);
3581 for (; it.IsValid(); it.Next())
3582 {
3583 MethodDesc * pMD = it.GetMethodDesc();
3584 CONSISTENCY_CHECK(pMD != NULL && pMD->GetMethodTable() == pMT);
3585 if (!pMD->IsUnboxingStub())
3586 {
3587 pModule->EnsuredStoreMethodDef(pMD->GetMemberDef(), pMD);
3588 }
3589 }
3590
3591 ApproxFieldDescIterator fdIterator(pMT, ApproxFieldDescIterator::ALL_FIELDS);
3592 FieldDesc* pFD;
3593
3594 while ((pFD = fdIterator.Next()) != NULL)
3595 {
3596 if (pFD->GetEnclosingMethodTable() == pMT)
3597 {
3598 pModule->EnsuredStoreFieldDef(pFD->GetMemberDef(), pFD);
3599 }
3600 }
3601
3602 // Publish the type last - to ensure that nobody can see it until all the method and field RID maps are filled in
3603 pModule->EnsuredStoreTypeDef(typeDef, typeHnd);
3604 }
3605
3606 return typeHnd;
3607}
3608
3609// Notify profiler and debugger that a type load has completed
3610// Also adjust perf counters
3611/*static*/
3612void ClassLoader::Notify(TypeHandle typeHnd)
3613{
3614 CONTRACTL
3615 {
3616 STANDARD_VM_CHECK;
3617 PRECONDITION(CheckPointer(typeHnd));
3618 }
3619 CONTRACTL_END;
3620
3621 LOG((LF_CLASSLOADER, LL_INFO1000, "Notify: %p %s\n", typeHnd.AsPtr(), typeHnd.IsTypeDesc() ? "typedesc" : typeHnd.AsMethodTable()->GetDebugClassName()));
3622
3623 if (typeHnd.IsTypeDesc())
3624 return;
3625
3626 MethodTable * pMT = typeHnd.AsMethodTable();
3627
3628#ifdef PROFILING_SUPPORTED
3629 {
3630 BEGIN_PIN_PROFILER(CORProfilerTrackClasses());
3631 // We don't tell profilers about typedescs, as per IF above. Also, we don't
3632 // tell profilers about:
3633 if (
3634 // ...generics with unbound variables
3635 (!pMT->ContainsGenericVariables()) &&
3636 // ...or array method tables
3637 // (This check is mainly for NGEN restore, as JITted code won't hit
3638 // this code path for array method tables anyway)
3639 (!pMT->IsArray()))
3640 {
3641 LOG((LF_CLASSLOADER, LL_INFO1000, "Notifying profiler of Started1 %p %s\n", pMT, pMT->GetDebugClassName()));
3642 // Record successful load of the class for the profiler
3643 g_profControlBlock.pProfInterface->ClassLoadStarted(TypeHandleToClassID(typeHnd));
3644
3645 //
3646 // Profiler can turn off TrackClasses during the Started() callback. Need to
3647 // retest the flag here.
3648 //
3649 if (CORProfilerTrackClasses())
3650 {
3651 LOG((LF_CLASSLOADER, LL_INFO1000, "Notifying profiler of Finished1 %p %s\n", pMT, pMT->GetDebugClassName()));
3652 g_profControlBlock.pProfInterface->ClassLoadFinished(TypeHandleToClassID(typeHnd),
3653 S_OK);
3654 }
3655 }
3656 END_PIN_PROFILER();
3657 }
3658#endif //PROFILING_SUPPORTED
3659
3660 g_IBCLogger.LogMethodTableAccess(pMT);
3661
3662 if (pMT->IsTypicalTypeDefinition())
3663 {
3664 LOG((LF_CLASSLOADER, LL_INFO100, "Successfully loaded class %s\n", pMT->GetDebugClassName()));
3665
3666#ifdef DEBUGGING_SUPPORTED
3667 {
3668 Module * pModule = pMT->GetModule();
3669 // Update metadata for dynamic module.
3670 pModule->UpdateDynamicMetadataIfNeeded();
3671 }
3672
3673 if (CORDebuggerAttached())
3674 {
3675 LOG((LF_CORDB, LL_EVERYTHING, "NotifyDebuggerLoad clsload 2239 class %s\n", pMT->GetDebugClassName()));
3676 typeHnd.NotifyDebuggerLoad(NULL, FALSE);
3677 }
3678#endif // DEBUGGING_SUPPORTED
3679
3680#if defined(ENABLE_PERF_COUNTERS)
3681 GetPerfCounters().m_Loading.cClassesLoaded ++;
3682#endif
3683 }
3684}
3685
3686
3687//-----------------------------------------------------------------------------
3688// Common helper for LoadTypeHandleForTypeKey and LoadTypeHandleForTypeKeyNoLock.
3689// Makes the root level call to kick off the transitive closure walk for
3690// the final level pushes.
3691//-----------------------------------------------------------------------------
3692static void PushFinalLevels(TypeHandle typeHnd, ClassLoadLevel targetLevel, const InstantiationContext *pInstContext)
3693{
3694 CONTRACTL
3695 {
3696 STANDARD_VM_CHECK;
3697 LOADS_TYPE(targetLevel);
3698 }
3699 CONTRACTL_END
3700
3701
3702 // This phase brings the type and all its transitive dependencies to their
3703 // final state, sans the IsFullyLoaded bit.
3704 if (targetLevel >= CLASS_DEPENDENCIES_LOADED)
3705 {
3706 BOOL fBailed = FALSE;
3707 typeHnd.DoFullyLoad(NULL, CLASS_DEPENDENCIES_LOADED, NULL, &fBailed, pInstContext);
3708 }
3709
3710 // This phase does access/constraint and other type-safety checks on the type
3711 // and on its transitive dependencies.
3712 if (targetLevel == CLASS_LOADED)
3713 {
3714 DFLPendingList pendingList;
3715 BOOL fBailed = FALSE;
3716
3717 typeHnd.DoFullyLoad(NULL, CLASS_LOADED, &pendingList, &fBailed, pInstContext);
3718
3719
3720 // In the case of a circular dependency, one or more types will have
3721 // had their promotions deferred.
3722 //
3723 // If we got to this point, all checks have successfully passed on
3724 // the transitive closure (otherwise, DoFullyLoad would have thrown.)
3725 //
3726 // So we can go ahead and mark everyone as fully loaded.
3727 //
3728 UINT numTH = pendingList.Count();
3729 TypeHandle *pTHPending = pendingList.Table();
3730 for (UINT i = 0; i < numTH; i++)
3731 {
3732 // NOTE: It is possible for duplicates to appear in this list so
3733 // don't do any operation that isn't idempodent.
3734
3735 pTHPending[i].SetIsFullyLoaded();
3736 }
3737 }
3738}
3739
3740
3741//
3742TypeHandle ClassLoader::LoadTypeHandleForTypeKey(TypeKey *pTypeKey,
3743 TypeHandle typeHnd,
3744 ClassLoadLevel targetLevel/*=CLASS_LOADED*/,
3745 const InstantiationContext *pInstContext/*=NULL*/)
3746{
3747
3748 CONTRACTL
3749 {
3750 INSTANCE_CHECK;
3751 THROWS;
3752 GC_TRIGGERS;
3753 MODE_ANY;
3754 LOADS_TYPE(targetLevel);
3755 }
3756 CONTRACTL_END
3757
3758 GCX_PREEMP();
3759
3760 // Type loading can be recursive. Probe for sufficient stack.
3761 //
3762 // Execution of the FINALLY in LoadTypeHandleForTypeKey_Body can eat
3763 // a lot of stack because LoadTypeHandleForTypeKey_Inner can rethrow
3764 // any non-SO exceptions that it takes, ensure that we have plenty
3765 // of stack before getting into it (>24 pages on AMD64, remember
3766 // that num pages probed is 2*N on AMD64).
3767 INTERIOR_STACK_PROBE_FOR(GetThread(),20);
3768
3769#ifdef _DEBUG
3770 if (LoggingOn(LF_CLASSLOADER, LL_INFO1000))
3771 {
3772 SString name;
3773 TypeString::AppendTypeKeyDebug(name, pTypeKey);
3774 LOG((LF_CLASSLOADER, LL_INFO10000, "PHASEDLOAD: LoadTypeHandleForTypeKey for type %S to level %s\n", name.GetUnicode(), classLoadLevelName[targetLevel]));
3775 CrstHolder unresolvedClassLockHolder(&m_UnresolvedClassLock);
3776 m_pUnresolvedClassHash->Dump();
3777 }
3778#endif
3779
3780 // When using domain neutral assemblies (and not eagerly propagating dependency loads),
3781 // it's possible to get here without having injected the module into the current app domain.
3782 // GetDomainFile will accomplish that.
3783
3784 if (!pTypeKey->IsConstructed())
3785 {
3786 pTypeKey->GetModule()->GetDomainFile();
3787 }
3788
3789 ClassLoadLevel currentLevel = typeHnd.IsNull() ? CLASS_LOAD_BEGIN : typeHnd.GetLoadLevel();
3790 ClassLoadLevel targetLevelUnderLock = targetLevel < CLASS_DEPENDENCIES_LOADED ? targetLevel : (ClassLoadLevel) (CLASS_DEPENDENCIES_LOADED-1);
3791 if (currentLevel < targetLevelUnderLock)
3792 {
3793 typeHnd = LoadTypeHandleForTypeKey_Body(pTypeKey,
3794 typeHnd,
3795 targetLevelUnderLock);
3796 _ASSERTE(!typeHnd.IsNull());
3797 }
3798 _ASSERTE(typeHnd.GetLoadLevel() >= targetLevelUnderLock);
3799
3800 PushFinalLevels(typeHnd, targetLevel, pInstContext);
3801
3802 END_INTERIOR_STACK_PROBE;
3803
3804 return typeHnd;
3805}
3806
3807//
3808TypeHandle ClassLoader::LoadTypeHandleForTypeKeyNoLock(TypeKey *pTypeKey,
3809 ClassLoadLevel targetLevel/*=CLASS_LOADED*/,
3810 const InstantiationContext *pInstContext/*=NULL*/)
3811{
3812
3813 CONTRACTL
3814 {
3815 INSTANCE_CHECK;
3816 THROWS;
3817 GC_TRIGGERS;
3818 MODE_ANY;
3819 LOADS_TYPE(targetLevel);
3820 PRECONDITION(CheckPointer(pTypeKey));
3821 PRECONDITION(targetLevel >= 0 && targetLevel <= CLASS_LOADED);
3822 }
3823 CONTRACTL_END
3824
3825 GCX_PREEMP();
3826
3827 TypeHandle typeHnd = TypeHandle();
3828
3829 // Type loading can be recursive. Probe for sufficient stack.
3830 INTERIOR_STACK_PROBE_FOR(GetThread(),8);
3831
3832 ClassLoadLevel currentLevel = CLASS_LOAD_BEGIN;
3833 ClassLoadLevel targetLevelUnderLock = targetLevel < CLASS_DEPENDENCIES_LOADED ? targetLevel : (ClassLoadLevel) (CLASS_DEPENDENCIES_LOADED-1);
3834 while (currentLevel < targetLevelUnderLock)
3835 {
3836 typeHnd = DoIncrementalLoad(pTypeKey, typeHnd, currentLevel);
3837 CONSISTENCY_CHECK(typeHnd.GetLoadLevel() > currentLevel);
3838 currentLevel = typeHnd.GetLoadLevel();
3839 }
3840
3841 PushFinalLevels(typeHnd, targetLevel, pInstContext);
3842
3843 END_INTERIOR_STACK_PROBE;
3844
3845 return typeHnd;
3846}
3847
3848//---------------------------------------------------------------------------------------
3849//
3850class PendingTypeLoadHolder
3851{
3852 Thread * m_pThread;
3853 PendingTypeLoadEntry * m_pEntry;
3854 PendingTypeLoadHolder * m_pPrevious;
3855
3856public:
3857 PendingTypeLoadHolder(PendingTypeLoadEntry * pEntry)
3858 {
3859 LIMITED_METHOD_CONTRACT;
3860
3861 m_pThread = GetThread();
3862 m_pEntry = pEntry;
3863
3864 m_pPrevious = m_pThread->GetPendingTypeLoad();
3865 m_pThread->SetPendingTypeLoad(this);
3866 }
3867
3868 ~PendingTypeLoadHolder()
3869 {
3870 LIMITED_METHOD_CONTRACT;
3871
3872 _ASSERTE(m_pThread->GetPendingTypeLoad() == this);
3873 m_pThread->SetPendingTypeLoad(m_pPrevious);
3874 }
3875
3876 static bool CheckForDeadLockOnCurrentThread(PendingTypeLoadEntry * pEntry)
3877 {
3878 LIMITED_METHOD_CONTRACT;
3879
3880 PendingTypeLoadHolder * pCurrent = GetThread()->GetPendingTypeLoad();
3881
3882 while (pCurrent != NULL)
3883 {
3884 if (pCurrent->m_pEntry == pEntry)
3885 return true;
3886
3887 pCurrent = pCurrent->m_pPrevious;
3888 }
3889
3890 return false;
3891 }
3892};
3893
3894//---------------------------------------------------------------------------------------
3895//
3896TypeHandle
3897ClassLoader::LoadTypeHandleForTypeKey_Body(
3898 TypeKey * pTypeKey,
3899 TypeHandle typeHnd,
3900 ClassLoadLevel targetLevel)
3901{
3902 CONTRACT(TypeHandle)
3903 {
3904 STANDARD_VM_CHECK;
3905 POSTCONDITION(!typeHnd.IsNull() && typeHnd.GetLoadLevel() >= targetLevel);
3906 }
3907 CONTRACT_END
3908
3909 if (!pTypeKey->IsConstructed())
3910 {
3911 Module *pModule = pTypeKey->GetModule();
3912 mdTypeDef cl = pTypeKey->GetTypeToken();
3913
3914 STRESS_LOG2(LF_CLASSLOADER, LL_INFO100000, "LoadTypeHandle: Loading Class from Module %p token %x\n", pModule, cl);
3915
3916#ifdef _DEBUG
3917 IMDInternalImport* pInternalImport = pModule->GetMDImport();
3918 LPCUTF8 className;
3919 LPCUTF8 nameSpace;
3920 if (FAILED(pInternalImport->GetNameOfTypeDef(cl, &className, &nameSpace)))
3921 {
3922 className = nameSpace = "Invalid TypeDef record";
3923 }
3924 if (g_pConfig->ShouldBreakOnClassLoad(className))
3925 CONSISTENCY_CHECK_MSGF(false, ("BreakOnClassLoad: typename '%s' ", className));
3926#endif
3927 }
3928
3929 ReleaseHolder<PendingTypeLoadEntry> pLoadingEntry;
3930 CrstHolderWithState unresolvedClassLockHolder(&m_UnresolvedClassLock, false);
3931
3932retry:
3933 unresolvedClassLockHolder.Acquire();
3934
3935 // Is it in the hash of classes currently being loaded?
3936 pLoadingEntry = m_pUnresolvedClassHash->GetValue(pTypeKey);
3937 if (pLoadingEntry)
3938 {
3939 pLoadingEntry->AddRef();
3940
3941 // It is in the hash, which means that another thread is waiting for it (or that we are
3942 // already loading this class on this thread, which should never happen, since that implies
3943 // a recursive dependency).
3944 unresolvedClassLockHolder.Release();
3945
3946 //
3947 // Check one last time before waiting that the type handle is not sufficiently loaded to
3948 // prevent deadlocks
3949 //
3950 {
3951 if (typeHnd.IsNull())
3952 {
3953 typeHnd = LookupTypeHandleForTypeKey(pTypeKey);
3954 }
3955
3956 if (!typeHnd.IsNull())
3957 {
3958 if (typeHnd.GetLoadLevel() >= targetLevel)
3959 RETURN typeHnd;
3960 }
3961 }
3962
3963 if (PendingTypeLoadHolder::CheckForDeadLockOnCurrentThread(pLoadingEntry))
3964 {
3965 // Attempting recursive load
3966 ClassLoader::ThrowTypeLoadException(pTypeKey, IDS_CLASSLOAD_GENERAL);
3967 }
3968
3969 //
3970 // Violation of type loadlevel ordering rules depends on type load failing in case of cyclic dependency that would
3971 // otherwise lead to deadlock. We will speculatively proceed with the type load to make it fail in the right spot,
3972 // in backward compatible way. In case the type load succeeds, we will only let one type win in PublishType.
3973 //
3974 if (typeHnd.IsNull() && GetThread()->HasThreadStateNC(Thread::TSNC_LoadsTypeViolation))
3975 {
3976 PendingTypeLoadHolder ptlh(pLoadingEntry);
3977 typeHnd = DoIncrementalLoad(pTypeKey, TypeHandle(), CLASS_LOAD_BEGIN);
3978 goto retry;
3979 }
3980
3981 {
3982 // Wait for class to be loaded by another thread. This is where we start tracking the
3983 // entry, so there is an implicit Acquire in our use of Assign here.
3984 CrstHolder loadingEntryLockHolder(&pLoadingEntry->m_Crst);
3985 _ASSERTE(pLoadingEntry->HasLock());
3986 }
3987
3988 // Result of other thread loading the class
3989 HRESULT hr = pLoadingEntry->m_hrResult;
3990
3991 if (FAILED(hr)) {
3992
3993 //
3994 // Redo the lookup one more time and return a valid type if possible. The other thread could
3995 // have hit error while loading the type to higher level than we need.
3996 //
3997 {
3998 if (typeHnd.IsNull())
3999 {
4000 typeHnd = LookupTypeHandleForTypeKey(pTypeKey);
4001 }
4002
4003 if (!typeHnd.IsNull())
4004 {
4005 if (typeHnd.GetLoadLevel() >= targetLevel)
4006 RETURN typeHnd;
4007 }
4008 }
4009
4010 if (hr == E_ABORT) {
4011 LOG((LF_CLASSLOADER, LL_INFO10, "need to retry LoadTypeHandle: %x\n", hr));
4012 goto retry;
4013 }
4014
4015 LOG((LF_CLASSLOADER, LL_INFO10, "Failed to load in other entry: %x\n", hr));
4016
4017 if (hr == E_OUTOFMEMORY) {
4018 COMPlusThrowOM();
4019 }
4020
4021 pLoadingEntry->ThrowException();
4022 }
4023
4024 // Get a pointer to the EEClass being loaded
4025 typeHnd = pLoadingEntry->m_typeHandle;
4026
4027 if (!typeHnd.IsNull())
4028 {
4029 // If the type load on the other thread loaded the type to the needed level, return it here.
4030 if (typeHnd.GetLoadLevel() >= targetLevel)
4031 RETURN typeHnd;
4032 }
4033
4034 // The type load on the other thread did not load the type "enough". Begin the type load
4035 // process again to cause us to load to the needed level.
4036 goto retry;
4037 }
4038
4039 if (typeHnd.IsNull())
4040 {
4041 // The class was not being loaded. However, it may have already been loaded after our
4042 // first LoadTypeHandleThrowIfFailed() and before taking the lock.
4043 typeHnd = LookupTypeHandleForTypeKey(pTypeKey);
4044 }
4045
4046 ClassLoadLevel currentLevel = CLASS_LOAD_BEGIN;
4047 if (!typeHnd.IsNull())
4048 {
4049 currentLevel = typeHnd.GetLoadLevel();
4050 if (currentLevel >= targetLevel)
4051 RETURN typeHnd;
4052 }
4053
4054 // It was not loaded, and it is not being loaded, so we must load it. Create a new LoadingEntry
4055 // and acquire it immediately so that other threads will block.
4056 pLoadingEntry = new PendingTypeLoadEntry(*pTypeKey, typeHnd); // this atomically creates a crst and acquires it
4057
4058 if (!(m_pUnresolvedClassHash->InsertValue(pLoadingEntry)))
4059 {
4060 COMPlusThrowOM();
4061 }
4062
4063 // Leave the global lock, so that other threads may now start waiting on our class's lock
4064 unresolvedClassLockHolder.Release();
4065
4066 EX_TRY
4067 {
4068 PendingTypeLoadHolder ptlh(pLoadingEntry);
4069
4070 TRIGGERS_TYPELOAD();
4071
4072 while (currentLevel < targetLevel)
4073 {
4074 typeHnd = DoIncrementalLoad(pTypeKey, typeHnd, currentLevel);
4075 CONSISTENCY_CHECK(typeHnd.GetLoadLevel() > currentLevel);
4076 currentLevel = typeHnd.GetLoadLevel();
4077
4078 // If other threads are waiting for this load, unblock them as soon as possible to prevent deadlocks.
4079 if (pLoadingEntry->HasWaiters())
4080 break;
4081 }
4082
4083 _ASSERTE(!typeHnd.IsNull());
4084 pLoadingEntry->SetResult(typeHnd);
4085 }
4086 EX_HOOK
4087 {
4088 LOG((LF_CLASSLOADER, LL_INFO10, "Caught an exception loading: %x, %0x (Module)\n", pTypeKey->GetTypeToken(), pTypeKey->GetModule()));
4089
4090 if (!GetThread()->HasThreadStateNC(Thread::TSNC_LoadsTypeViolation))
4091 {
4092 // Fix up the loading entry.
4093 Exception *pException = GET_EXCEPTION();
4094 pLoadingEntry->SetException(pException);
4095 }
4096
4097 // Unlink this class from the unresolved class list.
4098 unresolvedClassLockHolder.Acquire();
4099 m_pUnresolvedClassHash->DeleteValue(pTypeKey);
4100
4101 // Release the lock before proceeding. The unhandled exception filters take number of locks that
4102 // have ordering violations with this lock.
4103 unresolvedClassLockHolder.Release();
4104
4105 // Unblock any thread waiting to load same type as in TypeLoadEntry
4106 pLoadingEntry->UnblockWaiters();
4107 }
4108 EX_END_HOOK;
4109
4110 // Unlink this class from the unresolved class list.
4111 unresolvedClassLockHolder.Acquire();
4112 m_pUnresolvedClassHash->DeleteValue(pTypeKey);
4113 unresolvedClassLockHolder.Release();
4114
4115 // Unblock any thread waiting to load same type as in TypeLoadEntry. This should be done
4116 // after pLoadingEntry is removed from m_pUnresolvedClassHash. Otherwise the other thread
4117 // (which was waiting) will keep spinning for a while after waking up, till the current thread removes
4118 // pLoadingEntry from m_pUnresolvedClassHash. This can cause hang in situation when the current
4119 // thread is a background thread and so will get very less processor cycle to perform subsequent
4120 // operations to remove the entry from hash later.
4121 pLoadingEntry->UnblockWaiters();
4122
4123 if (currentLevel < targetLevel)
4124 goto retry;
4125
4126 RETURN typeHnd;
4127} // ClassLoader::LoadTypeHandleForTypeKey_Body
4128
4129#endif //!DACCESS_COMPILE
4130
4131//---------------------------------------------------------------------------------------
4132//
4133//static
4134TypeHandle
4135ClassLoader::LoadArrayTypeThrowing(
4136 TypeHandle elemType,
4137 CorElementType arrayKind,
4138 unsigned rank, //=0
4139 LoadTypesFlag fLoadTypes, //=LoadTypes
4140 ClassLoadLevel level)
4141{
4142 CONTRACT(TypeHandle)
4143 {
4144 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
4145 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
4146 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
4147 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
4148 if (fLoadTypes == DontLoadTypes) SO_TOLERANT; else SO_INTOLERANT;
4149 MODE_ANY;
4150 SUPPORTS_DAC;
4151 POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
4152 }
4153 CONTRACT_END
4154
4155 CorElementType predefinedElementType = ELEMENT_TYPE_END;
4156
4157 // Try finding it in our cache of primitive SD arrays
4158 if (arrayKind == ELEMENT_TYPE_SZARRAY) {
4159 predefinedElementType = elemType.GetSignatureCorElementType();
4160 if (predefinedElementType <= ELEMENT_TYPE_R8) {
4161 ArrayTypeDesc* typeDesc = g_pPredefinedArrayTypes[predefinedElementType];
4162 if (typeDesc != 0)
4163 RETURN(TypeHandle(typeDesc));
4164 }
4165 // This call to AsPtr is somewhat bogus and only used
4166 // as an optimization. If the TypeHandle is really a TypeDesc
4167 // then the equality checks for the optimizations below will
4168 // fail. Thus ArrayMT should not be used elsewhere in this function
4169 else if (elemType.AsPtr() == PTR_VOID(g_pObjectClass)) {
4170 // Code duplicated because Object[]'s SigCorElementType is E_T_CLASS, not OBJECT
4171 ArrayTypeDesc* typeDesc = g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT];
4172 if (typeDesc != 0)
4173 RETURN(TypeHandle(typeDesc));
4174 predefinedElementType = ELEMENT_TYPE_OBJECT;
4175 }
4176 else if (elemType.AsPtr() == PTR_VOID(g_pStringClass)) {
4177 // Code duplicated because String[]'s SigCorElementType is E_T_CLASS, not STRING
4178 ArrayTypeDesc* typeDesc = g_pPredefinedArrayTypes[ELEMENT_TYPE_STRING];
4179 if (typeDesc != 0)
4180 RETURN(TypeHandle(typeDesc));
4181 predefinedElementType = ELEMENT_TYPE_STRING;
4182 }
4183 else {
4184 predefinedElementType = ELEMENT_TYPE_END;
4185 }
4186 rank = 1;
4187 }
4188
4189#ifndef DACCESS_COMPILE
4190 // To avoid loading useless shared instantiations, normalize shared instantiations to the canonical form
4191 // (e.g. List<_Canon>[] -> _Canon[])
4192 // The denormalized shared instantiations should be needed only during JITing, so it is fine to skip this
4193 // for DACCESS_COMPILE.
4194 if (elemType.IsCanonicalSubtype())
4195 {
4196 elemType = ClassLoader::CanonicalizeGenericArg(elemType);
4197 }
4198#endif
4199
4200 TypeKey key(arrayKind, elemType, FALSE, rank);
4201 TypeHandle th = LoadConstructedTypeThrowing(&key, fLoadTypes, level);
4202
4203 if (predefinedElementType != ELEMENT_TYPE_END && !th.IsNull() && th.IsFullyLoaded())
4204 {
4205 g_pPredefinedArrayTypes[predefinedElementType] = th.AsArray();
4206 }
4207
4208 RETURN(th);
4209} // ClassLoader::LoadArrayTypeThrowing
4210
4211#ifndef DACCESS_COMPILE
4212
4213VOID ClassLoader::AddAvailableClassDontHaveLock(Module *pModule,
4214 mdTypeDef classdef,
4215 AllocMemTracker *pamTracker)
4216{
4217 CONTRACTL
4218 {
4219 INSTANCE_CHECK;
4220 THROWS;
4221 GC_TRIGGERS;
4222 MODE_ANY;
4223 INJECT_FAULT(COMPlusThrowOM(););
4224 }
4225 CONTRACTL_END
4226
4227#ifdef FEATURE_COMINTEROP
4228 _ASSERTE(!pModule->GetAssembly()->IsWinMD()); // WinMD files should never get into this path, otherwise provide szWinRtNamespacePrefix
4229#endif
4230
4231 CrstHolder ch(&m_AvailableClassLock);
4232 AddAvailableClassHaveLock(
4233 pModule,
4234 classdef,
4235 pamTracker,
4236 NULL, // szWinRtNamespacePrefix
4237 0); // cchWinRtNamespacePrefix
4238}
4239
4240// This routine must be single threaded! The reason is that there are situations which allow
4241// the same class name to have two different mdTypeDef tokens (for example, we load two different DLLs
4242// simultaneously, and they have some common class files, or we convert the same class file
4243// simultaneously on two threads). The problem is that we do not want to overwrite the old
4244// <classname> -> pModule mapping with the new one, because this may cause identity problems.
4245//
4246// This routine assumes you already have the lock. Use AddAvailableClassDontHaveLock() if you
4247// don't have it.
4248//
4249// Also validates that TypeDef namespace begins with szWinRTNamespacePrefix (if it is not NULL).
4250// The prefix should be NULL for normal non-WinRT .NET assemblies.
4251//
4252VOID ClassLoader::AddAvailableClassHaveLock(
4253 Module * pModule,
4254 mdTypeDef classdef,
4255 AllocMemTracker * pamTracker,
4256 LPCSTR szWinRtNamespacePrefix,
4257 DWORD cchWinRtNamespacePrefix) // Optimization for faster prefix comparison implementation
4258{
4259 CONTRACTL
4260 {
4261 INSTANCE_CHECK;
4262 THROWS;
4263 GC_TRIGGERS;
4264 MODE_ANY;
4265 INJECT_FAULT(COMPlusThrowOM(););
4266 }
4267 CONTRACTL_END
4268
4269 EEClassHashTable *pClassHash = pModule->GetAvailableClassHash();
4270 EEClassHashTable *pClassCaseInsHash = pModule->GetAvailableClassCaseInsHash();
4271
4272 LPCUTF8 pszName;
4273 LPCUTF8 pszNameSpace;
4274 HashDatum ThrowawayData;
4275 IMDInternalImport *pMDImport = pModule->GetMDImport();
4276 if (FAILED(pMDImport->GetNameOfTypeDef(classdef, &pszName, &pszNameSpace)))
4277 {
4278 pszName = pszNameSpace = "Invalid TypeDef token";
4279 pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
4280 }
4281
4282 EEClassHashEntry_t *pBucket;
4283 mdTypeDef enclosing;
4284 if (SUCCEEDED(pMDImport->GetNestedClassProps(classdef, &enclosing))) {
4285 // nested type
4286
4287 LPCUTF8 pszEnclosingName;
4288 LPCUTF8 pszEnclosingNameSpace;
4289 mdTypeDef enclEnclosing;
4290
4291 // Find this type's encloser's entry in the available table.
4292 // We'll save a pointer to it in the new hash entry for this type.
4293 BOOL fNestedEncl = SUCCEEDED(pMDImport->GetNestedClassProps(enclosing, &enclEnclosing));
4294
4295 EEClassHashTable::LookupContext sContext;
4296 if (FAILED(pMDImport->GetNameOfTypeDef(enclosing, &pszEnclosingName, &pszEnclosingNameSpace)))
4297 {
4298 pszName = pszNameSpace = "Invalid TypeDef token";
4299 pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
4300 }
4301 if ((pBucket = pClassHash->GetValue(pszEnclosingNameSpace,
4302 pszEnclosingName,
4303 &ThrowawayData,
4304 fNestedEncl,
4305 &sContext)) != NULL) {
4306 if (fNestedEncl) {
4307 // Find entry for enclosing class - NOTE, this assumes that the
4308 // enclosing class's TypeDef or ExportedType was inserted previously,
4309 // which assumes that, when enuming TD's, we get the enclosing class first
4310 while ((!CompareNestedEntryWithTypeDef(pMDImport,
4311 enclEnclosing,
4312 pClassHash,
4313 pBucket->GetEncloser())) &&
4314 (pBucket = pClassHash->FindNextNestedClass(pszEnclosingNameSpace,
4315 pszEnclosingName,
4316 &ThrowawayData,
4317 &sContext)) != NULL);
4318 }
4319
4320 if (!pBucket) // Enclosing type not found in hash table
4321 pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_ENCLOSING_TYPE_NOT_FOUND);
4322
4323 // In this hash table, if the lower bit is set, it means a Module, otherwise it means EEClass*
4324 ThrowawayData = EEClassHashTable::CompressClassDef(classdef);
4325 InsertValue(pClassHash, pClassCaseInsHash, pszNameSpace, pszName, ThrowawayData, pBucket, pamTracker);
4326 }
4327 }
4328 else {
4329 // Don't add duplicate top-level classes. Top-level classes are
4330 // added to the beginning of the bucket, while nested classes are
4331 // added to the end. So, a duplicate top-level class could hide
4332 // the previous type's EEClass* entry in the hash table.
4333 EEClassHashEntry_t *pCaseInsEntry = NULL;
4334 LPUTF8 pszLowerCaseNS = NULL;
4335 LPUTF8 pszLowerCaseName = NULL;
4336
4337 if (pClassCaseInsHash) {
4338 CreateCanonicallyCasedKey(pszNameSpace, pszName, &pszLowerCaseNS, &pszLowerCaseName);
4339 pCaseInsEntry = pClassCaseInsHash->AllocNewEntry(pamTracker);
4340 }
4341
4342 EEClassHashEntry_t *pEntry = pClassHash->FindItem(pszNameSpace, pszName, FALSE, NULL);
4343 if (pEntry) {
4344 HashDatum Data = pEntry->GetData();
4345
4346 if (((size_t)Data & EECLASSHASH_TYPEHANDLE_DISCR) &&
4347 ((size_t)Data & EECLASSHASH_MDEXPORT_DISCR)) {
4348
4349 // it's an ExportedType - check the 'already seen' bit and if on, report a class loading exception
4350 // otherwise, set it
4351 if ((size_t)Data & EECLASSHASH_ALREADYSEEN)
4352 pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
4353 else {
4354 Data = (HashDatum)((size_t)Data | EECLASSHASH_ALREADYSEEN);
4355 pEntry->SetData(Data);
4356 }
4357 }
4358 else {
4359 // We want to throw an exception for a duplicate typedef.
4360 // However, this used to be allowed in 1.0/1.1, and some third-party DLLs have
4361 // been obfuscated so that they have duplicate private typedefs.
4362 // We must allow this for old assemblies for app compat reasons
4363 pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
4364 }
4365 }
4366 else {
4367 pEntry = pClassHash->AllocNewEntry(pamTracker);
4368
4369 CANNOTTHROWCOMPLUSEXCEPTION();
4370 FAULT_FORBID();
4371
4372 pClassHash->InsertValueUsingPreallocatedEntry(pEntry, pszNameSpace, pszName, EEClassHashTable::CompressClassDef(classdef), NULL);
4373
4374 if (pClassCaseInsHash)
4375 pClassCaseInsHash->InsertValueUsingPreallocatedEntry(pCaseInsEntry, pszLowerCaseNS, pszLowerCaseName, pEntry, pEntry->GetEncloser());
4376 }
4377
4378#ifdef FEATURE_COMINTEROP
4379 // Check WinRT namespace prefix if required
4380 if (szWinRtNamespacePrefix != NULL)
4381 {
4382 DWORD dwAttr;
4383 if (FAILED(pMDImport->GetTypeDefProps(classdef, &dwAttr, NULL)))
4384 {
4385 pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
4386 }
4387
4388 // Check only public WinRT types that are not nested (i.e. only types available for binding, excluding NoPIA)
4389 if (IsTdPublic(dwAttr) && IsTdWindowsRuntime(dwAttr))
4390 {
4391 // Guaranteed by the caller - code:ClassLoader::PopulateAvailableClassHashTable
4392 _ASSERTE(cchWinRtNamespacePrefix == strlen(szWinRtNamespacePrefix));
4393
4394 // Now make sure namespace is, or begins with the namespace-prefix (note: 'MyN' should not match namespace 'MyName')
4395 // Note: Case insensitive comparison function has to be in sync with Win8 implementation
4396 // (ExtractExactCaseNamespaceSegmentFromMetadataFile in com\WinRT\WinTypes\TypeResolution\NamespaceResolution.cpp)
4397 BOOL fIsNamespaceSubstring = (pszNameSpace != NULL) &&
4398 ((strncmp(pszNameSpace, szWinRtNamespacePrefix, cchWinRtNamespacePrefix) == 0) ||
4399 (_strnicmp(pszNameSpace, szWinRtNamespacePrefix, cchWinRtNamespacePrefix) == 0));
4400 BOOL fIsSubNamespace = fIsNamespaceSubstring &&
4401 ((pszNameSpace[cchWinRtNamespacePrefix] == '\0') ||
4402 (pszNameSpace[cchWinRtNamespacePrefix] == '.'));
4403 if (!fIsSubNamespace)
4404 {
4405 pModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_WINRT_INVALID_NAMESPACE_FOR_TYPE);
4406 }
4407 }
4408 }
4409#endif // FEATURE_COMINTEROP
4410 }
4411}
4412
4413VOID ClassLoader::AddExportedTypeDontHaveLock(Module *pManifestModule,
4414 mdExportedType cl,
4415 AllocMemTracker *pamTracker)
4416{
4417 CONTRACTL
4418 {
4419 INSTANCE_CHECK;
4420 THROWS;
4421 GC_TRIGGERS;
4422 MODE_ANY;
4423 INJECT_FAULT(COMPlusThrowOM(););
4424 }
4425 CONTRACTL_END
4426
4427 CrstHolder ch(&m_AvailableClassLock);
4428 AddExportedTypeHaveLock(
4429 pManifestModule,
4430 cl,
4431 pamTracker);
4432}
4433
4434VOID ClassLoader::AddExportedTypeHaveLock(Module *pManifestModule,
4435 mdExportedType cl,
4436 AllocMemTracker *pamTracker)
4437{
4438 CONTRACTL
4439 {
4440 INSTANCE_CHECK;
4441 THROWS;
4442 GC_TRIGGERS;
4443 MODE_ANY;
4444 INJECT_FAULT(COMPlusThrowOM(););
4445 }
4446 CONTRACTL_END
4447
4448
4449 mdToken mdImpl;
4450 LPCSTR pszName;
4451 LPCSTR pszNameSpace;
4452 IMDInternalImport* pAsmImport = pManifestModule->GetMDImport();
4453 if (FAILED(pAsmImport->GetExportedTypeProps(
4454 cl,
4455 &pszNameSpace,
4456 &pszName,
4457 &mdImpl,
4458 NULL, // type def
4459 NULL))) // flags
4460 {
4461 pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
4462 }
4463
4464 HashDatum ThrowawayData;
4465
4466 if (TypeFromToken(mdImpl) == mdtExportedType)
4467 {
4468 // nested class
4469 LPCUTF8 pszEnclosingNameSpace;
4470 LPCUTF8 pszEnclosingName;
4471 mdToken nextImpl;
4472 if (FAILED(pAsmImport->GetExportedTypeProps(
4473 mdImpl,
4474 &pszEnclosingNameSpace,
4475 &pszEnclosingName,
4476 &nextImpl,
4477 NULL, // type def
4478 NULL))) // flags
4479 {
4480 pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
4481 }
4482
4483 // Find entry for enclosing class - NOTE, this assumes that the
4484 // enclosing class's ExportedType was inserted previously, which assumes that,
4485 // when enuming ExportedTypes, we get the enclosing class first
4486 EEClassHashEntry_t *pBucket;
4487 EEClassHashTable::LookupContext sContext;
4488 if ((pBucket = pManifestModule->GetAvailableClassHash()->GetValue(pszEnclosingNameSpace,
4489 pszEnclosingName,
4490 &ThrowawayData,
4491 TypeFromToken(nextImpl) == mdtExportedType,
4492 &sContext)) != NULL) {
4493 do {
4494 // check to see if this is the correct class
4495 if (EEClassHashTable::UncompressModuleAndClassDef(ThrowawayData) == mdImpl) {
4496 ThrowawayData = EEClassHashTable::CompressClassDef(cl);
4497
4498 // we explicitly don't check for the case insensitive hash table because we know it can't have been created yet
4499 pManifestModule->GetAvailableClassHash()->InsertValue(pszNameSpace, pszName, ThrowawayData, pBucket, pamTracker);
4500 }
4501 pBucket = pManifestModule->GetAvailableClassHash()->FindNextNestedClass(pszEnclosingNameSpace, pszEnclosingName, &ThrowawayData, &sContext);
4502 } while (pBucket);
4503 }
4504
4505 // If the encloser is not in the hash table, this nested class
4506 // was defined in the manifest module, so it doesn't need to be added
4507 return;
4508 }
4509 else {
4510 // Defined in the manifest module - add to the hash table by TypeDef instead
4511 if (mdImpl == mdFileNil)
4512 return;
4513
4514 // Don't add duplicate top-level classes
4515 // In this hash table, if the lower bit is set, it means a Module, otherwise it means EEClass*
4516 ThrowawayData = EEClassHashTable::CompressClassDef(cl);
4517 // ThrowawayData is an IN OUT param. Going in its the pointer to the new value if the entry needs
4518 // to be inserted. The OUT param points to the value stored in the hash table.
4519 BOOL bFound;
4520 pManifestModule->GetAvailableClassHash()->InsertValueIfNotFound(pszNameSpace, pszName, &ThrowawayData, NULL, FALSE, &bFound, pamTracker);
4521 if (bFound) {
4522
4523 // Check for duplicate ExportedTypes
4524 // Let it slide if it's pointing to the same type
4525 mdToken foundTypeImpl;
4526 if ((size_t)ThrowawayData & EECLASSHASH_MDEXPORT_DISCR)
4527 {
4528 mdExportedType foundExportedType = EEClassHashTable::UncompressModuleAndClassDef(ThrowawayData);
4529 if (FAILED(pAsmImport->GetExportedTypeProps(
4530 foundExportedType,
4531 NULL, // namespace
4532 NULL, // name
4533 &foundTypeImpl,
4534 NULL, // TypeDef
4535 NULL))) // flags
4536 {
4537 pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_INVALID_TOKEN);
4538 }
4539 }
4540 else
4541 {
4542 foundTypeImpl = mdFileNil;
4543 }
4544
4545 if (mdImpl != foundTypeImpl)
4546 {
4547 pManifestModule->GetAssembly()->ThrowBadImageException(pszNameSpace, pszName, BFA_MULT_TYPE_SAME_NAME);
4548 }
4549 }
4550 }
4551}
4552
4553static MethodTable* GetEnclosingMethodTable(MethodTable *pMT)
4554{
4555 CONTRACT(MethodTable*)
4556 {
4557 THROWS;
4558 GC_TRIGGERS;
4559 INJECT_FAULT(COMPlusThrowOM(););
4560 MODE_ANY;
4561 PRECONDITION(CheckPointer(pMT));
4562 POSTCONDITION(RETVAL == NULL || RETVAL->IsTypicalTypeDefinition());
4563 }
4564 CONTRACT_END;
4565
4566 RETURN pMT->LoadEnclosingMethodTable();
4567}
4568
4569StaticAccessCheckContext::StaticAccessCheckContext(MethodDesc* pCallerMethod)
4570{
4571 CONTRACTL
4572 {
4573 LIMITED_METHOD_CONTRACT;
4574 PRECONDITION(CheckPointer(pCallerMethod));
4575 }
4576 CONTRACTL_END;
4577
4578 m_pCallerMethod = pCallerMethod;
4579 m_pCallerMT = m_pCallerMethod->GetMethodTable();
4580 m_pCallerAssembly = m_pCallerMT->GetAssembly();
4581}
4582
4583StaticAccessCheckContext::StaticAccessCheckContext(MethodDesc* pCallerMethod, MethodTable* pCallerType)
4584{
4585 CONTRACTL
4586 {
4587 LIMITED_METHOD_CONTRACT;
4588 PRECONDITION(CheckPointer(pCallerMethod, NULL_OK));
4589 PRECONDITION(CheckPointer(pCallerType));
4590 }
4591 CONTRACTL_END;
4592
4593 m_pCallerMethod = pCallerMethod;
4594 m_pCallerMT = pCallerType;
4595 m_pCallerAssembly = pCallerType->GetAssembly();
4596}
4597
4598//******************************************************************************
4599
4600// static
4601AccessCheckOptions* AccessCheckOptions::s_pNormalAccessChecks;
4602
4603//******************************************************************************
4604
4605void AccessCheckOptions::Startup()
4606{
4607 STANDARD_VM_CONTRACT;
4608
4609 s_pNormalAccessChecks = new AccessCheckOptions(
4610 AccessCheckOptions::kNormalAccessibilityChecks,
4611 NULL,
4612 FALSE,
4613 (MethodTable *)NULL);
4614}
4615
4616//******************************************************************************
4617AccessCheckOptions::AccessCheckOptions(
4618 const AccessCheckOptions & templateOptions,
4619 BOOL throwIfTargetIsInaccessible) :
4620 m_pAccessContext(templateOptions.m_pAccessContext)
4621{
4622 WRAPPER_NO_CONTRACT;
4623
4624 Initialize(
4625 templateOptions.m_accessCheckType,
4626 throwIfTargetIsInaccessible,
4627 templateOptions.m_pTargetMT,
4628 templateOptions.m_pTargetMethod,
4629 templateOptions.m_pTargetField);
4630}
4631
4632//******************************************************************************
4633// This function should only be called when normal accessibility is not possible.
4634// It returns TRUE if the target can be accessed.
4635// Otherwise, it either returns FALSE or throws an exception, depending on the value of throwIfTargetIsInaccessible.
4636
4637BOOL AccessCheckOptions::DemandMemberAccess(AccessCheckContext *pContext, MethodTable * pTargetMT, BOOL visibilityCheck) const
4638{
4639 CONTRACTL
4640 {
4641 THROWS;
4642 GC_TRIGGERS;
4643 MODE_ANY;
4644 PRECONDITION(m_accessCheckType != kNormalAccessibilityChecks);
4645 PRECONDITION(CheckPointer(pContext));
4646 }
4647 CONTRACTL_END;
4648
4649 _ASSERTE(m_accessCheckType != kNormalAccessibilityChecks);
4650
4651 if (NingenEnabled())
4652 {
4653 // NinGen should always perform normal accessibility checks
4654 _ASSERTE(false);
4655
4656 if (m_fThrowIfTargetIsInaccessible)
4657 {
4658 ThrowAccessException(pContext, pTargetMT, NULL);
4659 }
4660
4661 return FALSE;
4662 }
4663
4664 if (pTargetMT && pTargetMT->GetAssembly()->IsDisabledPrivateReflection())
4665 {
4666 if (m_fThrowIfTargetIsInaccessible)
4667 {
4668 ThrowAccessException(pContext, pTargetMT, NULL);
4669 }
4670
4671 return FALSE;
4672 }
4673
4674 BOOL canAccessTarget = FALSE;
4675
4676#ifndef CROSSGEN_COMPILE
4677
4678 // In CoreCLR kRestrictedMemberAccess means that one can access private/internal
4679 // classes/members in app code.
4680 if (m_accessCheckType != kMemberAccess && pTargetMT)
4681 {
4682 // We allow all transparency checks to succeed in LCG methods and reflection invocation.
4683 if (m_accessCheckType == kNormalAccessNoTransparency || m_accessCheckType == kRestrictedMemberAccessNoTransparency)
4684 return TRUE;
4685 }
4686
4687 // Always allow interop (NULL) callers full access.
4688 if (pContext->IsCalledFromInterop())
4689 return TRUE;
4690
4691 // No Access
4692 if (m_fThrowIfTargetIsInaccessible)
4693 {
4694 ThrowAccessException(pContext, pTargetMT, NULL);
4695 }
4696
4697#endif // CROSSGEN_COMPILE
4698
4699 return canAccessTarget;
4700}
4701
4702//******************************************************************************
4703// pFailureMT - the MethodTable that we were trying to access. It can be null
4704// if the failure is not because of a specific type. This will be a
4705// a component of the instantiation of m_pTargetMT/m_pTargetMethod/m_pTargetField.
4706
4707void AccessCheckOptions::ThrowAccessException(
4708 AccessCheckContext* pContext,
4709 MethodTable* pFailureMT, /* = NULL */
4710 Exception* pInnerException /* = NULL */) const
4711{
4712 CONTRACTL
4713 {
4714 THROWS;
4715 GC_TRIGGERS;
4716 MODE_ANY;
4717 PRECONDITION(CheckPointer(pContext));
4718 PRECONDITION(CheckPointer(pInnerException, NULL_OK));
4719 PRECONDITION(m_fThrowIfTargetIsInaccessible);
4720 }
4721 CONTRACTL_END;
4722
4723 GCX_COOP();
4724
4725 MethodDesc* pCallerMD = pContext->GetCallerMethod();
4726
4727 if (m_pTargetMT != NULL)
4728 {
4729 // If we know the specific type that caused the failure, display it.
4730 // Else display the whole type that we are trying to access.
4731 MethodTable * pMT = (pFailureMT != NULL) ? pFailureMT : m_pTargetMT;
4732 ThrowTypeAccessException(pContext, pMT, 0, pInnerException);
4733 }
4734 else if (m_pTargetMethod != NULL)
4735 {
4736 // If the caller and target method are non-null and the same, then this means that we're checking to see
4737 // if the method has access to itself in order to validate that it has access to its parameter types,
4738 // containing type, and return type. In this case, throw a more informative TypeAccessException to
4739 // describe the error that occurred (for instance, "this method doesn't have access to one of its
4740 // parameter types", rather than "this method doesn't have access to itself").
4741 // We only want to do this if we know the exact type that caused the problem, otherwise fall back to
4742 // throwing the standard MethodAccessException.
4743 if (pCallerMD != NULL && m_pTargetMethod == pCallerMD && pFailureMT != NULL)
4744 {
4745 ThrowTypeAccessException(pContext, pFailureMT, 0, pInnerException);
4746 }
4747 else
4748 {
4749 ThrowMethodAccessException(pContext, m_pTargetMethod, 0, pInnerException);
4750 }
4751 }
4752 else
4753 {
4754 _ASSERTE(m_pTargetField != NULL);
4755 ThrowFieldAccessException(pContext, m_pTargetField, 0, pInnerException);
4756 }
4757}
4758
4759//******************************************************************************
4760// This will do a security demand if appropriate.
4761// If access is not possible, this will either throw an exception or return FALSE
4762BOOL AccessCheckOptions::DemandMemberAccessOrFail(AccessCheckContext *pContext, MethodTable * pTargetMT, BOOL visibilityCheck) const
4763{
4764 CONTRACTL
4765 {
4766 THROWS;
4767 GC_TRIGGERS;
4768 MODE_ANY;
4769 }
4770 CONTRACTL_END;
4771
4772 if (DoNormalAccessibilityChecks())
4773 {
4774 if (pContext->GetCallerAssembly()->IgnoresAccessChecksTo(pTargetMT->GetAssembly()))
4775 {
4776 return TRUE;
4777 }
4778
4779 if (m_fThrowIfTargetIsInaccessible)
4780 {
4781 ThrowAccessException(pContext, pTargetMT);
4782 }
4783
4784 return FALSE;
4785 }
4786
4787 return DemandMemberAccess(pContext, pTargetMT, visibilityCheck);
4788}
4789
4790//******************************************************************************
4791// This should be called if access to the target is not possible.
4792// This will either throw an exception or return FALSE.
4793BOOL AccessCheckOptions::FailOrThrow(AccessCheckContext *pContext) const
4794{
4795 CONTRACTL
4796 {
4797 THROWS;
4798 GC_TRIGGERS;
4799 MODE_ANY;
4800 PRECONDITION(CheckPointer(pContext));
4801 }
4802 CONTRACTL_END;
4803
4804 if (m_fThrowIfTargetIsInaccessible)
4805 {
4806 ThrowAccessException(pContext);
4807 }
4808
4809 return FALSE;
4810}
4811
4812void DECLSPEC_NORETURN ThrowFieldAccessException(AccessCheckContext* pContext,
4813 FieldDesc *pFD,
4814 UINT messageID /* = 0 */,
4815 Exception *pInnerException /* = NULL */)
4816{
4817 CONTRACTL
4818 {
4819 THROWS;
4820 GC_TRIGGERS;
4821 MODE_ANY;
4822 PRECONDITION(CheckPointer(pContext));
4823 PRECONDITION(CheckPointer(pFD));
4824 }
4825 CONTRACTL_END;
4826
4827 MethodDesc* pCallerMD = pContext->GetCallerMethod();
4828
4829 ThrowFieldAccessException(pCallerMD,
4830 pFD,
4831 messageID,
4832 pInnerException);
4833}
4834
4835void DECLSPEC_NORETURN ThrowFieldAccessException(MethodDesc* pCallerMD,
4836 FieldDesc *pFD,
4837 UINT messageID /* = 0 */,
4838 Exception *pInnerException /* = NULL */)
4839{
4840 CONTRACTL
4841 {
4842 THROWS;
4843 GC_TRIGGERS;
4844 MODE_ANY;
4845 PRECONDITION(CheckPointer(pCallerMD, NULL_OK));
4846 PRECONDITION(CheckPointer(pFD));
4847 }
4848 CONTRACTL_END;
4849
4850 if (pCallerMD != NULL)
4851 {
4852 if (messageID == 0)
4853 {
4854 messageID = IDS_E_FIELDACCESS;
4855 }
4856
4857 EX_THROW_WITH_INNER(EEFieldException, (pFD, pCallerMD, SString::Empty(), messageID), pInnerException);
4858 }
4859 else
4860 {
4861 EX_THROW_WITH_INNER(EEFieldException, (pFD), pInnerException);
4862 }
4863}
4864
4865void DECLSPEC_NORETURN ThrowMethodAccessException(AccessCheckContext* pContext,
4866 MethodDesc *pCalleeMD,
4867 UINT messageID /* = 0 */,
4868 Exception *pInnerException /* = NULL */)
4869{
4870 CONTRACTL
4871 {
4872 THROWS;
4873 GC_TRIGGERS;
4874 MODE_ANY;
4875 PRECONDITION(CheckPointer(pContext));
4876 PRECONDITION(CheckPointer(pCalleeMD));
4877 }
4878 CONTRACTL_END;
4879
4880 MethodDesc* pCallerMD = pContext->GetCallerMethod();
4881
4882 ThrowMethodAccessException(pCallerMD,
4883 pCalleeMD,
4884 messageID,
4885 pInnerException);
4886}
4887
4888void DECLSPEC_NORETURN ThrowMethodAccessException(MethodDesc* pCallerMD,
4889 MethodDesc *pCalleeMD,
4890 UINT messageID /* = 0 */,
4891 Exception *pInnerException /* = NULL */)
4892{
4893 CONTRACTL
4894 {
4895 THROWS;
4896 GC_TRIGGERS;
4897 MODE_ANY;
4898 PRECONDITION(CheckPointer(pCallerMD, NULL_OK));
4899 PRECONDITION(CheckPointer(pCalleeMD));
4900 }
4901 CONTRACTL_END;
4902
4903 if (pCallerMD != NULL)
4904 {
4905 if (messageID == 0)
4906 {
4907 messageID = IDS_E_METHODACCESS;
4908 }
4909
4910 EX_THROW_WITH_INNER(EEMethodException, (pCalleeMD, pCallerMD, SString::Empty(), messageID), pInnerException);
4911 }
4912 else
4913 {
4914 EX_THROW_WITH_INNER(EEMethodException, (pCalleeMD), pInnerException);
4915 }
4916}
4917
4918void DECLSPEC_NORETURN ThrowTypeAccessException(AccessCheckContext* pContext,
4919 MethodTable *pMT,
4920 UINT messageID /* = 0 */,
4921 Exception *pInnerException /* = NULL */)
4922{
4923 CONTRACTL
4924 {
4925 THROWS;
4926 GC_TRIGGERS;
4927 MODE_ANY;
4928 PRECONDITION(CheckPointer(pContext));
4929 PRECONDITION(CheckPointer(pMT));
4930 }
4931 CONTRACTL_END;
4932
4933 MethodDesc* pCallerMD = pContext->GetCallerMethod();
4934
4935 ThrowTypeAccessException(pCallerMD,
4936 pMT,
4937 messageID,
4938 pInnerException);
4939}
4940
4941void DECLSPEC_NORETURN ThrowTypeAccessException(MethodDesc* pCallerMD,
4942 MethodTable *pMT,
4943 UINT messageID /* = 0 */,
4944 Exception *pInnerException /* = NULL */)
4945{
4946 CONTRACTL
4947 {
4948 THROWS;
4949 GC_TRIGGERS;
4950 MODE_ANY;
4951 PRECONDITION(CheckPointer(pCallerMD, NULL_OK));
4952 PRECONDITION(CheckPointer(pMT));
4953 }
4954 CONTRACTL_END;
4955
4956 if (pCallerMD != NULL)
4957 {
4958 if (messageID == 0)
4959 {
4960 messageID = IDS_E_TYPEACCESS;
4961 }
4962
4963 EX_THROW_WITH_INNER(EETypeAccessException, (pMT, pCallerMD, SString::Empty(), messageID), pInnerException);
4964 }
4965 else
4966 {
4967 EX_THROW_WITH_INNER(EETypeAccessException, (pMT), pInnerException);
4968 }
4969}
4970
4971//---------------------------------------------------------------------------------------
4972//
4973// Checks to see if access to a member with assembly visiblity is allowed.
4974//
4975// Arguments:
4976// pAccessingAssembly - The assembly requesting access to the internal member
4977// pTargetAssembly - The assembly which contains the target member
4978// pOptionalTargetField - Internal field being accessed OR
4979// pOptionalTargetMethod - Internal type being accessed OR
4980// pOptionalTargetType - Internal type being accessed
4981//
4982// Return Value:
4983// TRUE if pTargetAssembly is pAccessingAssembly, or if pTargetAssembly allows
4984// pAccessingAssembly friend access to the target. FALSE otherwise.
4985//
4986
4987static BOOL AssemblyOrFriendAccessAllowed(Assembly *pAccessingAssembly,
4988 Assembly *pTargetAssembly,
4989 FieldDesc *pOptionalTargetField,
4990 MethodDesc *pOptionalTargetMethod,
4991 MethodTable *pOptionalTargetType)
4992{
4993 CONTRACTL
4994 {
4995 THROWS;
4996 GC_TRIGGERS;
4997 PRECONDITION(CheckPointer(pAccessingAssembly));
4998 PRECONDITION(CheckPointer(pTargetAssembly));
4999 PRECONDITION(pOptionalTargetField != NULL || pOptionalTargetMethod != NULL || pOptionalTargetType != NULL);
5000 PRECONDITION(pOptionalTargetField == NULL || pOptionalTargetMethod == NULL);
5001 }
5002 CONTRACTL_END;
5003
5004 if (pAccessingAssembly == pTargetAssembly)
5005 {
5006 return TRUE;
5007 }
5008
5009 if (pAccessingAssembly->IgnoresAccessChecksTo(pTargetAssembly))
5010 {
5011 return TRUE;
5012 }
5013
5014 else if (pOptionalTargetField != NULL)
5015 {
5016 return pTargetAssembly->GrantsFriendAccessTo(pAccessingAssembly, pOptionalTargetField);
5017 }
5018 else if (pOptionalTargetMethod != NULL)
5019 {
5020 return pTargetAssembly->GrantsFriendAccessTo(pAccessingAssembly, pOptionalTargetMethod);
5021 }
5022 else
5023 {
5024 return pTargetAssembly->GrantsFriendAccessTo(pAccessingAssembly, pOptionalTargetType);
5025 }
5026}
5027
5028//******************************************************************************
5029// This function determines whether a target class is accessible from
5030// some given class.
5031/* static */
5032BOOL ClassLoader::CanAccessMethodInstantiation( // True if access is legal, false otherwise.
5033 AccessCheckContext* pContext,
5034 MethodDesc* pOptionalTargetMethod, // The desired method; if NULL, return TRUE (or)
5035 const AccessCheckOptions & accessCheckOptions)
5036{
5037 CONTRACTL
5038 {
5039 THROWS;
5040 GC_TRIGGERS;
5041 INJECT_FAULT(COMPlusThrowOM(););
5042 MODE_ANY;
5043 PRECONDITION(CheckPointer(pContext));
5044 }
5045 CONTRACTL_END
5046
5047 // If there is no target method just allow access.
5048 // NB: the caller may just be checking access to a field or class, so we allow for NULL.
5049 if (!pOptionalTargetMethod)
5050 return TRUE;
5051
5052 // Is the desired target an instantiated generic method?
5053 if (pOptionalTargetMethod->HasMethodInstantiation())
5054 { // check that the current class has access
5055 // to all of the instantiating classes.
5056 Instantiation inst = pOptionalTargetMethod->GetMethodInstantiation();
5057 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
5058 {
5059 TypeHandle th = inst[i];
5060
5061 MethodTable* pMT = th.GetMethodTableOfElementType();
5062
5063 // Either a TypeVarTypeDesc or a FnPtrTypeDesc. No access check needed.
5064 if (pMT == NULL)
5065 continue;
5066
5067 if (!CanAccessClass(
5068 pContext,
5069 pMT,
5070 th.GetAssembly(),
5071 accessCheckOptions))
5072 {
5073 return FALSE;
5074 }
5075 }
5076 // If we are here, the current class has access to all of the target's instantiating args,
5077 }
5078 return TRUE;
5079}
5080
5081//******************************************************************************
5082// This function determines whether a target class is accessible from
5083// some given class.
5084// CanAccessClass does the following checks:
5085// 1. Transparency check on the target class
5086// 2. Recursively calls CanAccessClass on the generic arguments of the target class if it is generic.
5087// 3. Visibility check on the target class, if the target class is nested, this will be translated
5088// to a member access check on the enclosing type (calling CanAccess with appropriate dwProtection.
5089//
5090/* static */
5091BOOL ClassLoader::CanAccessClass( // True if access is legal, false otherwise.
5092 AccessCheckContext* pContext, // The caller context
5093 MethodTable* pTargetClass, // The desired target class.
5094 Assembly* pTargetAssembly, // Assembly containing the target class.
5095 const AccessCheckOptions & accessCheckOptions)// = TRUE
5096{
5097 CONTRACTL
5098 {
5099 THROWS;
5100 GC_TRIGGERS;
5101 INJECT_FAULT(COMPlusThrowOM(););
5102 MODE_ANY;
5103 PRECONDITION(CheckPointer(pContext));
5104 PRECONDITION(CheckPointer(pTargetClass));
5105 }
5106 CONTRACTL_END
5107
5108 // If there is no target class, allow access.
5109 // @todo: what does that mean?
5110 //if (!pTargetClass)
5111 // return TRUE;
5112
5113 // Step 2: Recursively call CanAccessClass on the generic type arguments
5114 // Is the desired target a generic instantiation?
5115 if (pTargetClass->HasInstantiation())
5116 { // Yes, so before going any further, check that the current class has access
5117 // to all of the instantiating classes.
5118 Instantiation inst = pTargetClass->GetInstantiation();
5119 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
5120 {
5121 TypeHandle th = inst[i];
5122
5123 MethodTable* pMT = th.GetMethodTableOfElementType();
5124
5125 // Either a TypeVarTypeDesc or a FnPtrTypeDesc. No access check needed.
5126 if (pMT == NULL)
5127 continue;
5128
5129 if (!CanAccessClass(
5130 pContext,
5131 pMT,
5132 th.GetAssembly(),
5133 accessCheckOptions))
5134 {
5135 // no need to call accessCheckOptions.DemandMemberAccessOrFail here because the base case in
5136 // CanAccessClass does that already
5137 return FALSE;
5138 }
5139 }
5140 // If we are here, the current class has access to all of the desired target's instantiating args.
5141 // Now, check whether the current class has access to the desired target itself.
5142 }
5143
5144 // Step 3: Visibility Check
5145 if (!pTargetClass->GetClass()->IsNested())
5146 { // a non-nested class can be either all public or accessible only from its own assembly (and friends).
5147 if (IsTdPublic(pTargetClass->GetClass()->GetProtection()))
5148 {
5149 return TRUE;
5150 }
5151 else
5152 {
5153 // Always allow interop callers full access.
5154 if (pContext->IsCalledFromInterop())
5155 return TRUE;
5156
5157 Assembly* pCurrentAssembly = pContext->GetCallerAssembly();
5158 _ASSERTE(pCurrentAssembly != NULL);
5159
5160 if (AssemblyOrFriendAccessAllowed(pCurrentAssembly,
5161 pTargetAssembly,
5162 NULL,
5163 NULL,
5164 pTargetClass))
5165 {
5166 return TRUE;
5167 }
5168 else
5169 {
5170 return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetClass, TRUE /*visibilityCheck*/);
5171 }
5172 }
5173 }
5174
5175 // If we are here, the desired target class is nested. Translate the type flags
5176 // to corresponding method access flags. We need to make a note if friend access was allowed to the
5177 // type being checked since we're not passing it directly to the recurisve call to CanAccess, and
5178 // instead are just passing in the dwProtectionFlags.
5179 DWORD dwProtection = pTargetClass->GetClass()->GetProtection();
5180
5181 switch(dwProtection) {
5182 case tdNestedPublic:
5183 dwProtection = mdPublic;
5184 break;
5185 case tdNestedFamily:
5186 dwProtection = mdFamily;
5187 break;
5188 case tdNestedPrivate:
5189 dwProtection = mdPrivate;
5190 break;
5191 case tdNestedFamORAssem:
5192 // If we can access the class because we have assembly or friend access, we have satisfied the
5193 // FamORAssem accessibility, so we we can simplify it down to public. Otherwise we require that
5194 // family access be allowed to grant access.
5195 case tdNestedFamANDAssem:
5196 // If we don't grant assembly or friend access to the target class, then there is no way we
5197 // could satisfy the FamANDAssem requirement. Otherwise, since we have satsified the Assm
5198 // portion, we only need to check for the Fam portion.
5199 case tdNestedAssembly:
5200 // If we don't grant assembly or friend access to the target class, and that class has assembly
5201 // protection, we can fail the request now. Otherwise we can check to make sure a public member
5202 // of the outer class is allowed, since we have satisfied the target's accessibility rules.
5203
5204 // Always allow interop callers full access.
5205 if (pContext->IsCalledFromInterop())
5206 return TRUE;
5207
5208 if (AssemblyOrFriendAccessAllowed(pContext->GetCallerAssembly(), pTargetAssembly, NULL, NULL, pTargetClass))
5209 dwProtection = (dwProtection == tdNestedFamANDAssem) ? mdFamily : mdPublic;
5210 else if (dwProtection == tdNestedFamORAssem)
5211 dwProtection = mdFamily;
5212 else
5213 return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetClass, TRUE /*visibilityCheck*/);
5214
5215 break;
5216
5217 default:
5218 THROW_BAD_FORMAT_MAYBE(!"Unexpected class visibility flag value", BFA_BAD_VISIBILITY, pTargetClass);
5219 }
5220
5221 // The desired target class is nested, so translate the class access request into
5222 // a member access request. That is, if the current class is trying to access A::B,
5223 // check if it can access things in A with the visibility of B.
5224 // So, pass A as the desired target class and visibility of B within A as the member access
5225 // We've already done transparency check above. No need to do it again.
5226 return ClassLoader::CanAccess(
5227 pContext,
5228 GetEnclosingMethodTable(pTargetClass),
5229 pTargetAssembly,
5230 dwProtection,
5231 NULL,
5232 NULL,
5233 accessCheckOptions);
5234} // BOOL ClassLoader::CanAccessClass()
5235
5236//******************************************************************************
5237// This is a front-end to CheckAccessMember that handles the nested class scope. If can't access
5238// from the current point and are a nested class, then try from the enclosing class.
5239// In addition to CanAccessMember, if the caller class doesn't have access to the caller, see if the enclosing class does.
5240//
5241/* static */
5242BOOL ClassLoader::CanAccess( // TRUE if access is allowed, FALSE otherwise.
5243 AccessCheckContext* pContext, // The caller context
5244 MethodTable* pTargetMT, // The class containing the desired target member.
5245 Assembly* pTargetAssembly, // Assembly containing that class.
5246 DWORD dwMemberAccess, // Member access flags of the desired target member (as method bits).
5247 MethodDesc* pOptionalTargetMethod, // The target method; NULL if the target is a not a method or
5248 // there is no need to check the method's instantiation.
5249 FieldDesc* pOptionalTargetField, // or The desired field; if NULL, return TRUE
5250 const AccessCheckOptions & accessCheckOptions) // = s_NormalAccessChecks
5251{
5252 CONTRACT(BOOL)
5253 {
5254 THROWS;
5255 GC_TRIGGERS;
5256 INJECT_FAULT(COMPlusThrowOM(););
5257 PRECONDITION(CheckPointer(pContext));
5258 MODE_ANY;
5259 }
5260 CONTRACT_END;
5261
5262 // Recursive: CanAccess->CheckAccessMember->CanAccessClass->CanAccess
5263 INTERIOR_STACK_PROBE(GetThread());
5264
5265 AccessCheckOptions accessCheckOptionsNoThrow(accessCheckOptions, FALSE);
5266
5267 if (!CheckAccessMember(pContext,
5268 pTargetMT,
5269 pTargetAssembly,
5270 dwMemberAccess,
5271 pOptionalTargetMethod,
5272 pOptionalTargetField,
5273 // Suppress exceptions for nested classes since this is not a hard-failure,
5274 // and we can do additional checks
5275 accessCheckOptionsNoThrow))
5276 {
5277 // If we're here, CheckAccessMember didn't allow access.
5278 BOOL canAccess = FALSE;
5279
5280 // If the current class is nested, there may be an enclosing class that might have access
5281 // to the target. And if the pCurrentMT == NULL, the current class is global, and so there
5282 // is no enclosing class.
5283 MethodTable* pCurrentMT = pContext->GetCallerMT();
5284
5285 // if this is called from interop, the CheckAccessMember call above should have already succeeded.
5286 _ASSERTE(!pContext->IsCalledFromInterop());
5287
5288 BOOL isNestedClass = (pCurrentMT && pCurrentMT->GetClass()->IsNested());
5289
5290 if (isNestedClass)
5291 {
5292 // A nested class also has access to anything that the enclosing class does, so
5293 // recursively check whether the enclosing class can access the desired target member.
5294 MethodTable * pEnclosingMT = GetEnclosingMethodTable(pCurrentMT);
5295
5296 StaticAccessCheckContext accessContext(pContext->GetCallerMethod(),
5297 pEnclosingMT,
5298 pContext->GetCallerAssembly());
5299
5300 // On failure, do not throw from inside this call since that will cause the exception message
5301 // to refer to the enclosing type.
5302 canAccess = ClassLoader::CanAccess(
5303 &accessContext,
5304 pTargetMT,
5305 pTargetAssembly,
5306 dwMemberAccess,
5307 pOptionalTargetMethod,
5308 pOptionalTargetField,
5309 accessCheckOptionsNoThrow);
5310 }
5311
5312 if (!canAccess)
5313 {
5314 BOOL fail = accessCheckOptions.FailOrThrow(pContext);
5315 RETURN_FROM_INTERIOR_PROBE(fail);
5316 }
5317 }
5318
5319 RETURN_FROM_INTERIOR_PROBE(TRUE);
5320
5321 END_INTERIOR_STACK_PROBE;
5322} // BOOL ClassLoader::CanAccess()
5323
5324//******************************************************************************
5325// This is the helper function for the corresponding CanAccess()
5326// It does the following checks:
5327// 1. CanAccessClass on pTargetMT
5328// 2. CanAccessMethodInstantiation if the pOptionalTargetMethod is provided and is generic.
5329// 3. Transparency check on pTargetMT, pOptionalTargetMethod and pOptionalTargetField.
5330// 4. Visibility check on dwMemberAccess (on pTargetMT)
5331
5332/* static */
5333BOOL ClassLoader::CheckAccessMember( // TRUE if access is allowed, false otherwise.
5334 AccessCheckContext* pContext,
5335 MethodTable* pTargetMT, // The class containing the desired target member.
5336 Assembly* pTargetAssembly, // Assembly containing that class.
5337 DWORD dwMemberAccess, // Member access flags of the desired target member (as method bits).
5338 MethodDesc* pOptionalTargetMethod, // The target method; NULL if the target is a not a method or
5339 // there is no need to check the method's instantiation.
5340 FieldDesc* pOptionalTargetField, // target field, NULL if there is no Target field
5341 const AccessCheckOptions & accessCheckOptions
5342 )
5343{
5344 CONTRACTL
5345 {
5346 THROWS;
5347 GC_TRIGGERS;
5348 INJECT_FAULT(COMPlusThrowOM(););
5349 PRECONDITION(CheckPointer(pContext));
5350 MODE_ANY;
5351 }
5352 CONTRACTL_END
5353
5354 // we're trying to access a member that is contained in the class pTargetClass, so need to
5355 // check if have access to pTargetClass itself from the current point before worry about
5356 // having access to the member within the class
5357 if (!CanAccessClass(pContext,
5358 pTargetMT,
5359 pTargetAssembly,
5360 accessCheckOptions))
5361 {
5362 return FALSE;
5363 }
5364
5365 // If we are trying to access a generic method, we have to ensure its instantiation is accessible.
5366 // Note that we need to perform transparency checks on the instantiation even if we have
5367 if (!CanAccessMethodInstantiation(
5368 pContext,
5369 pOptionalTargetMethod,
5370 accessCheckOptions))
5371 {
5372 return FALSE;
5373 }
5374
5375 // pOptionalTargetMethod and pOptionalTargetField can never be NULL at the same time.
5376 _ASSERTE(pOptionalTargetMethod == NULL || pOptionalTargetField == NULL);
5377
5378 // Perform transparency checks
5379 // We don't need to do transparency check against pTargetMT here because
5380 // it was already done in CanAccessClass above.
5381
5382 if (IsMdPublic(dwMemberAccess))
5383 {
5384 return TRUE;
5385 }
5386
5387 // Always allow interop callers full access.
5388 if (pContext->IsCalledFromInterop())
5389 return TRUE;
5390
5391 MethodTable* pCurrentMT = pContext->GetCallerMT();
5392
5393 if (IsMdPrivateScope(dwMemberAccess))
5394 {
5395 if (pCurrentMT != NULL && pCurrentMT->GetModule() == pTargetMT->GetModule())
5396 {
5397 return TRUE;
5398 }
5399 else
5400 {
5401 return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
5402 }
5403 }
5404
5405
5406#ifdef _DEBUG
5407 if (pTargetMT == NULL &&
5408 (IsMdFamORAssem(dwMemberAccess) ||
5409 IsMdFamANDAssem(dwMemberAccess) ||
5410 IsMdFamily(dwMemberAccess))) {
5411 THROW_BAD_FORMAT_MAYBE(!"Family flag is not allowed on global functions", BFA_FAMILY_ON_GLOBAL, pTargetMT);
5412 }
5413#endif
5414
5415 if (pTargetMT == NULL ||
5416 IsMdAssem(dwMemberAccess) ||
5417 IsMdFamORAssem(dwMemberAccess) ||
5418 IsMdFamANDAssem(dwMemberAccess))
5419 {
5420 // If the member has Assembly accessibility, grant access if the current
5421 // class is in the same assembly as the desired target member, or if the
5422 // desired target member's assembly grants friend access to the current
5423 // assembly.
5424 // @todo: What does it mean for the target class to be NULL?
5425
5426 Assembly* pCurrentAssembly = pContext->GetCallerAssembly();
5427
5428 // pCurrentAssembly should never be NULL, unless we are called from interop,
5429 // in which case we should have already returned TRUE.
5430 _ASSERTE(pCurrentAssembly != NULL);
5431
5432 const BOOL fAssemblyOrFriendAccessAllowed = AssemblyOrFriendAccessAllowed(pCurrentAssembly,
5433 pTargetAssembly,
5434 pOptionalTargetField,
5435 pOptionalTargetMethod,
5436 pTargetMT);
5437
5438 if ((pTargetMT == NULL || IsMdAssem(dwMemberAccess) || IsMdFamORAssem(dwMemberAccess)) &&
5439 fAssemblyOrFriendAccessAllowed)
5440 {
5441 return TRUE;
5442 }
5443 else if (IsMdFamANDAssem(dwMemberAccess) &&
5444 !fAssemblyOrFriendAccessAllowed)
5445 {
5446 return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
5447 }
5448 }
5449
5450 // Nested classes can access all members of the parent class.
5451 while(pCurrentMT != NULL)
5452 {
5453 //@GENERICSVER:
5454 if (pTargetMT->HasSameTypeDefAs(pCurrentMT))
5455 return TRUE;
5456
5457 if (IsMdPrivate(dwMemberAccess))
5458 {
5459 if (!pCurrentMT->GetClass()->IsNested())
5460 {
5461 return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
5462 }
5463 }
5464 else if (IsMdFamORAssem(dwMemberAccess) || IsMdFamily(dwMemberAccess) || IsMdFamANDAssem(dwMemberAccess))
5465 {
5466 if (CanAccessFamily(pCurrentMT, pTargetMT))
5467 {
5468 return TRUE;
5469 }
5470 }
5471
5472 pCurrentMT = GetEnclosingMethodTable(pCurrentMT);
5473 }
5474
5475 return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, TRUE /*visibilityCheck*/);
5476}
5477
5478// The family check is actually in two parts (Partition I, 8.5.3.2). The first part:
5479//
5480// ...accessible to referents that support the same type
5481// (i.e., an exact type and all of the types that inherit
5482// from it).
5483//
5484// Translation: pCurrentClass must be the same type as pTargetClass or a derived class. (i.e. Derived
5485// can access Base.protected but Unrelated cannot access Base.protected).
5486//
5487// The second part:
5488//
5489// For verifiable code (see Section 8.8), there is an additional
5490// requirement that can require a runtime check: the reference
5491// shall be made through an item whose exact type supports
5492// the exact type of the referent. That is, the item whose
5493// member is being accessed shall inherit from the type
5494// performing the access.
5495//
5496// Translation: The C++ protected rule. For those unfamiliar, it means that:
5497// if you have:
5498// GrandChild : Child
5499// and
5500// Child : Parent
5501// and
5502// Parent {
5503// protected:
5504// int protectedField;
5505// }
5506//
5507// Child::function(GrandChild * o) {
5508// o->protectedField; //This access is legal.
5509// }
5510//
5511// GrandChild:function2(Child * o) {
5512// o->protectedField; //This access is illegal.
5513// }
5514//
5515// The reason for this rule is that if you had:
5516// Sibling : Parent
5517//
5518// Child::function3( Sibling * o ) {
5519// o->protectedField; //This access is illegal
5520// }
5521//
5522// This is intuitively correct. However, you need to prevent:
5523// Child::function4( Sibling * o ) {
5524// ((Parent*)o)->protectedField;
5525// }
5526//
5527// Which means that you must access protected fields through a type that is yourself or one of your
5528// derived types.
5529
5530//This checks the first part of the rule above.
5531/* static */
5532BOOL ClassLoader::CanAccessFamily(
5533 MethodTable *pCurrentClass,
5534 MethodTable *pTargetClass)
5535{
5536 CONTRACTL
5537 {
5538 THROWS;
5539 GC_TRIGGERS;
5540 INJECT_FAULT(COMPlusThrowOM(););
5541 MODE_ANY;
5542 PRECONDITION(CheckPointer(pTargetClass));
5543 }
5544 CONTRACTL_END
5545
5546 _ASSERTE(pCurrentClass);
5547 _ASSERTE(pTargetClass);
5548
5549 BOOL bIsInterface = pTargetClass->IsInterface();
5550
5551 //Look to see if Current is a child of the Target.
5552 while (pCurrentClass) {
5553 if (bIsInterface)
5554 {
5555 // Calling a protected interface member
5556 MethodTable::InterfaceMapIterator it = pCurrentClass->IterateInterfaceMap();
5557 while (it.Next())
5558 {
5559 // We only loosely check if they are of the same generic type
5560 if (it.GetInterface()->HasSameTypeDefAs(pTargetClass))
5561 return TRUE;
5562 }
5563 }
5564 else
5565 {
5566 MethodTable *pCurInstance = pCurrentClass;
5567
5568 while (pCurInstance) {
5569 //This is correct. csc is incredibly lax about generics. Essentially if you are a subclass of
5570 //any type of generic it lets you access it. Since the standard is totally unclear, mirror that
5571 //behavior here.
5572 if (pCurInstance->HasSameTypeDefAs(pTargetClass)) {
5573 return TRUE;
5574 }
5575
5576 pCurInstance = pCurInstance->GetParentMethodTable();
5577 }
5578 }
5579
5580 ///Looking at 8.5.3, it looks like a protected member of a nested class in a parent type is also
5581 //accessible.
5582 pCurrentClass = GetEnclosingMethodTable(pCurrentClass);
5583 }
5584
5585 return FALSE;
5586}
5587
5588//If instance is an inner class, this also succeeds if the outer class conforms to 8.5.3.2. A nested class
5589//is enclosed inside of the enclosing class' open type. So we need to ignore generic variables. That also
5590//helps us with:
5591/*
5592class Base {
5593 protected int m_family;
5594}
5595class Derived<T> : Base {
5596 class Inner {
5597 public int function(Derived<T> d) {
5598 return d.m_family;
5599 }
5600 }
5601}
5602*/
5603
5604//Since the inner T is not the same T as the enclosing T (since accessing generic variables is a CLS rule,
5605//not a CLI rule), we see that as a comparison between Derived<T> and Derived<T'>. CanCastTo rejects that.
5606//Instead we just check against the typedef of the two types. This ignores all generic parameters (formal
5607//or not).
5608
5609BOOL CanAccessFamilyVerificationEnclosingHelper(MethodTable * pMTCurrentEnclosingClass,
5610 TypeHandle thInstanceClass)
5611{
5612 CONTRACTL
5613 {
5614 THROWS;
5615 GC_TRIGGERS;
5616 MODE_ANY;
5617 }
5618 CONTRACTL_END
5619
5620 _ASSERTE(pMTCurrentEnclosingClass);
5621
5622 if (thInstanceClass.IsGenericVariable())
5623 {
5624 //In this case it is a TypeVarTypeDesc (i.e. T). If this access would be legal due to a
5625 //constraint:
5626 //
5627 /*
5628 public class My<T>
5629 {
5630 public class Inner<U> where U : My<T>
5631 {
5632 public int foo(U u)
5633 {
5634 return u.field;
5635 }
5636 }
5637 protected int field;
5638 }
5639 */
5640 //We need to find the generic class constraint. (The above is legal because U must be a My<T> which makes this
5641 //legal by 8.5.3.2)
5642 // There may only be 1 class constraint on a generic parameter
5643
5644 // Get the constraints on this generic variable
5645 // At most 1 of them is a class constraint.
5646 // That class constraint methodtable can go through the normal search for matching typedef logic below
5647 TypeVarTypeDesc *tyvar = thInstanceClass.AsGenericVariable();
5648 DWORD numConstraints;
5649 TypeHandle *constraints = tyvar->GetConstraints(&numConstraints, CLASS_DEPENDENCIES_LOADED);
5650 if (constraints == NULL)
5651 {
5652 // If we did not find a class constraint, we cannot generate a methodtable to search for
5653 return FALSE;
5654 }
5655 else
5656 {
5657 for (DWORD i = 0; i < numConstraints; i++)
5658 {
5659 if (!constraints[i].IsInterface())
5660 {
5661 // We have found the class constraint on this TypeVarTypeDesc
5662 // Recurse on the found class constraint. It is possible that this constraint may also be a TypeVarTypeDesc
5663//class Outer4<T>
5664//{
5665// protected int field;
5666//
5667// public class Inner<U,V> where V:U where U : Outer4<T>
5668// {
5669// public int Method(V param) { return (++param.field); }
5670// }
5671//}
5672 return CanAccessFamilyVerificationEnclosingHelper(pMTCurrentEnclosingClass, constraints[i]);
5673 }
5674 }
5675 // If we did not find a class constraint, we cannot generate a methodtable to search for
5676 return FALSE;
5677 }
5678 }
5679 do
5680 {
5681 MethodTable * pAccessor = pMTCurrentEnclosingClass;
5682 //If thInstanceClass is a MethodTable, we should only be doing the TypeDef comparison (see
5683 //above).
5684 if (!thInstanceClass.IsTypeDesc())
5685 {
5686 MethodTable *pInstanceMT = thInstanceClass.AsMethodTable();
5687
5688 // This is a CanCastTo implementation for classes, assuming we should ignore generic instantiation parameters.
5689 do
5690 {
5691 if (pAccessor->HasSameTypeDefAs(pInstanceMT))
5692 return TRUE;
5693 pInstanceMT = pInstanceMT->GetParentMethodTable();
5694 }while(pInstanceMT);
5695 }
5696 else
5697 {
5698 // Leave this logic in place for now, as I'm not fully confident it can't happen, and we are very close to RTM
5699 // This logic was originally written to handle TypeVarTypeDescs, but those are now handled above.
5700 _ASSERTE(FALSE);
5701 if (thInstanceClass.CanCastTo(TypeHandle(pAccessor)))
5702 return TRUE;
5703 }
5704
5705 pMTCurrentEnclosingClass = GetEnclosingMethodTable(pMTCurrentEnclosingClass);
5706 }while(pMTCurrentEnclosingClass);
5707 return FALSE;
5708}
5709
5710
5711//This checks the verification only part of the rule above.
5712//From the example above:
5713// GrandChild::function2(Child * o) {
5714// o->protectedField; //This access is illegal.
5715// }
5716// pCurrentClass is GrandChild and pTargetClass is Child. This check is completely unnecessary for statics,
5717// but by legacy convention you can use GrandChild for pTargetClass in that case.
5718
5719BOOL ClassLoader::CanAccessFamilyVerification(TypeHandle thCurrentClass,
5720 TypeHandle thInstanceClass)
5721{
5722 CONTRACTL
5723 {
5724 THROWS;
5725 GC_TRIGGERS;
5726 INJECT_FAULT(COMPlusThrowOM(););
5727 MODE_ANY;
5728 PRECONDITION(!thCurrentClass.IsNull());
5729 PRECONDITION(!thCurrentClass.IsTypeDesc());
5730 }
5731 CONTRACTL_END
5732
5733 //Check to see if Instance is equal to or derived from pCurrentClass.
5734 //
5735 //In some cases the type we have for the instance type is actually a TypeVarTypeDesc. In those cases we
5736 //need to check against the constraints (You're accessing a member through a 'T' with a type constraint
5737 //that makes this legal). For those cases, CanCastTo does what I want.
5738 MethodTable * pAccessor = thCurrentClass.GetMethodTable();
5739 if (thInstanceClass.CanCastTo(TypeHandle(pAccessor)))
5740 return TRUE;
5741
5742 //ArrayTypeDescs are the only typedescs that have methods, and their methods don't have IL. All other
5743 //TypeDescs don't need to be here. So only run this on MethodTables.
5744 if (!thInstanceClass.IsNull())
5745 {
5746 return CanAccessFamilyVerificationEnclosingHelper(pAccessor, thInstanceClass);
5747 }
5748 return FALSE;
5749}
5750
5751#endif // #ifndef DACCESS_COMPILE
5752
5753#ifdef DACCESS_COMPILE
5754
5755void
5756ClassLoader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
5757{
5758 WRAPPER_NO_CONTRACT;
5759 SUPPORTS_DAC;
5760 DAC_ENUM_DTHIS();
5761
5762 EMEM_OUT(("MEM: %p ClassLoader\n", dac_cast<TADDR>(this)));
5763
5764 if (m_pAssembly.IsValid())
5765 {
5766 ModuleIterator modIter = GetAssembly()->IterateModules();
5767
5768 while (modIter.Next())
5769 {
5770 modIter.GetModule()->EnumMemoryRegions(flags, true);
5771 }
5772 }
5773}
5774
5775#endif // #ifdef DACCESS_COMPILE
5776