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 */ |
75 | PTR_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 | { |
153 | ComputeCollectibleLoaderModule: |
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 */ |
198 | PTR_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 | } |
313 | ModuleAdjustedForVersionResiliency: ; |
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*/ |
359 | Module * 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*/ |
378 | Module *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*/ |
402 | BOOL 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*/ |
439 | TypeHandle 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 |
490 | TypeHandle 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> |
544 | EEClassHashEntry_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 | |
587 | BOOL 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 | |
641 | BOOL 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 | |
682 | BOOL 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*/ |
730 | BOOL 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 | |
767 | BOOL 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 | |
794 | void 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 | |
965 | VOID 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 | |
1025 | void 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 | |
1073 | void 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*/ |
1124 | void 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 | |
1139 | TypeHandle 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*/ |
1218 | void 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*/ |
1261 | void 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 */ |
1283 | TypeHandle 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 */ |
1298 | TypeHandle 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 */ |
1331 | TypeHandle 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 */ |
1361 | TypeHandle 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 */ |
1384 | TypeHandle 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 */ |
1408 | TypeHandle 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 | // |
1488 | BOOL 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). |
1721 | bool 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. |
1754 | static 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 | // |
1763 | TypeHandle |
1764 | ClassLoader::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 */ |
1952 | TypeHandle 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 */ |
1977 | TypeHandle 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 */ |
1999 | TypeHandle 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 */ |
2027 | TypeHandle 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*/ |
2105 | HRESULT 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 | |
2146 | VOID 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*/ |
2216 | TypeHandle 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 | |
2251 | DomainAssembly *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 | // |
2262 | void 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 | |
2290 | ClassLoader::~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 | //---------------------------------------------------------------------------- |
2358 | ClassLoader::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 | //---------------------------------------------------------------------------- |
2399 | VOID 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*/ |
2433 | TypeHandle 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*/ |
2498 | TypeHandle 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) |
2672 | Exit: |
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*/ |
2699 | TypeHandle 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*/ |
2870 | BOOL |
2871 | ClassLoader::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*/ |
2959 | BOOL |
2960 | ClassLoader::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 |
3052 | VOID |
3053 | ClassLoader::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 |
3097 | TypeHandle |
3098 | ClassLoader::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 |
3197 | MethodTable * |
3198 | ClassLoader::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*/ |
3262 | TypeHandle 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 |
3348 | TypeHandle 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*/ |
3474 | TypeHandle 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*/ |
3612 | void 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 | //----------------------------------------------------------------------------- |
3692 | static 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 | // |
3742 | TypeHandle 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 | // |
3808 | TypeHandle 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 | // |
3850 | class PendingTypeLoadHolder |
3851 | { |
3852 | Thread * m_pThread; |
3853 | PendingTypeLoadEntry * m_pEntry; |
3854 | PendingTypeLoadHolder * m_pPrevious; |
3855 | |
3856 | public: |
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 | // |
3896 | TypeHandle |
3897 | ClassLoader::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 | |
3932 | retry: |
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 |
4134 | TypeHandle |
4135 | ClassLoader::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 | |
4213 | VOID 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 | // |
4252 | VOID 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 | |
4413 | VOID 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 | |
4434 | VOID 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 | |
4553 | static 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 | |
4569 | StaticAccessCheckContext::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 | |
4583 | StaticAccessCheckContext::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 |
4601 | AccessCheckOptions* AccessCheckOptions::s_pNormalAccessChecks; |
4602 | |
4603 | //****************************************************************************** |
4604 | |
4605 | void 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 | //****************************************************************************** |
4617 | AccessCheckOptions::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 | |
4637 | BOOL 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 | |
4707 | void 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 |
4762 | BOOL 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. |
4793 | BOOL 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 | |
4812 | void 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 | |
4835 | void 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 | |
4865 | void 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 | |
4888 | void 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 | |
4918 | void 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 | |
4941 | void 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 | |
4987 | static 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 */ |
5032 | BOOL 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 */ |
5091 | BOOL 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 */ |
5242 | BOOL 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 */ |
5333 | BOOL 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 */ |
5532 | BOOL 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 | /* |
5592 | class Base { |
5593 | protected int m_family; |
5594 | } |
5595 | class 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 | |
5609 | BOOL 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 | |
5719 | BOOL 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 | |
5755 | void |
5756 | ClassLoader::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 | |