1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*============================================================ |
6 | ** |
7 | ** Header: AssemblySpec.cpp |
8 | ** |
9 | ** Purpose: Implements Assembly binding class |
10 | ** |
11 | ** |
12 | |
13 | |
14 | ** |
15 | ===========================================================*/ |
16 | |
17 | #include "common.h" |
18 | |
19 | #include <stdlib.h> |
20 | |
21 | #include "assemblyspec.hpp" |
22 | #include "eeconfig.h" |
23 | #include "strongname.h" |
24 | #include "strongnameholders.h" |
25 | #include "mdaassistants.h" |
26 | #include "eventtrace.h" |
27 | |
28 | #ifdef FEATURE_COMINTEROP |
29 | #include "clrprivbinderutil.h" |
30 | #include "winrthelpers.h" |
31 | #endif |
32 | |
33 | #ifdef _DEBUG |
34 | // This debug-only wrapper for LookupAssembly is solely for the use of postconditions and |
35 | // assertions. The problem is that the real LookupAssembly can throw an OOM |
36 | // simply because it can't allocate scratch space. For the sake of asserting, |
37 | // we can treat those as successful lookups. |
38 | BOOL UnsafeVerifyLookupAssembly(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec, DomainAssembly *pComparator) |
39 | { |
40 | STATIC_CONTRACT_NOTHROW; |
41 | STATIC_CONTRACT_GC_TRIGGERS; |
42 | STATIC_CONTRACT_FORBID_FAULT; |
43 | |
44 | BOOL result = FALSE; |
45 | |
46 | EX_TRY |
47 | { |
48 | SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with. |
49 | result = (pComparator == pCache->LookupAssembly(pSpec)); |
50 | } |
51 | EX_CATCH |
52 | { |
53 | Exception *ex = GET_EXCEPTION(); |
54 | |
55 | result = ex->IsTransient(); |
56 | } |
57 | EX_END_CATCH(SwallowAllExceptions) |
58 | |
59 | return result; |
60 | |
61 | } |
62 | #endif |
63 | |
64 | #ifdef _DEBUG |
65 | // This debug-only wrapper for LookupFile is solely for the use of postconditions and |
66 | // assertions. The problem is that the real LookupFile can throw an OOM |
67 | // simply because it can't allocate scratch space. For the sake of asserting, |
68 | // we can treat those as successful lookups. |
69 | BOOL UnsafeVerifyLookupFile(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec, PEAssembly *pComparator) |
70 | { |
71 | STATIC_CONTRACT_NOTHROW; |
72 | STATIC_CONTRACT_GC_TRIGGERS; |
73 | STATIC_CONTRACT_FORBID_FAULT; |
74 | |
75 | BOOL result = FALSE; |
76 | |
77 | EX_TRY |
78 | { |
79 | SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with. |
80 | result = pCache->LookupFile(pSpec)->Equals(pComparator); |
81 | } |
82 | EX_CATCH |
83 | { |
84 | Exception *ex = GET_EXCEPTION(); |
85 | |
86 | result = ex->IsTransient(); |
87 | } |
88 | EX_END_CATCH(SwallowAllExceptions) |
89 | |
90 | return result; |
91 | |
92 | } |
93 | |
94 | #endif |
95 | |
96 | #ifdef _DEBUG |
97 | |
98 | // This debug-only wrapper for Contains is solely for the use of postconditions and |
99 | // assertions. The problem is that the real Contains can throw an OOM |
100 | // simply because it can't allocate scratch space. For the sake of asserting, |
101 | // we can treat those as successful lookups. |
102 | BOOL UnsafeContains(AssemblySpecBindingCache *pCache, AssemblySpec *pSpec) |
103 | { |
104 | STATIC_CONTRACT_NOTHROW; |
105 | STATIC_CONTRACT_GC_TRIGGERS; |
106 | STATIC_CONTRACT_FORBID_FAULT; |
107 | |
108 | BOOL result = FALSE; |
109 | |
110 | EX_TRY |
111 | { |
112 | SCAN_IGNORE_FAULT; // Won't go away: This wrapper exists precisely to turn an OOM here into something our postconditions can deal with. |
113 | result = pCache->Contains(pSpec); |
114 | } |
115 | EX_CATCH |
116 | { |
117 | Exception *ex = GET_EXCEPTION(); |
118 | |
119 | result = ex->IsTransient(); |
120 | } |
121 | EX_END_CATCH(SwallowAllExceptions) |
122 | |
123 | return result; |
124 | |
125 | } |
126 | #endif |
127 | |
128 | |
129 | |
130 | AssemblySpecHash::~AssemblySpecHash() |
131 | { |
132 | CONTRACTL |
133 | { |
134 | DESTRUCTOR_CHECK; |
135 | NOTHROW; |
136 | GC_TRIGGERS; |
137 | MODE_ANY; |
138 | } |
139 | CONTRACTL_END; |
140 | |
141 | PtrHashMap::PtrIterator i = m_map.begin(); |
142 | while (!i.end()) |
143 | { |
144 | AssemblySpec *s = (AssemblySpec*) i.GetValue(); |
145 | if (m_pHeap != NULL) |
146 | s->~AssemblySpec(); |
147 | else |
148 | delete s; |
149 | |
150 | ++i; |
151 | } |
152 | } |
153 | |
154 | // Check assembly name for invalid characters |
155 | // Return value: |
156 | // TRUE: If no invalid characters were found, or if the assembly name isn't set |
157 | // FALSE: If invalid characters were found |
158 | // This is needed to prevent security loopholes with ':', '/' and '\' in the assembly name |
159 | BOOL AssemblySpec::IsValidAssemblyName() |
160 | { |
161 | CONTRACTL |
162 | { |
163 | THROWS; |
164 | GC_TRIGGERS; |
165 | } |
166 | CONTRACTL_END; |
167 | |
168 | if (GetName()) |
169 | { |
170 | SString ssAssemblyName(SString::Utf8, GetName()); |
171 | for (SString::Iterator i = ssAssemblyName.Begin(); i[0] != W('\0'); i++) { |
172 | switch (i[0]) { |
173 | case W(':'): |
174 | case W('\\'): |
175 | case W('/'): |
176 | return FALSE; |
177 | |
178 | default: |
179 | break; |
180 | } |
181 | } |
182 | } |
183 | return TRUE; |
184 | } |
185 | |
186 | HRESULT AssemblySpec::InitializeSpecInternal(mdToken kAssemblyToken, |
187 | IMDInternalImport *pImport, |
188 | DomainAssembly *pStaticParent, |
189 | BOOL fAllowAllocation) |
190 | { |
191 | CONTRACTL |
192 | { |
193 | INSTANCE_CHECK; |
194 | if (fAllowAllocation) {GC_TRIGGERS;} else {GC_NOTRIGGER;}; |
195 | if (fAllowAllocation) {INJECT_FAULT(COMPlusThrowOM());} else {FORBID_FAULT;}; |
196 | NOTHROW; |
197 | MODE_ANY; |
198 | PRECONDITION(pImport->IsValidToken(kAssemblyToken)); |
199 | PRECONDITION(TypeFromToken(kAssemblyToken) == mdtAssembly |
200 | || TypeFromToken(kAssemblyToken) == mdtAssemblyRef); |
201 | } |
202 | CONTRACTL_END; |
203 | |
204 | HRESULT hr = S_OK; |
205 | |
206 | EX_TRY |
207 | { |
208 | IfFailThrow(BaseAssemblySpec::Init(kAssemblyToken,pImport)); |
209 | |
210 | if (IsContentType_WindowsRuntime()) |
211 | { |
212 | if (!fAllowAllocation) |
213 | { // We don't support this because we must be able to allocate in order to |
214 | // extract embedded type names for the native image scenario. Currently, |
215 | // the only caller of this method with fAllowAllocation == FALSE is |
216 | // Module::GetAssemblyIfLoaded, and since this method will only check the |
217 | // assembly spec cache, and since we can't cache WinRT assemblies, this |
218 | // limitation should have no negative impact. |
219 | IfFailThrow(E_FAIL); |
220 | } |
221 | |
222 | // Extract embedded content, if present (currently used for embedded WinRT type names). |
223 | ParseEncodedName(); |
224 | } |
225 | |
226 | // For static binds, we cannot reference a weakly named assembly from a strong named one. |
227 | // (Note that this constraint doesn't apply to dynamic binds which is why this check is |
228 | // not farther down the stack.) |
229 | if (pStaticParent != NULL) |
230 | { |
231 | // We dont validate this for CoreCLR as there is no good use-case for this scenario. |
232 | |
233 | SetParentAssembly(pStaticParent); |
234 | } |
235 | } |
236 | EX_CATCH_HRESULT(hr); |
237 | |
238 | return hr; |
239 | } // AssemblySpec::InitializeSpecInternal |
240 | |
241 | |
242 | |
243 | void AssemblySpec::InitializeSpec(PEAssembly * pFile) |
244 | { |
245 | CONTRACTL |
246 | { |
247 | INSTANCE_CHECK; |
248 | THROWS; |
249 | GC_TRIGGERS; |
250 | MODE_ANY; |
251 | PRECONDITION(CheckPointer(pFile)); |
252 | INJECT_FAULT(COMPlusThrowOM();); |
253 | } |
254 | CONTRACTL_END; |
255 | ReleaseHolder<IMDInternalImport> pImport(pFile->GetMDImportWithRef()); |
256 | mdAssembly a; |
257 | IfFailThrow(pImport->GetAssemblyFromScope(&a)); |
258 | |
259 | InitializeSpec(a, pImport, NULL); |
260 | |
261 | #ifdef FEATURE_COMINTEROP |
262 | if (IsContentType_WindowsRuntime()) |
263 | { |
264 | LPCSTR szNamespace; |
265 | LPCSTR szTypeName; |
266 | SString ssFakeNameSpaceAllocationBuffer; |
267 | IfFailThrow(::GetFirstWinRTTypeDef(pImport, &szNamespace, &szTypeName, pFile->GetPath(), &ssFakeNameSpaceAllocationBuffer)); |
268 | |
269 | SetWindowsRuntimeType(szNamespace, szTypeName); |
270 | |
271 | // pFile is not guaranteed to stay around (it might be unloaded with the AppDomain), we have to copy the type name |
272 | CloneFields(WINRT_TYPE_NAME_OWNED); |
273 | } |
274 | #endif //FEATURE_COMINTEROP |
275 | |
276 | // Set the binding context for the AssemblySpec |
277 | ICLRPrivBinder* pCurrentBinder = GetBindingContext(); |
278 | ICLRPrivBinder* pExpectedBinder = pFile->GetBindingContext(); |
279 | if (pCurrentBinder == NULL) |
280 | { |
281 | // We should aways having the binding context in the PEAssembly. The only exception to this are the following: |
282 | // |
283 | // 1) when we are here during EEStartup and loading mscorlib.dll. |
284 | // 2) We are dealing with dynamic assemblies |
285 | _ASSERTE((pExpectedBinder != NULL) || pFile->IsSystem() || pFile->IsDynamic()); |
286 | SetBindingContext(pExpectedBinder); |
287 | } |
288 | } |
289 | |
290 | #ifndef CROSSGEN_COMPILE |
291 | |
292 | // This uses thread storage to allocate space. Please use Checkpoint and release it. |
293 | HRESULT AssemblySpec::InitializeSpec(StackingAllocator* alloc, ASSEMBLYNAMEREF* pName, |
294 | BOOL fParse /*=TRUE*/) |
295 | { |
296 | CONTRACTL |
297 | { |
298 | INSTANCE_CHECK; |
299 | THROWS; |
300 | MODE_COOPERATIVE; |
301 | GC_TRIGGERS; |
302 | PRECONDITION(CheckPointer(alloc)); |
303 | PRECONDITION(CheckPointer(pName)); |
304 | PRECONDITION(IsProtectedByGCFrame(pName)); |
305 | INJECT_FAULT(COMPlusThrowOM();); |
306 | } |
307 | CONTRACTL_END; |
308 | |
309 | // Simple name |
310 | if ((*pName)->GetSimpleName() != NULL) { |
311 | WCHAR* pString; |
312 | int iString; |
313 | ((STRINGREF) (*pName)->GetSimpleName())->RefInterpretGetStringValuesDangerousForGC(&pString, &iString); |
314 | DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL); |
315 | if (lgth + 1 < lgth) |
316 | ThrowHR(E_INVALIDARG); |
317 | LPSTR lpName = (LPSTR) alloc->Alloc(S_UINT32(lgth) + S_UINT32(1)); |
318 | WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, |
319 | lpName, lgth+1, NULL, NULL); |
320 | lpName[lgth] = '\0'; |
321 | // Calling Init here will trash the cached lpName in AssemblySpec, but lpName is still needed by ParseName |
322 | // call below. |
323 | SetName(lpName); |
324 | } |
325 | else |
326 | { |
327 | // Ensure we always have an assembly simple name. |
328 | LPSTR lpName = (LPSTR) alloc->Alloc(S_UINT32(1)); |
329 | lpName[0] = '\0'; |
330 | SetName(lpName); |
331 | } |
332 | |
333 | if (fParse) { |
334 | HRESULT hr = ParseName(); |
335 | // Sometimes Fusion flags invalid characters in the name, sometimes it doesn't |
336 | // depending on where the invalid characters are |
337 | // We want to Raise the assembly resolve event on all invalid characters |
338 | // but calling ParseName before checking for invalid characters gives Fusion a chance to |
339 | // parse the rest of the name (to get a public key token, etc.) |
340 | if ((hr == FUSION_E_INVALID_NAME) || (!IsValidAssemblyName())) { |
341 | // This is the only case where we do not throw on an error |
342 | // We don't want to throw so as to give the caller a chance to call RaiseAssemblyResolveEvent |
343 | // The only caller that cares is System.Reflection.Assembly.InternalLoad which calls us through |
344 | // AssemblyNameNative::Init |
345 | return FUSION_E_INVALID_NAME; |
346 | } |
347 | else |
348 | IfFailThrow(hr); |
349 | } |
350 | else { |
351 | AssemblyMetaDataInternal asmInfo; |
352 | // Flags |
353 | DWORD dwFlags = (*pName)->GetFlags(); |
354 | |
355 | // Version |
356 | VERSIONREF version = (VERSIONREF) (*pName)->GetVersion(); |
357 | if(version == NULL) { |
358 | asmInfo.usMajorVersion = (USHORT)-1; |
359 | asmInfo.usMinorVersion = (USHORT)-1; |
360 | asmInfo.usBuildNumber = (USHORT)-1; |
361 | asmInfo.usRevisionNumber = (USHORT)-1; |
362 | } |
363 | else { |
364 | asmInfo.usMajorVersion = (USHORT)version->GetMajor(); |
365 | asmInfo.usMinorVersion = (USHORT)version->GetMinor(); |
366 | asmInfo.usBuildNumber = (USHORT)version->GetBuild(); |
367 | asmInfo.usRevisionNumber = (USHORT)version->GetRevision(); |
368 | } |
369 | |
370 | asmInfo.szLocale = 0; |
371 | asmInfo.ulOS = 0; |
372 | asmInfo.rOS = 0; |
373 | asmInfo.ulProcessor = 0; |
374 | asmInfo.rProcessor = 0; |
375 | |
376 | if ((*pName)->GetCultureInfo() != NULL) |
377 | { |
378 | struct _gc { |
379 | OBJECTREF cultureinfo; |
380 | STRINGREF pString; |
381 | } gc; |
382 | |
383 | gc.cultureinfo = (*pName)->GetCultureInfo(); |
384 | gc.pString = NULL; |
385 | |
386 | GCPROTECT_BEGIN(gc); |
387 | |
388 | MethodDescCallSite getName(METHOD__CULTURE_INFO__GET_NAME, &gc.cultureinfo); |
389 | |
390 | ARG_SLOT args[] = { |
391 | ObjToArgSlot(gc.cultureinfo) |
392 | }; |
393 | gc.pString = getName.Call_RetSTRINGREF(args); |
394 | if (gc.pString != NULL) { |
395 | WCHAR* pString; |
396 | int iString; |
397 | gc.pString->RefInterpretGetStringValuesDangerousForGC(&pString, &iString); |
398 | DWORD lgth = WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, NULL, 0, NULL, NULL); |
399 | S_UINT32 lengthWillNull = S_UINT32(lgth) + S_UINT32(1); |
400 | LPSTR lpLocale = (LPSTR) alloc->Alloc(lengthWillNull); |
401 | if (lengthWillNull.IsOverflow()) |
402 | { |
403 | COMPlusThrowHR(COR_E_OVERFLOW); |
404 | } |
405 | WszWideCharToMultiByte(CP_UTF8, 0, pString, iString, |
406 | lpLocale, lengthWillNull.Value(), NULL, NULL); |
407 | lpLocale[lgth] = '\0'; |
408 | asmInfo.szLocale = lpLocale; |
409 | } |
410 | GCPROTECT_END(); |
411 | } |
412 | |
413 | // Strong name |
414 | DWORD cbPublicKeyOrToken=0; |
415 | BYTE* pbPublicKeyOrToken=NULL; |
416 | // Note that we prefer to take a public key token if present, |
417 | // even if flags indicate a full public key |
418 | if ((*pName)->GetPublicKeyToken() != NULL) { |
419 | dwFlags &= ~afPublicKey; |
420 | PBYTE pArray = NULL; |
421 | pArray = (*pName)->GetPublicKeyToken()->GetDirectPointerToNonObjectElements(); |
422 | cbPublicKeyOrToken = (*pName)->GetPublicKeyToken()->GetNumComponents(); |
423 | pbPublicKeyOrToken = pArray; |
424 | } |
425 | else if ((*pName)->GetPublicKey() != NULL) { |
426 | dwFlags |= afPublicKey; |
427 | PBYTE pArray = NULL; |
428 | pArray = (*pName)->GetPublicKey()->GetDirectPointerToNonObjectElements(); |
429 | cbPublicKeyOrToken = (*pName)->GetPublicKey()->GetNumComponents(); |
430 | pbPublicKeyOrToken = pArray; |
431 | } |
432 | BaseAssemblySpec::Init(GetName(),&asmInfo,pbPublicKeyOrToken,cbPublicKeyOrToken,dwFlags); |
433 | } |
434 | |
435 | CloneFieldsToStackingAllocator(alloc); |
436 | |
437 | // Hash for control |
438 | // <TODO>@TODO cts, can we use unsafe in this case!!!</TODO> |
439 | if ((*pName)->GetHashForControl() != NULL) |
440 | SetHashForControl((*pName)->GetHashForControl()->GetDataPtr(), |
441 | (*pName)->GetHashForControl()->GetNumComponents(), |
442 | (*pName)->GetHashAlgorithmForControl()); |
443 | |
444 | // Extract embedded WinRT name, if present. |
445 | ParseEncodedName(); |
446 | |
447 | return S_OK; |
448 | } |
449 | |
450 | void AssemblySpec::AssemblyNameInit(ASSEMBLYNAMEREF* pAsmName, PEImage* pImageInfo) |
451 | { |
452 | CONTRACTL |
453 | { |
454 | THROWS; |
455 | MODE_COOPERATIVE; |
456 | GC_TRIGGERS; |
457 | SO_INTOLERANT; |
458 | PRECONDITION(IsProtectedByGCFrame (pAsmName)); |
459 | } |
460 | CONTRACTL_END; |
461 | |
462 | struct _gc { |
463 | OBJECTREF CultureInfo; |
464 | STRINGREF Locale; |
465 | OBJECTREF Version; |
466 | U1ARRAYREF PublicKeyOrToken; |
467 | STRINGREF Name; |
468 | STRINGREF CodeBase; |
469 | } gc; |
470 | ZeroMemory(&gc, sizeof(gc)); |
471 | |
472 | GCPROTECT_BEGIN(gc); |
473 | |
474 | if ((m_context.usMajorVersion != (USHORT) -1) && |
475 | (m_context.usMinorVersion != (USHORT) -1)) { |
476 | |
477 | MethodTable* pVersion = MscorlibBinder::GetClass(CLASS__VERSION); |
478 | |
479 | // version |
480 | gc.Version = AllocateObject(pVersion); |
481 | |
482 | // BaseAssemblySpec and AssemblyName properties store uint16 components for the version. Version and AssemblyVersion |
483 | // store int32 or uint32. When the former are initialized from the latter, the components are truncated to uint16 size. |
484 | // When the latter are initialized from the former, they are zero-extended to int32 size. For uint16 components, the max |
485 | // value is used to indicate an unspecified component. For int32 components, -1 is used. Since we're initializing a |
486 | // Version from an assembly version, map the uint16 unspecified value to the int32 size. |
487 | int componentCount = 2; |
488 | if (m_context.usBuildNumber != (USHORT)-1) |
489 | { |
490 | ++componentCount; |
491 | if (m_context.usRevisionNumber != (USHORT)-1) |
492 | { |
493 | ++componentCount; |
494 | } |
495 | } |
496 | switch (componentCount) |
497 | { |
498 | case 2: |
499 | { |
500 | // Call Version(int, int) because Version(int, int, int, int) does not allow passing the unspecified value -1 |
501 | MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix2); |
502 | ARG_SLOT VersionArgs[] = |
503 | { |
504 | ObjToArgSlot(gc.Version), |
505 | (ARG_SLOT) m_context.usMajorVersion, |
506 | (ARG_SLOT) m_context.usMinorVersion |
507 | }; |
508 | ctorMethod.Call(VersionArgs); |
509 | break; |
510 | } |
511 | |
512 | case 3: |
513 | { |
514 | // Call Version(int, int, int) because Version(int, int, int, int) does not allow passing the unspecified value -1 |
515 | MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix3); |
516 | ARG_SLOT VersionArgs[] = |
517 | { |
518 | ObjToArgSlot(gc.Version), |
519 | (ARG_SLOT) m_context.usMajorVersion, |
520 | (ARG_SLOT) m_context.usMinorVersion, |
521 | (ARG_SLOT) m_context.usBuildNumber |
522 | }; |
523 | ctorMethod.Call(VersionArgs); |
524 | break; |
525 | } |
526 | |
527 | default: |
528 | { |
529 | // Call Version(int, int, int, int) |
530 | _ASSERTE(componentCount == 4); |
531 | MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix4); |
532 | ARG_SLOT VersionArgs[] = |
533 | { |
534 | ObjToArgSlot(gc.Version), |
535 | (ARG_SLOT) m_context.usMajorVersion, |
536 | (ARG_SLOT) m_context.usMinorVersion, |
537 | (ARG_SLOT) m_context.usBuildNumber, |
538 | (ARG_SLOT) m_context.usRevisionNumber |
539 | }; |
540 | ctorMethod.Call(VersionArgs); |
541 | break; |
542 | } |
543 | } |
544 | } |
545 | |
546 | // cultureinfo |
547 | if (m_context.szLocale) { |
548 | |
549 | MethodTable* pCI = MscorlibBinder::GetClass(CLASS__CULTURE_INFO); |
550 | gc.CultureInfo = AllocateObject(pCI); |
551 | |
552 | gc.Locale = StringObject::NewString(m_context.szLocale); |
553 | |
554 | MethodDescCallSite strCtor(METHOD__CULTURE_INFO__STR_CTOR); |
555 | |
556 | ARG_SLOT args[2] = |
557 | { |
558 | ObjToArgSlot(gc.CultureInfo), |
559 | ObjToArgSlot(gc.Locale) |
560 | }; |
561 | |
562 | strCtor.Call(args); |
563 | } |
564 | |
565 | // public key or token byte array |
566 | if (m_pbPublicKeyOrToken) |
567 | { |
568 | gc.PublicKeyOrToken = (U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1, m_cbPublicKeyOrToken); |
569 | memcpyNoGCRefs(gc.PublicKeyOrToken->m_Array, m_pbPublicKeyOrToken, m_cbPublicKeyOrToken); |
570 | } |
571 | |
572 | // simple name |
573 | if(GetName()) |
574 | gc.Name = StringObject::NewString(GetName()); |
575 | |
576 | if (GetCodeBase()) |
577 | gc.CodeBase = StringObject::NewString(GetCodeBase()); |
578 | |
579 | BOOL fPublicKey = m_dwFlags & afPublicKey; |
580 | |
581 | ULONG hashAlgId=0; |
582 | if (pImageInfo != NULL) |
583 | { |
584 | if(!pImageInfo->GetMDImport()->IsValidToken(TokenFromRid(1, mdtAssembly))) |
585 | { |
586 | ThrowHR(COR_E_BADIMAGEFORMAT); |
587 | } |
588 | IfFailThrow(pImageInfo->GetMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, &hashAlgId, NULL, NULL, NULL)); |
589 | } |
590 | |
591 | MethodDescCallSite init(METHOD__ASSEMBLY_NAME__INIT); |
592 | |
593 | ARG_SLOT MethodArgs[] = |
594 | { |
595 | ObjToArgSlot(*pAsmName), |
596 | ObjToArgSlot(gc.Name), |
597 | fPublicKey ? ObjToArgSlot(gc.PublicKeyOrToken) : |
598 | (ARG_SLOT) NULL, // public key |
599 | fPublicKey ? (ARG_SLOT) NULL : |
600 | ObjToArgSlot(gc.PublicKeyOrToken), // public key token |
601 | ObjToArgSlot(gc.Version), |
602 | ObjToArgSlot(gc.CultureInfo), |
603 | (ARG_SLOT) hashAlgId, |
604 | (ARG_SLOT) 1, // AssemblyVersionCompatibility.SameMachine |
605 | ObjToArgSlot(gc.CodeBase), |
606 | (ARG_SLOT) m_dwFlags, |
607 | (ARG_SLOT) NULL // key pair |
608 | }; |
609 | |
610 | init.Call(MethodArgs); |
611 | |
612 | // Only set the processor architecture if we're looking at a newer binary that has |
613 | // that information in the PE, and we're not looking at a reference assembly. |
614 | if(pImageInfo && !pImageInfo->HasV1Metadata() && !pImageInfo->IsReferenceAssembly()) |
615 | { |
616 | DWORD dwMachine, dwKind; |
617 | |
618 | pImageInfo->GetPEKindAndMachine(&dwMachine,&dwKind); |
619 | |
620 | MethodDescCallSite setPA(METHOD__ASSEMBLY_NAME__SET_PROC_ARCH_INDEX); |
621 | |
622 | ARG_SLOT PAMethodArgs[] = { |
623 | ObjToArgSlot(*pAsmName), |
624 | (ARG_SLOT)dwMachine, |
625 | (ARG_SLOT)dwKind |
626 | }; |
627 | |
628 | setPA.Call(PAMethodArgs); |
629 | } |
630 | |
631 | GCPROTECT_END(); |
632 | } |
633 | |
634 | // This uses thread storage to allocate space. Please use Checkpoint and release it. |
635 | void AssemblySpec::SetCodeBase(StackingAllocator* alloc, STRINGREF *pCodeBase) |
636 | { |
637 | CONTRACTL |
638 | { |
639 | INSTANCE_CHECK; |
640 | THROWS; |
641 | GC_TRIGGERS; |
642 | MODE_COOPERATIVE; |
643 | PRECONDITION(CheckPointer(pCodeBase)); |
644 | INJECT_FAULT(COMPlusThrowOM();); |
645 | } |
646 | CONTRACTL_END; |
647 | |
648 | // Codebase |
649 | if (pCodeBase != NULL && *pCodeBase != NULL) { |
650 | WCHAR* pString; |
651 | int iString; |
652 | (*pCodeBase)->RefInterpretGetStringValuesDangerousForGC(&pString, &iString); |
653 | |
654 | DWORD dwCodeBase = (DWORD) iString+1; |
655 | m_wszCodeBase = new (alloc) WCHAR[dwCodeBase]; |
656 | memcpy((void*)m_wszCodeBase, pString, dwCodeBase * sizeof(WCHAR)); |
657 | } |
658 | } |
659 | |
660 | #endif // CROSSGEN_COMPILE |
661 | |
662 | |
663 | void AssemblySpec::MatchRetargetedPublicKeys(Assembly *pAssembly) |
664 | { |
665 | CONTRACTL |
666 | { |
667 | INSTANCE_CHECK; |
668 | THROWS; |
669 | GC_TRIGGERS; |
670 | MODE_ANY; |
671 | PRECONDITION(CheckPointer(pAssembly)); |
672 | } |
673 | CONTRACTL_END; |
674 | ThrowHR(FUSION_E_REF_DEF_MISMATCH); |
675 | } |
676 | |
677 | |
678 | // Check if the supplied assembly's public key matches up with the one in the Spec, if any |
679 | // Throws an appropriate exception in case of a mismatch |
680 | void AssemblySpec::MatchPublicKeys(Assembly *pAssembly) |
681 | { |
682 | CONTRACTL |
683 | { |
684 | INSTANCE_CHECK; |
685 | THROWS; |
686 | GC_TRIGGERS; |
687 | MODE_ANY; |
688 | } |
689 | CONTRACTL_END; |
690 | // Check that the public keys are the same as in the AR. |
691 | if (IsStrongNamed()) { |
692 | |
693 | const void *pbPublicKey; |
694 | DWORD cbPublicKey; |
695 | pbPublicKey = pAssembly->GetPublicKey(&cbPublicKey); |
696 | if (cbPublicKey == 0) |
697 | ThrowHR(FUSION_E_PRIVATE_ASM_DISALLOWED); |
698 | |
699 | if (m_dwFlags & afPublicKey) { |
700 | if ((m_cbPublicKeyOrToken != cbPublicKey) || |
701 | memcmp(m_pbPublicKeyOrToken, pbPublicKey, m_cbPublicKeyOrToken)) |
702 | return MatchRetargetedPublicKeys(pAssembly); |
703 | } |
704 | |
705 | // Ref has a token |
706 | else { |
707 | StrongNameBufferHolder<BYTE> pbStrongNameToken; |
708 | DWORD cbStrongNameToken; |
709 | |
710 | if (!StrongNameTokenFromPublicKey((BYTE*) pbPublicKey, |
711 | cbPublicKey, |
712 | &pbStrongNameToken, |
713 | &cbStrongNameToken)) |
714 | ThrowHR(StrongNameErrorInfo()); |
715 | if ((m_cbPublicKeyOrToken != cbStrongNameToken) || |
716 | memcmp(m_pbPublicKeyOrToken, |
717 | pbStrongNameToken, |
718 | cbStrongNameToken)) { |
719 | return MatchRetargetedPublicKeys(pAssembly); |
720 | } |
721 | } |
722 | } |
723 | } |
724 | |
725 | |
726 | PEAssembly *AssemblySpec::ResolveAssemblyFile(AppDomain *pDomain) |
727 | { |
728 | CONTRACT(PEAssembly *) |
729 | { |
730 | INSTANCE_CHECK; |
731 | THROWS; |
732 | GC_TRIGGERS; |
733 | MODE_ANY; |
734 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
735 | INJECT_FAULT(COMPlusThrowOM();); |
736 | } |
737 | CONTRACT_END; |
738 | |
739 | // No assembly resolve on codebase binds |
740 | if (GetName() == NULL) |
741 | RETURN NULL; |
742 | |
743 | Assembly *pAssembly = pDomain->RaiseAssemblyResolveEvent(this); |
744 | |
745 | if (pAssembly != NULL) { |
746 | PEAssembly *pFile = pAssembly->GetManifestFile(); |
747 | pFile->AddRef(); |
748 | |
749 | RETURN pFile; |
750 | } |
751 | |
752 | RETURN NULL; |
753 | } |
754 | |
755 | |
756 | Assembly *AssemblySpec::LoadAssembly(FileLoadLevel targetLevel, BOOL fThrowOnFileNotFound) |
757 | { |
758 | CONTRACTL |
759 | { |
760 | THROWS; |
761 | GC_TRIGGERS; |
762 | MODE_ANY; |
763 | } |
764 | CONTRACTL_END; |
765 | |
766 | DomainAssembly * pDomainAssembly = LoadDomainAssembly(targetLevel, fThrowOnFileNotFound); |
767 | if (pDomainAssembly == NULL) { |
768 | _ASSERTE(!fThrowOnFileNotFound); |
769 | return NULL; |
770 | } |
771 | return pDomainAssembly->GetAssembly(); |
772 | } |
773 | |
774 | // Returns a BOOL indicating if the two Binder references point to the same |
775 | // binder instance. |
776 | BOOL AreSameBinderInstance(ICLRPrivBinder *pBinderA, ICLRPrivBinder *pBinderB) |
777 | { |
778 | LIMITED_METHOD_CONTRACT; |
779 | |
780 | BOOL fIsSameInstance = (pBinderA == pBinderB); |
781 | |
782 | if (!fIsSameInstance && (pBinderA != NULL) && (pBinderB != NULL)) |
783 | { |
784 | // Get the ID for the first binder |
785 | UINT_PTR binderIDA = 0, binderIDB = 0; |
786 | HRESULT hr = pBinderA->GetBinderID(&binderIDA); |
787 | if (SUCCEEDED(hr)) |
788 | { |
789 | // Get the ID for the second binder |
790 | hr = pBinderB->GetBinderID(&binderIDB); |
791 | if (SUCCEEDED(hr)) |
792 | { |
793 | fIsSameInstance = (binderIDA == binderIDB); |
794 | } |
795 | } |
796 | } |
797 | |
798 | return fIsSameInstance; |
799 | } |
800 | |
801 | ICLRPrivBinder* AssemblySpec::GetBindingContextFromParentAssembly(AppDomain *pDomain) |
802 | { |
803 | CONTRACTL |
804 | { |
805 | NOTHROW; |
806 | GC_NOTRIGGER; |
807 | MODE_ANY; |
808 | PRECONDITION(pDomain != NULL); |
809 | } |
810 | CONTRACTL_END; |
811 | |
812 | ICLRPrivBinder *pParentAssemblyBinder = NULL; |
813 | DomainAssembly *pParentDomainAssembly = GetParentAssembly(); |
814 | |
815 | if(pParentDomainAssembly != NULL) |
816 | { |
817 | // Get the PEAssembly associated with the parent's domain assembly |
818 | PEAssembly *pParentPEAssembly = pParentDomainAssembly->GetFile(); |
819 | |
820 | // ICLRPrivAssembly implements ICLRPrivBinder and thus, "is a" binder in a manner of semantics. |
821 | pParentAssemblyBinder = pParentPEAssembly->GetBindingContext(); |
822 | } |
823 | |
824 | if (GetPreferFallbackLoadContextBinder()) |
825 | { |
826 | // If we have been asked to use the fallback load context binder (currently only supported for AssemblyLoadContext.LoadFromAssemblyName), |
827 | // then pretend we do not have any binder yet available. |
828 | _ASSERTE(GetFallbackLoadContextBinderForRequestingAssembly() != NULL); |
829 | pParentAssemblyBinder = NULL; |
830 | } |
831 | |
832 | if (pParentAssemblyBinder == NULL) |
833 | { |
834 | // If the parent assembly binder is not available, then we maybe dealing with one of the following |
835 | // assembly scenarios: |
836 | // |
837 | // 1) Domain Neutral assembly |
838 | // 2) Entrypoint assembly |
839 | // 3) AssemblyLoadContext.LoadFromAssemblyName |
840 | // |
841 | // For (1) and (2), we will need to bind against the DefaultContext binder (aka TPA Binder). This happens |
842 | // below if we do not find the parent assembly binder. |
843 | // |
844 | // For (3), fetch the fallback load context binder reference. |
845 | |
846 | pParentAssemblyBinder = GetFallbackLoadContextBinderForRequestingAssembly(); |
847 | } |
848 | |
849 | if (pParentAssemblyBinder != NULL) |
850 | { |
851 | CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext(); |
852 | if (AreSameBinderInstance(pTPABinder, pParentAssemblyBinder)) |
853 | { |
854 | // If the parent assembly is a platform (TPA) assembly, then its binding context will always be the TPABinder context. In |
855 | // such case, we will return the default context for binding to allow the bind to go |
856 | // via the custom binder context, if it was overridden. If it was not overridden, then we will get the expected |
857 | // TPABinder context anyways. |
858 | // |
859 | // Get the reference to the default binding context (this could be the TPABinder context or custom AssemblyLoadContext) |
860 | pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext()); |
861 | } |
862 | } |
863 | |
864 | #if defined(FEATURE_COMINTEROP) |
865 | if (!IsContentType_WindowsRuntime() && (pParentAssemblyBinder != NULL)) |
866 | { |
867 | CLRPrivBinderWinRT *pWinRTBinder = pDomain->GetWinRtBinder(); |
868 | if (AreSameBinderInstance(pWinRTBinder, pParentAssemblyBinder)) |
869 | { |
870 | // We could be here when a non-WinRT assembly load is triggerred by a winmd (e.g. System.Runtime being loaded due to |
871 | // types being referenced from Windows.Foundation.Winmd). |
872 | // |
873 | // If the AssemblySpec does not correspond to WinRT type but our parent assembly binder is a WinRT binder, |
874 | // then such an assembly will not be found by the binder. In such a case, we reset our binder reference. |
875 | pParentAssemblyBinder = NULL; |
876 | } |
877 | } |
878 | #endif // defined(FEATURE_COMINTEROP) |
879 | |
880 | if (!pParentAssemblyBinder) |
881 | { |
882 | // We can be here when loading assemblies via the host (e.g. ICLRRuntimeHost2::ExecuteAssembly) or dealing with assemblies |
883 | // whose parent is a domain neutral assembly (see comment above for details). |
884 | // |
885 | // In such a case, the parent assembly (semantically) is CoreLibrary and thus, the default binding context should be |
886 | // used as the parent assembly binder. |
887 | pParentAssemblyBinder = static_cast<ICLRPrivBinder*>(pDomain->GetFusionContext()); |
888 | } |
889 | |
890 | return pParentAssemblyBinder; |
891 | } |
892 | |
893 | DomainAssembly *AssemblySpec::LoadDomainAssembly(FileLoadLevel targetLevel, |
894 | BOOL fThrowOnFileNotFound) |
895 | { |
896 | CONTRACT(DomainAssembly *) |
897 | { |
898 | INSTANCE_CHECK; |
899 | THROWS; |
900 | GC_TRIGGERS; |
901 | MODE_ANY; |
902 | POSTCONDITION((!fThrowOnFileNotFound && CheckPointer(RETVAL, NULL_OK)) |
903 | || CheckPointer(RETVAL)); |
904 | INJECT_FAULT(COMPlusThrowOM();); |
905 | } |
906 | CONTRACT_END; |
907 | |
908 | ETWOnStartup (LoaderCatchCall_V1, LoaderCatchCallEnd_V1); |
909 | AppDomain* pDomain = GetAppDomain(); |
910 | |
911 | |
912 | DomainAssembly *pAssembly = nullptr; |
913 | |
914 | ICLRPrivBinder * pBinder = GetHostBinder(); |
915 | |
916 | // If no binder was explicitly set, check if parent assembly has a binder. |
917 | if (pBinder == nullptr) |
918 | { |
919 | pBinder = GetBindingContextFromParentAssembly(pDomain); |
920 | } |
921 | |
922 | if ((pAssembly == nullptr) && CanUseWithBindingCache()) |
923 | { |
924 | pAssembly = pDomain->FindCachedAssembly(this); |
925 | } |
926 | |
927 | if (pAssembly) |
928 | { |
929 | pDomain->LoadDomainFile(pAssembly, targetLevel); |
930 | RETURN pAssembly; |
931 | } |
932 | |
933 | |
934 | PEAssemblyHolder pFile(pDomain->BindAssemblySpec(this, fThrowOnFileNotFound)); |
935 | if (pFile == NULL) |
936 | RETURN NULL; |
937 | |
938 | pAssembly = pDomain->LoadDomainAssembly(this, pFile, targetLevel); |
939 | |
940 | RETURN pAssembly; |
941 | } |
942 | |
943 | /* static */ |
944 | Assembly *AssemblySpec::LoadAssembly(LPCSTR pSimpleName, |
945 | AssemblyMetaDataInternal* pContext, |
946 | const BYTE * pbPublicKeyOrToken, |
947 | DWORD cbPublicKeyOrToken, |
948 | DWORD dwFlags) |
949 | { |
950 | CONTRACT(Assembly *) |
951 | { |
952 | THROWS; |
953 | GC_TRIGGERS; |
954 | MODE_ANY; |
955 | PRECONDITION(CheckPointer(pSimpleName)); |
956 | POSTCONDITION(CheckPointer(RETVAL)); |
957 | INJECT_FAULT(COMPlusThrowOM();); |
958 | } |
959 | CONTRACT_END; |
960 | |
961 | AssemblySpec spec; |
962 | IfFailThrow(spec.Init(pSimpleName, pContext, |
963 | pbPublicKeyOrToken, cbPublicKeyOrToken, dwFlags)); |
964 | |
965 | RETURN spec.LoadAssembly(FILE_LOADED); |
966 | } |
967 | |
968 | /* static */ |
969 | Assembly *AssemblySpec::LoadAssembly(LPCWSTR pFilePath) |
970 | { |
971 | CONTRACT(Assembly *) |
972 | { |
973 | THROWS; |
974 | GC_TRIGGERS; |
975 | MODE_ANY; |
976 | PRECONDITION(CheckPointer(pFilePath)); |
977 | POSTCONDITION(CheckPointer(RETVAL)); |
978 | INJECT_FAULT(COMPlusThrowOM();); |
979 | } |
980 | CONTRACT_END; |
981 | |
982 | AssemblySpec spec; |
983 | spec.SetCodeBase(pFilePath); |
984 | RETURN spec.LoadAssembly(FILE_LOADED); |
985 | } |
986 | |
987 | HRESULT AssemblySpec::CheckFriendAssemblyName() |
988 | { |
989 | WRAPPER_NO_CONTRACT; |
990 | |
991 | // Version, Culture, Architecture, and publickeytoken are not permitted |
992 | if ((m_context.usMajorVersion != (USHORT) -1) || |
993 | (m_context.szLocale != NULL) || |
994 | (IsAfPA_Specified(m_dwFlags)) || |
995 | (IsStrongNamed() && !HasPublicKey())) |
996 | { |
997 | return META_E_CA_BAD_FRIENDS_ARGS; |
998 | } |
999 | else |
1000 | { |
1001 | return S_OK; |
1002 | } |
1003 | } |
1004 | |
1005 | HRESULT AssemblySpec::EmitToken( |
1006 | IMetaDataAssemblyEmit *pEmit, |
1007 | mdAssemblyRef *pToken, |
1008 | BOOL fUsePublicKeyToken, /*=TRUE*/ |
1009 | BOOL fMustBeBindable /*=FALSE*/) |
1010 | { |
1011 | CONTRACTL |
1012 | { |
1013 | INSTANCE_CHECK; |
1014 | MODE_ANY; |
1015 | NOTHROW; |
1016 | GC_NOTRIGGER; |
1017 | INJECT_FAULT(return E_OUTOFMEMORY;); |
1018 | } |
1019 | CONTRACTL_END; |
1020 | |
1021 | HRESULT hr = S_OK; |
1022 | |
1023 | EX_TRY |
1024 | { |
1025 | SmallStackSString ssName; |
1026 | fMustBeBindable ? GetEncodedName(ssName) : GetName(ssName); |
1027 | |
1028 | ASSEMBLYMETADATA AMD; |
1029 | |
1030 | AMD.usMajorVersion = m_context.usMajorVersion; |
1031 | AMD.usMinorVersion = m_context.usMinorVersion; |
1032 | AMD.usBuildNumber = m_context.usBuildNumber; |
1033 | AMD.usRevisionNumber = m_context.usRevisionNumber; |
1034 | |
1035 | if (m_context.szLocale) { |
1036 | AMD.cbLocale = MultiByteToWideChar(CP_UTF8, 0, m_context.szLocale, -1, NULL, 0); |
1037 | if(AMD.cbLocale==0) |
1038 | IfFailGo(HRESULT_FROM_GetLastError()); |
1039 | AMD.szLocale = (LPWSTR) alloca(AMD.cbLocale * sizeof(WCHAR) ); |
1040 | if(MultiByteToWideChar(CP_UTF8, 0, m_context.szLocale, -1, AMD.szLocale, AMD.cbLocale)==0) |
1041 | IfFailGo(HRESULT_FROM_GetLastError()); |
1042 | } |
1043 | else { |
1044 | AMD.cbLocale = 0; |
1045 | AMD.szLocale = NULL; |
1046 | } |
1047 | |
1048 | // If we've been asked to emit a public key token in the reference but we've |
1049 | // been given a public key then we need to generate the token now. |
1050 | if (m_cbPublicKeyOrToken && fUsePublicKeyToken && IsAfPublicKey(m_dwFlags)) { |
1051 | StrongNameBufferHolder<BYTE> pbPublicKeyToken; |
1052 | DWORD cbPublicKeyToken; |
1053 | if (!StrongNameTokenFromPublicKey(m_pbPublicKeyOrToken, |
1054 | m_cbPublicKeyOrToken, |
1055 | &pbPublicKeyToken, |
1056 | &cbPublicKeyToken)) { |
1057 | IfFailGo(StrongNameErrorInfo()); |
1058 | } |
1059 | |
1060 | hr = pEmit->DefineAssemblyRef(pbPublicKeyToken, |
1061 | cbPublicKeyToken, |
1062 | ssName.GetUnicode(), |
1063 | &AMD, |
1064 | NULL, |
1065 | 0, |
1066 | m_dwFlags & ~afPublicKey, |
1067 | pToken); |
1068 | } |
1069 | else { |
1070 | hr = pEmit->DefineAssemblyRef(m_pbPublicKeyOrToken, |
1071 | m_cbPublicKeyOrToken, |
1072 | ssName.GetUnicode(), |
1073 | &AMD, |
1074 | NULL, |
1075 | 0, |
1076 | m_dwFlags, |
1077 | pToken); |
1078 | } |
1079 | |
1080 | hr = S_OK; |
1081 | ErrExit: |
1082 | ; |
1083 | } |
1084 | EX_CATCH_HRESULT(hr); |
1085 | |
1086 | return hr; |
1087 | } |
1088 | |
1089 | //=========================================================================================== |
1090 | // Constructs an AssemblySpec for the given IAssemblyName. Recognizes IAssemblyName objects |
1091 | // that were built from WinRT AssemblySpec objects, extracts the encoded type name, and sets |
1092 | // the type namespace and class name properties appropriately. |
1093 | |
1094 | void AssemblySpec::ParseEncodedName() |
1095 | { |
1096 | CONTRACTL { |
1097 | THROWS; |
1098 | GC_NOTRIGGER; |
1099 | MODE_ANY; |
1100 | } CONTRACTL_END |
1101 | |
1102 | #ifdef FEATURE_COMINTEROP |
1103 | if (IsContentType_WindowsRuntime()) |
1104 | { |
1105 | StackSString ssEncodedName(SString::Utf8, m_pAssemblyName); |
1106 | ssEncodedName.Normalize(); |
1107 | |
1108 | SString::Iterator itBang = ssEncodedName.Begin(); |
1109 | if (ssEncodedName.Find(itBang, SL(W("!" )))) |
1110 | { |
1111 | StackSString ssAssemblyName(ssEncodedName, ssEncodedName.Begin(), itBang - ssEncodedName.Begin()); |
1112 | StackSString ssTypeName(ssEncodedName, ++itBang, ssEncodedName.End() - itBang); |
1113 | SetName(ssAssemblyName); |
1114 | SetWindowsRuntimeType(ssTypeName); |
1115 | } |
1116 | } |
1117 | #endif |
1118 | } |
1119 | |
1120 | void AssemblySpec::SetWindowsRuntimeType( |
1121 | LPCUTF8 szNamespace, |
1122 | LPCUTF8 szClassName) |
1123 | { |
1124 | CONTRACTL |
1125 | { |
1126 | THROWS; |
1127 | GC_NOTRIGGER; |
1128 | MODE_ANY; |
1129 | } |
1130 | CONTRACTL_END; |
1131 | #ifdef FEATURE_COMINTEROP |
1132 | // Release already allocated string |
1133 | if (m_ownedFlags & WINRT_TYPE_NAME_OWNED) |
1134 | { |
1135 | if (m_szWinRtTypeNamespace != nullptr) |
1136 | delete [] m_szWinRtTypeNamespace; |
1137 | if (m_szWinRtTypeClassName != nullptr) |
1138 | delete [] m_szWinRtTypeClassName; |
1139 | } |
1140 | m_szWinRtTypeNamespace = szNamespace; |
1141 | m_szWinRtTypeClassName = szClassName; |
1142 | |
1143 | m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED; |
1144 | #else |
1145 | // Classic (non-phone) CoreCLR does not support WinRT interop; this should never be called with a non-empty type name |
1146 | _ASSERTE((szNamespace == NULL) && (szClassName == NULL)); |
1147 | #endif |
1148 | } |
1149 | |
1150 | void AssemblySpec::SetWindowsRuntimeType( |
1151 | SString const & _ssTypeName) |
1152 | { |
1153 | CONTRACTL |
1154 | { |
1155 | THROWS; |
1156 | GC_NOTRIGGER; |
1157 | MODE_ANY; |
1158 | } |
1159 | CONTRACTL_END; |
1160 | |
1161 | // Release already allocated string |
1162 | if (m_ownedFlags & WINRT_TYPE_NAME_OWNED) |
1163 | { |
1164 | if (m_szWinRtTypeNamespace != nullptr) |
1165 | delete[] m_szWinRtTypeNamespace; |
1166 | if (m_szWinRtTypeClassName != nullptr) |
1167 | delete[] m_szWinRtTypeClassName; |
1168 | m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED; |
1169 | } |
1170 | |
1171 | SString ssTypeName; |
1172 | _ssTypeName.ConvertToUTF8(ssTypeName); |
1173 | |
1174 | LPUTF8 szTypeName = (LPUTF8)ssTypeName.GetUTF8NoConvert(); |
1175 | ns::SplitInline(szTypeName, m_szWinRtTypeNamespace, m_szWinRtTypeClassName); |
1176 | m_ownedFlags &= ~WINRT_TYPE_NAME_OWNED; |
1177 | // Make a copy of the type name strings |
1178 | CloneFields(WINRT_TYPE_NAME_OWNED); |
1179 | } |
1180 | |
1181 | |
1182 | AssemblySpecBindingCache::AssemblySpecBindingCache() |
1183 | { |
1184 | LIMITED_METHOD_CONTRACT; |
1185 | } |
1186 | |
1187 | AssemblySpecBindingCache::~AssemblySpecBindingCache() |
1188 | { |
1189 | CONTRACTL |
1190 | { |
1191 | DESTRUCTOR_CHECK; |
1192 | NOTHROW; |
1193 | GC_TRIGGERS; |
1194 | MODE_ANY; |
1195 | } |
1196 | CONTRACTL_END; |
1197 | |
1198 | Clear(); |
1199 | } |
1200 | |
1201 | void AssemblySpecBindingCache::Clear() |
1202 | { |
1203 | CONTRACTL |
1204 | { |
1205 | DESTRUCTOR_CHECK; |
1206 | NOTHROW; |
1207 | GC_TRIGGERS; |
1208 | MODE_ANY; |
1209 | } |
1210 | CONTRACTL_END; |
1211 | |
1212 | PtrHashMap::PtrIterator i = m_map.begin(); |
1213 | while (!i.end()) |
1214 | { |
1215 | AssemblyBinding *b = (AssemblyBinding*) i.GetValue(); |
1216 | if (m_pHeap == NULL) |
1217 | delete b; |
1218 | else |
1219 | b->~AssemblyBinding(); |
1220 | |
1221 | ++i; |
1222 | } |
1223 | |
1224 | m_map.Clear(); |
1225 | } |
1226 | |
1227 | void AssemblySpecBindingCache::OnAppDomainUnload() |
1228 | { |
1229 | CONTRACTL |
1230 | { |
1231 | DESTRUCTOR_CHECK; |
1232 | NOTHROW; |
1233 | GC_TRIGGERS; |
1234 | MODE_ANY; |
1235 | } |
1236 | CONTRACTL_END; |
1237 | |
1238 | PtrHashMap::PtrIterator i = m_map.begin(); |
1239 | while (!i.end()) |
1240 | { |
1241 | AssemblyBinding *b = (AssemblyBinding*) i.GetValue(); |
1242 | b->OnAppDomainUnload(); |
1243 | |
1244 | ++i; |
1245 | } |
1246 | } |
1247 | |
1248 | void AssemblySpecBindingCache::Init(CrstBase *pCrst, LoaderHeap *pHeap) |
1249 | { |
1250 | WRAPPER_NO_CONTRACT; |
1251 | |
1252 | LockOwner lock = {pCrst, IsOwnerOfCrst}; |
1253 | m_map.Init(INITIAL_ASM_SPEC_HASH_SIZE, CompareSpecs, TRUE, &lock); |
1254 | m_pHeap = pHeap; |
1255 | } |
1256 | |
1257 | AssemblySpecBindingCache::AssemblyBinding* AssemblySpecBindingCache::LookupInternal(AssemblySpec* pSpec, BOOL fThrow) |
1258 | { |
1259 | CONTRACTL |
1260 | { |
1261 | if (fThrow) |
1262 | { |
1263 | THROWS; |
1264 | GC_TRIGGERS; |
1265 | INJECT_FAULT(COMPlusThrowOM();); |
1266 | } |
1267 | else |
1268 | { |
1269 | GC_NOTRIGGER; |
1270 | NOTHROW; |
1271 | FORBID_FAULT; |
1272 | } |
1273 | MODE_ANY; |
1274 | PRECONDITION(pSpec != NULL); |
1275 | } |
1276 | CONTRACTL_END; |
1277 | |
1278 | UPTR key = (UPTR)pSpec->Hash(); |
1279 | UPTR lookupKey = key; |
1280 | |
1281 | // On CoreCLR, we will use the BinderID as the key |
1282 | ICLRPrivBinder *pBinderContextForLookup = NULL; |
1283 | AppDomain *pSpecDomain = pSpec->GetAppDomain(); |
1284 | bool fGetBindingContextFromParent = true; |
1285 | |
1286 | // Check if the AssemblySpec already has specified its binding context. This will be set for assemblies that are |
1287 | // attempted to be explicitly bound using AssemblyLoadContext LoadFrom* methods. |
1288 | if(!pSpec->IsAssemblySpecForMscorlib()) |
1289 | pBinderContextForLookup = pSpec->GetBindingContext(); |
1290 | else |
1291 | { |
1292 | // For System.Private.Corelib Binding context is either not set or if set then it should be TPA |
1293 | _ASSERTE(pSpec->GetBindingContext() == NULL || pSpec->GetBindingContext() == pSpecDomain->GetFusionContext()); |
1294 | } |
1295 | |
1296 | if (pBinderContextForLookup != NULL) |
1297 | { |
1298 | // We are working with the actual binding context in which the assembly was expected to be loaded. |
1299 | // Thus, we don't need to get it from the parent assembly. |
1300 | fGetBindingContextFromParent = false; |
1301 | } |
1302 | |
1303 | if (fGetBindingContextFromParent) |
1304 | { |
1305 | // MScorlib does not have a binding context associated with it and its lookup will only be done |
1306 | // using its AssemblySpec hash. |
1307 | if (!pSpec->IsAssemblySpecForMscorlib()) |
1308 | { |
1309 | pBinderContextForLookup = pSpec->GetBindingContextFromParentAssembly(pSpecDomain); |
1310 | pSpec->SetBindingContext(pBinderContextForLookup); |
1311 | } |
1312 | } |
1313 | |
1314 | if (pBinderContextForLookup) |
1315 | { |
1316 | UINT_PTR binderID = 0; |
1317 | HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID); |
1318 | _ASSERTE(SUCCEEDED(hr)); |
1319 | lookupKey = key^binderID; |
1320 | } |
1321 | |
1322 | AssemblyBinding* pEntry = (AssemblyBinding *)m_map.LookupValue(lookupKey, pSpec); |
1323 | |
1324 | // Reset the binding context if one was originally never present in the AssemblySpec and we didnt find any entry |
1325 | // in the cache. |
1326 | if (fGetBindingContextFromParent) |
1327 | { |
1328 | if (pEntry == (AssemblyBinding *) INVALIDENTRY) |
1329 | { |
1330 | pSpec->SetBindingContext(NULL); |
1331 | } |
1332 | } |
1333 | |
1334 | return pEntry; |
1335 | } |
1336 | |
1337 | BOOL AssemblySpecBindingCache::Contains(AssemblySpec *pSpec) |
1338 | { |
1339 | WRAPPER_NO_CONTRACT; |
1340 | return (LookupInternal(pSpec, TRUE) != (AssemblyBinding *) INVALIDENTRY); |
1341 | } |
1342 | |
1343 | DomainAssembly *AssemblySpecBindingCache::LookupAssembly(AssemblySpec *pSpec, |
1344 | BOOL fThrow /*=TRUE*/) |
1345 | { |
1346 | CONTRACT(DomainAssembly *) |
1347 | { |
1348 | INSTANCE_CHECK; |
1349 | if (fThrow) { |
1350 | GC_TRIGGERS; |
1351 | THROWS; |
1352 | INJECT_FAULT(COMPlusThrowOM();); |
1353 | } |
1354 | else { |
1355 | GC_NOTRIGGER; |
1356 | NOTHROW; |
1357 | FORBID_FAULT; |
1358 | } |
1359 | MODE_ANY; |
1360 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
1361 | } |
1362 | CONTRACT_END; |
1363 | |
1364 | AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY; |
1365 | |
1366 | entry = LookupInternal(pSpec, fThrow); |
1367 | |
1368 | if (entry == (AssemblyBinding *) INVALIDENTRY) |
1369 | RETURN NULL; |
1370 | else |
1371 | { |
1372 | if ((entry->GetAssembly() == NULL) && fThrow) |
1373 | { |
1374 | // May be either unloaded, or an exception occurred. |
1375 | entry->ThrowIfError(); |
1376 | } |
1377 | |
1378 | RETURN entry->GetAssembly(); |
1379 | } |
1380 | } |
1381 | |
1382 | PEAssembly *AssemblySpecBindingCache::LookupFile(AssemblySpec *pSpec, BOOL fThrow /*=TRUE*/) |
1383 | { |
1384 | CONTRACT(PEAssembly *) |
1385 | { |
1386 | INSTANCE_CHECK; |
1387 | if (fThrow) { |
1388 | GC_TRIGGERS; |
1389 | THROWS; |
1390 | INJECT_FAULT(COMPlusThrowOM();); |
1391 | } |
1392 | else { |
1393 | GC_NOTRIGGER; |
1394 | NOTHROW; |
1395 | FORBID_FAULT; |
1396 | } |
1397 | MODE_ANY; |
1398 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
1399 | } |
1400 | CONTRACT_END; |
1401 | |
1402 | AssemblyBinding *entry = (AssemblyBinding *) INVALIDENTRY; |
1403 | entry = LookupInternal(pSpec, fThrow); |
1404 | |
1405 | if (entry == (AssemblyBinding *) INVALIDENTRY) |
1406 | RETURN NULL; |
1407 | else |
1408 | { |
1409 | if (fThrow && (entry->GetFile() == NULL)) |
1410 | { |
1411 | CONSISTENCY_CHECK(entry->IsError()); |
1412 | entry->ThrowIfError(); |
1413 | } |
1414 | |
1415 | RETURN entry->GetFile(); |
1416 | } |
1417 | } |
1418 | |
1419 | |
1420 | class AssemblyBindingHolder |
1421 | { |
1422 | public: |
1423 | AssemblyBindingHolder() |
1424 | { |
1425 | LIMITED_METHOD_CONTRACT; |
1426 | m_entry = NULL; |
1427 | m_pHeap = NULL; |
1428 | } |
1429 | |
1430 | AssemblySpecBindingCache::AssemblyBinding *CreateAssemblyBinding(LoaderHeap *pHeap) |
1431 | { |
1432 | CONTRACTL |
1433 | { |
1434 | THROWS; |
1435 | GC_TRIGGERS; |
1436 | INJECT_FAULT(COMPlusThrowOM();); |
1437 | } |
1438 | CONTRACTL_END |
1439 | |
1440 | m_pHeap = pHeap; |
1441 | if (pHeap) |
1442 | { |
1443 | m_entry = new (m_amTracker.Track(pHeap->AllocMem(S_SIZE_T(sizeof(AssemblySpecBindingCache::AssemblyBinding))))) AssemblySpecBindingCache::AssemblyBinding; |
1444 | } |
1445 | else |
1446 | { |
1447 | m_entry = new AssemblySpecBindingCache::AssemblyBinding; |
1448 | } |
1449 | return m_entry; |
1450 | } |
1451 | |
1452 | ~AssemblyBindingHolder() |
1453 | { |
1454 | CONTRACTL |
1455 | { |
1456 | NOTHROW; |
1457 | GC_TRIGGERS; |
1458 | FORBID_FAULT; |
1459 | } |
1460 | CONTRACTL_END |
1461 | |
1462 | if (m_entry) |
1463 | { |
1464 | if (m_pHeap) |
1465 | { |
1466 | // just call destructor - m_amTracker will delete the memory for m_entry itself. |
1467 | m_entry->~AssemblyBinding(); |
1468 | } |
1469 | else |
1470 | { |
1471 | delete m_entry; |
1472 | } |
1473 | } |
1474 | } |
1475 | |
1476 | void SuppressRelease() |
1477 | { |
1478 | LIMITED_METHOD_CONTRACT; |
1479 | m_entry = NULL; |
1480 | m_pHeap = NULL; |
1481 | m_amTracker.SuppressRelease(); |
1482 | } |
1483 | |
1484 | AllocMemTracker *GetPamTracker() |
1485 | { |
1486 | LIMITED_METHOD_CONTRACT; |
1487 | return &m_amTracker; |
1488 | } |
1489 | |
1490 | |
1491 | |
1492 | private: |
1493 | AssemblySpecBindingCache::AssemblyBinding *m_entry; |
1494 | LoaderHeap *m_pHeap; |
1495 | AllocMemTracker m_amTracker; |
1496 | }; |
1497 | |
1498 | // NOTE ABOUT STATE OF CACHE ENTRIES: |
1499 | // |
1500 | // A cache entry can be in one of 4 states: |
1501 | // 1. Empty (no entry) |
1502 | // 2. File (a PEAssembly has been bound, but not yet an Assembly) |
1503 | // 3. Assembly (Both a PEAssembly & Assembly are available.) |
1504 | // 4. Error (an error has occurred) |
1505 | // |
1506 | // The legal state transitions are: |
1507 | // 1 -> any |
1508 | // 2 -> 3 |
1509 | // 2 -> 4 |
1510 | |
1511 | |
1512 | BOOL AssemblySpecBindingCache::StoreAssembly(AssemblySpec *pSpec, DomainAssembly *pAssembly) |
1513 | { |
1514 | CONTRACT(BOOL) |
1515 | { |
1516 | INSTANCE_CHECK; |
1517 | THROWS; |
1518 | GC_TRIGGERS; |
1519 | MODE_ANY; |
1520 | // Host binder based assembly spec's cannot currently be safely inserted into caches. |
1521 | PRECONDITION(pSpec->GetHostBinder() == nullptr); |
1522 | POSTCONDITION(UnsafeContains(this, pSpec)); |
1523 | POSTCONDITION(UnsafeVerifyLookupAssembly(this, pSpec, pAssembly)); |
1524 | INJECT_FAULT(COMPlusThrowOM();); |
1525 | } |
1526 | CONTRACT_END; |
1527 | |
1528 | UPTR key = (UPTR)pSpec->Hash(); |
1529 | |
1530 | // On CoreCLR, we will use the BinderID as the key |
1531 | ICLRPrivBinder* pBinderContextForLookup = pAssembly->GetFile()->GetBindingContext(); |
1532 | |
1533 | _ASSERTE(pBinderContextForLookup || pAssembly->GetFile()->IsSystem()); |
1534 | if (pBinderContextForLookup) |
1535 | { |
1536 | UINT_PTR binderID = 0; |
1537 | HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID); |
1538 | _ASSERTE(SUCCEEDED(hr)); |
1539 | key = key^binderID; |
1540 | |
1541 | if (!pSpec->GetBindingContext()) |
1542 | { |
1543 | pSpec->SetBindingContext(pBinderContextForLookup); |
1544 | } |
1545 | } |
1546 | |
1547 | AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec); |
1548 | |
1549 | if (entry == (AssemblyBinding *) INVALIDENTRY) |
1550 | { |
1551 | AssemblyBindingHolder abHolder; |
1552 | |
1553 | LoaderHeap* pHeap = m_pHeap; |
1554 | if (pAssembly->IsCollectible()) |
1555 | { |
1556 | pHeap = pAssembly->GetLoaderAllocator()->GetHighFrequencyHeap(); |
1557 | } |
1558 | |
1559 | entry = abHolder.CreateAssemblyBinding(pHeap); |
1560 | entry->Init(pSpec,pAssembly->GetFile(),pAssembly,NULL,pHeap, abHolder.GetPamTracker()); |
1561 | |
1562 | m_map.InsertValue(key, entry); |
1563 | |
1564 | abHolder.SuppressRelease(); |
1565 | |
1566 | STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreAssembly): Add cached entry (%p) with PEFile %p" ,entry,pAssembly->GetFile()); |
1567 | RETURN TRUE; |
1568 | } |
1569 | else |
1570 | { |
1571 | if (!entry->IsError()) |
1572 | { |
1573 | if (entry->GetAssembly() != NULL) |
1574 | { |
1575 | // OK if this is a duplicate |
1576 | if (entry->GetAssembly() == pAssembly) |
1577 | RETURN TRUE; |
1578 | } |
1579 | else |
1580 | { |
1581 | // OK if we have have a matching PEAssembly |
1582 | if (entry->GetFile() != NULL |
1583 | && pAssembly->GetFile()->Equals(entry->GetFile())) |
1584 | { |
1585 | entry->SetAssembly(pAssembly); |
1586 | RETURN TRUE; |
1587 | } |
1588 | } |
1589 | } |
1590 | |
1591 | // Invalid cache transition (see above note about state transitions) |
1592 | RETURN FALSE; |
1593 | } |
1594 | } |
1595 | |
1596 | // Note that this routine may be called outside a lock, so may be racing with another thread. |
1597 | // Returns TRUE if add was successful - if FALSE is returned, caller should honor current |
1598 | // cached value to ensure consistency. |
1599 | |
1600 | BOOL AssemblySpecBindingCache::StoreFile(AssemblySpec *pSpec, PEAssembly *pFile) |
1601 | { |
1602 | CONTRACT(BOOL) |
1603 | { |
1604 | INSTANCE_CHECK; |
1605 | THROWS; |
1606 | GC_TRIGGERS; |
1607 | MODE_ANY; |
1608 | // Host binder based assembly spec's cannot currently be safely inserted into caches. |
1609 | PRECONDITION(pSpec->GetHostBinder() == nullptr); |
1610 | POSTCONDITION((!RETVAL) || (UnsafeContains(this, pSpec) && UnsafeVerifyLookupFile(this, pSpec, pFile))); |
1611 | INJECT_FAULT(COMPlusThrowOM();); |
1612 | } |
1613 | CONTRACT_END; |
1614 | |
1615 | UPTR key = (UPTR)pSpec->Hash(); |
1616 | |
1617 | // On CoreCLR, we will use the BinderID as the key |
1618 | ICLRPrivBinder* pBinderContextForLookup = pFile->GetBindingContext(); |
1619 | |
1620 | _ASSERTE(pBinderContextForLookup || pFile->IsSystem()); |
1621 | if (pBinderContextForLookup) |
1622 | { |
1623 | UINT_PTR binderID = 0; |
1624 | HRESULT hr = pBinderContextForLookup->GetBinderID(&binderID); |
1625 | _ASSERTE(SUCCEEDED(hr)); |
1626 | key = key^binderID; |
1627 | |
1628 | if (!pSpec->GetBindingContext()) |
1629 | { |
1630 | pSpec->SetBindingContext(pBinderContextForLookup); |
1631 | } |
1632 | } |
1633 | |
1634 | AssemblyBinding *entry = (AssemblyBinding *) m_map.LookupValue(key, pSpec); |
1635 | |
1636 | if (entry == (AssemblyBinding *) INVALIDENTRY) |
1637 | { |
1638 | AssemblyBindingHolder abHolder; |
1639 | |
1640 | LoaderHeap* pHeap = m_pHeap; |
1641 | |
1642 | #ifndef CROSSGEN_COMPILE |
1643 | if (pBinderContextForLookup != NULL) |
1644 | { |
1645 | LoaderAllocator* pLoaderAllocator = NULL; |
1646 | |
1647 | // Assemblies loaded with AssemblyLoadContext need to use a different heap if |
1648 | // marked as collectible |
1649 | if (SUCCEEDED(pBinderContextForLookup->GetLoaderAllocator((LPVOID*)&pLoaderAllocator))) |
1650 | { |
1651 | _ASSERTE(pLoaderAllocator != NULL); |
1652 | pHeap = pLoaderAllocator->GetHighFrequencyHeap(); |
1653 | } |
1654 | } |
1655 | #endif // !CROSSGEN_COMPILE |
1656 | |
1657 | entry = abHolder.CreateAssemblyBinding(pHeap); |
1658 | |
1659 | entry->Init(pSpec,pFile,NULL,NULL,pHeap, abHolder.GetPamTracker()); |
1660 | |
1661 | m_map.InsertValue(key, entry); |
1662 | abHolder.SuppressRelease(); |
1663 | |
1664 | STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile: Add cached entry (%p) with PEFile %p\n" , entry, pFile); |
1665 | |
1666 | RETURN TRUE; |
1667 | } |
1668 | else |
1669 | { |
1670 | if (!entry->IsError()) |
1671 | { |
1672 | // OK if this is a duplicate |
1673 | if (entry->GetFile() != NULL |
1674 | && pFile->Equals(entry->GetFile())) |
1675 | RETURN TRUE; |
1676 | } |
1677 | else |
1678 | if (entry->IsPostBindError()) |
1679 | { |
1680 | // Another thread has reported what's going to happen later. |
1681 | entry->ThrowIfError(); |
1682 | |
1683 | } |
1684 | STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"Incompatible cached entry found (%p) when adding PEFile %p\n" , entry, pFile); |
1685 | // Invalid cache transition (see above note about state transitions) |
1686 | RETURN FALSE; |
1687 | } |
1688 | } |
1689 | |
1690 | BOOL AssemblySpecBindingCache::StoreException(AssemblySpec *pSpec, Exception* pEx) |
1691 | { |
1692 | CONTRACT(BOOL) |
1693 | { |
1694 | INSTANCE_CHECK; |
1695 | THROWS; |
1696 | GC_TRIGGERS; |
1697 | MODE_ANY; |
1698 | // Host binder based assembly spec's cannot currently be safely inserted into caches. |
1699 | PRECONDITION(pSpec->GetHostBinder() == nullptr); |
1700 | DISABLED(POSTCONDITION(UnsafeContains(this, pSpec))); //<TODO>@todo: Getting violations here - StoreExceptions could happen anywhere so this is possibly too aggressive.</TODO> |
1701 | INJECT_FAULT(COMPlusThrowOM();); |
1702 | } |
1703 | CONTRACT_END; |
1704 | |
1705 | UPTR key = (UPTR)pSpec->Hash(); |
1706 | |
1707 | AssemblyBinding *entry = LookupInternal(pSpec, TRUE); |
1708 | if (entry == (AssemblyBinding *) INVALIDENTRY) |
1709 | { |
1710 | // TODO: Merge this with the failure lookup in the binder |
1711 | // |
1712 | // Since no entry was found for this assembly in any binding context, save the failure |
1713 | // in the TPABinder context |
1714 | ICLRPrivBinder* pBinderToSaveException = NULL; |
1715 | pBinderToSaveException = pSpec->GetBindingContext(); |
1716 | if (pBinderToSaveException == NULL) |
1717 | { |
1718 | if (!pSpec->IsAssemblySpecForMscorlib()) |
1719 | { |
1720 | pBinderToSaveException = pSpec->GetBindingContextFromParentAssembly(pSpec->GetAppDomain()); |
1721 | UINT_PTR binderID = 0; |
1722 | HRESULT hr = pBinderToSaveException->GetBinderID(&binderID); |
1723 | _ASSERTE(SUCCEEDED(hr)); |
1724 | key = key^binderID; |
1725 | } |
1726 | } |
1727 | } |
1728 | |
1729 | if (entry == (AssemblyBinding *) INVALIDENTRY) { |
1730 | AssemblyBindingHolder abHolder; |
1731 | entry = abHolder.CreateAssemblyBinding(m_pHeap); |
1732 | |
1733 | entry->Init(pSpec,NULL,NULL,pEx,m_pHeap, abHolder.GetPamTracker()); |
1734 | |
1735 | m_map.InsertValue(key, entry); |
1736 | abHolder.SuppressRelease(); |
1737 | |
1738 | STRESS_LOG2(LF_CLASSLOADER,LL_INFO10,"StoreFile (StoreException): Add cached entry (%p) with exception %p" ,entry,pEx); |
1739 | RETURN TRUE; |
1740 | } |
1741 | else |
1742 | { |
1743 | // OK if this is a duplicate |
1744 | if (entry->IsError()) |
1745 | { |
1746 | if (entry->GetHR() == pEx->GetHR()) |
1747 | RETURN TRUE; |
1748 | } |
1749 | else |
1750 | { |
1751 | // OK to transition to error if we don't have an Assembly yet |
1752 | if (entry->GetAssembly() == NULL) |
1753 | { |
1754 | entry->InitException(pEx); |
1755 | RETURN TRUE; |
1756 | } |
1757 | } |
1758 | |
1759 | // Invalid cache transition (see above note about state transitions) |
1760 | RETURN FALSE; |
1761 | } |
1762 | } |
1763 | |
1764 | BOOL AssemblySpecBindingCache::RemoveAssembly(DomainAssembly* pAssembly) |
1765 | { |
1766 | CONTRACT(BOOL) |
1767 | { |
1768 | INSTANCE_CHECK; |
1769 | NOTHROW; |
1770 | GC_TRIGGERS; |
1771 | MODE_ANY; |
1772 | PRECONDITION(pAssembly != NULL); |
1773 | } |
1774 | CONTRACT_END; |
1775 | BOOL result = FALSE; |
1776 | PtrHashMap::PtrIterator i = m_map.begin(); |
1777 | while (!i.end()) |
1778 | { |
1779 | AssemblyBinding* entry = (AssemblyBinding*)i.GetValue(); |
1780 | if (entry->GetAssembly() == pAssembly) |
1781 | { |
1782 | UPTR key = i.GetKey(); |
1783 | m_map.DeleteValue(key, entry); |
1784 | |
1785 | if (m_pHeap == NULL) |
1786 | delete entry; |
1787 | else |
1788 | entry->~AssemblyBinding(); |
1789 | |
1790 | result = TRUE; |
1791 | } |
1792 | ++i; |
1793 | } |
1794 | |
1795 | RETURN result; |
1796 | } |
1797 | |
1798 | /* static */ |
1799 | BOOL AssemblySpecHash::CompareSpecs(UPTR u1, UPTR u2) |
1800 | { |
1801 | // the same... |
1802 | WRAPPER_NO_CONTRACT; |
1803 | return AssemblySpecBindingCache::CompareSpecs(u1,u2); |
1804 | } |
1805 | |
1806 | /* static */ |
1807 | BOOL AssemblySpecBindingCache::CompareSpecs(UPTR u1, UPTR u2) |
1808 | { |
1809 | WRAPPER_NO_CONTRACT; |
1810 | AssemblySpec *a1 = (AssemblySpec *) (u1 << 1); |
1811 | AssemblySpec *a2 = (AssemblySpec *) u2; |
1812 | |
1813 | return a1->CompareEx(a2); |
1814 | } |
1815 | |
1816 | /* static */ |
1817 | BOOL DomainAssemblyCache::CompareBindingSpec(UPTR spec1, UPTR spec2) |
1818 | { |
1819 | WRAPPER_NO_CONTRACT; |
1820 | |
1821 | AssemblySpec* pSpec1 = (AssemblySpec*) (spec1 << 1); |
1822 | AssemblyEntry* pEntry2 = (AssemblyEntry*) spec2; |
1823 | |
1824 | return pSpec1->CompareEx(&pEntry2->spec); |
1825 | } |
1826 | |
1827 | DomainAssemblyCache::AssemblyEntry* DomainAssemblyCache::LookupEntry(AssemblySpec* pSpec) |
1828 | { |
1829 | CONTRACT (DomainAssemblyCache::AssemblyEntry*) |
1830 | { |
1831 | INSTANCE_CHECK; |
1832 | THROWS; |
1833 | GC_TRIGGERS; |
1834 | MODE_ANY; |
1835 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
1836 | INJECT_FAULT(COMPlusThrowOM();); |
1837 | } |
1838 | CONTRACT_END |
1839 | |
1840 | DWORD hashValue = pSpec->Hash(); |
1841 | |
1842 | LPVOID pResult = m_Table.LookupValue(hashValue, pSpec); |
1843 | if(pResult == (LPVOID) INVALIDENTRY) |
1844 | RETURN NULL; |
1845 | else |
1846 | RETURN (AssemblyEntry*) pResult; |
1847 | } |
1848 | |
1849 | VOID DomainAssemblyCache::InsertEntry(AssemblySpec* pSpec, LPVOID pData1, LPVOID pData2/*=NULL*/) |
1850 | { |
1851 | CONTRACTL |
1852 | { |
1853 | INSTANCE_CHECK; |
1854 | THROWS; |
1855 | GC_TRIGGERS; |
1856 | MODE_ANY; |
1857 | INJECT_FAULT(COMPlusThrowOM();); |
1858 | } |
1859 | CONTRACTL_END |
1860 | |
1861 | LPVOID ptr = LookupEntry(pSpec); |
1862 | if(ptr == NULL) { |
1863 | |
1864 | BaseDomain::CacheLockHolder lh(m_pDomain); |
1865 | |
1866 | ptr = LookupEntry(pSpec); |
1867 | if(ptr == NULL) { |
1868 | AllocMemTracker amTracker; |
1869 | AllocMemTracker *pamTracker = &amTracker; |
1870 | |
1871 | AssemblyEntry* pEntry = (AssemblyEntry*) pamTracker->Track( m_pDomain->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(AssemblyEntry))) ); |
1872 | new (&pEntry->spec) AssemblySpec (); |
1873 | |
1874 | pEntry->spec.CopyFrom(pSpec); |
1875 | pEntry->spec.CloneFieldsToLoaderHeap(AssemblySpec::ALL_OWNED, m_pDomain->GetLowFrequencyHeap(), pamTracker); |
1876 | pEntry->pData[0] = pData1; |
1877 | pEntry->pData[1] = pData2; |
1878 | DWORD hashValue = pEntry->Hash(); |
1879 | m_Table.InsertValue(hashValue, pEntry); |
1880 | |
1881 | pamTracker->SuppressRelease(); |
1882 | } |
1883 | // lh goes out of scope here |
1884 | } |
1885 | #ifdef _DEBUG |
1886 | else { |
1887 | _ASSERTE(pData1 == ((AssemblyEntry*) ptr)->pData[0]); |
1888 | _ASSERTE(pData2 == ((AssemblyEntry*) ptr)->pData[1]); |
1889 | } |
1890 | #endif |
1891 | |
1892 | } |
1893 | |
1894 | |
1895 | |
1896 | |
1897 | DomainAssembly * AssemblySpec::GetParentAssembly() |
1898 | { |
1899 | LIMITED_METHOD_CONTRACT; |
1900 | return m_pParentAssembly; |
1901 | } |
1902 | |