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.hpp
8**
9** Purpose: Implements classes used to bind to assemblies
10**
11**
12
13
14**
15===========================================================*/
16#ifndef _ASSEMBLYSPEC_H
17#define _ASSEMBLYSPEC_H
18#include "hash.h"
19#include "memorypool.h"
20#include "assemblyspecbase.h"
21#include "domainfile.h"
22#include "genericstackprobe.h"
23#include "holder.h"
24
25class AppDomain;
26class Assembly;
27class DomainAssembly;
28enum FileLoadLevel;
29
30class AssemblySpec : public BaseAssemblySpec
31{
32 private:
33
34 friend class AppDomain;
35 friend class AssemblyNameNative;
36
37 AppDomain *m_pAppDomain;
38 SBuffer m_HashForControl;
39 DWORD m_dwHashAlg;
40 DomainAssembly *m_pParentAssembly;
41
42 // Contains the reference to the fallback load context associated with RefEmitted assembly requesting the load of another assembly (static or dynamic)
43 ICLRPrivBinder *m_pFallbackLoadContextBinder;
44
45 // Flag to indicate if we should prefer the fallback load context binder for binding or not.
46 bool m_fPreferFallbackLoadContextBinder;
47
48 BOOL IsValidAssemblyName();
49
50 HRESULT InitializeSpecInternal(mdToken kAssemblyRefOrDef,
51 IMDInternalImport *pImport,
52 DomainAssembly *pStaticParent,
53 BOOL fAllowAllocation);
54
55 // InitializeSpecInternal should be used very carefully so it's made private.
56 // functions that take special care (and thus are allowed to use the function) are listed below
57 friend Assembly * Module::GetAssemblyIfLoaded(
58 mdAssemblyRef kAssemblyRef,
59 LPCSTR szWinRtNamespace,
60 LPCSTR szWinRtClassName,
61 IMDInternalImport * pMDImportOverride,
62 BOOL fDoNotUtilizeExtraChecks,
63 ICLRPrivBinder *pBindingContextForLoadedAssembly);
64
65 public:
66
67#ifndef DACCESS_COMPILE
68 AssemblySpec() : m_pAppDomain(::GetAppDomain())
69 {
70 LIMITED_METHOD_CONTRACT;
71 m_pParentAssembly = NULL;
72
73 m_pFallbackLoadContextBinder = NULL;
74 m_fPreferFallbackLoadContextBinder = false;
75
76 }
77#endif //!DACCESS_COMPILE
78
79 AssemblySpec(AppDomain *pAppDomain) : m_pAppDomain(pAppDomain)
80 {
81 LIMITED_METHOD_CONTRACT
82 m_pParentAssembly = NULL;
83
84 m_pFallbackLoadContextBinder = NULL;
85 m_fPreferFallbackLoadContextBinder = false;
86
87 }
88
89
90 DomainAssembly* GetParentAssembly();
91
92 ICLRPrivBinder* GetBindingContextFromParentAssembly(AppDomain *pDomain);
93
94 bool HasParentAssembly()
95 { WRAPPER_NO_CONTRACT; return GetParentAssembly() != NULL; }
96
97 void InitializeSpec(mdToken kAssemblyRefOrDef,
98 IMDInternalImport *pImport,
99 DomainAssembly *pStaticParent = NULL)
100 {
101 CONTRACTL
102 {
103 INSTANCE_CHECK;
104 GC_TRIGGERS;
105 THROWS;
106 MODE_ANY;
107 }
108 CONTRACTL_END;
109 HRESULT hr=InitializeSpecInternal(kAssemblyRefOrDef, pImport,pStaticParent,TRUE);
110 if(FAILED(hr))
111 EEFileLoadException::Throw(this,hr);
112 };
113
114
115 void InitializeSpec(PEAssembly *pFile);
116 HRESULT InitializeSpec(StackingAllocator* alloc,
117 ASSEMBLYNAMEREF* pName,
118 BOOL fParse = TRUE);
119
120 void AssemblyNameInit(ASSEMBLYNAMEREF* pName, PEImage* pImageInfo); //[in,out], [in]
121
122
123 void SetCodeBase(LPCWSTR szCodeBase)
124 {
125 WRAPPER_NO_CONTRACT;
126 BaseAssemblySpec::SetCodeBase(szCodeBase);
127 }
128 void SetCodeBase(StackingAllocator* alloc, STRINGREF *pCodeBase);
129
130 void SetParentAssembly(DomainAssembly *pAssembly)
131 {
132 CONTRACTL
133 {
134 INSTANCE_CHECK;
135 GC_NOTRIGGER;
136 NOTHROW;
137 MODE_ANY;
138 }
139 CONTRACTL_END;
140
141 m_pParentAssembly = pAssembly;
142 }
143
144 void SetFallbackLoadContextBinderForRequestingAssembly(ICLRPrivBinder *pFallbackLoadContextBinder)
145 {
146 LIMITED_METHOD_CONTRACT;
147
148 m_pFallbackLoadContextBinder = pFallbackLoadContextBinder;
149 }
150
151 ICLRPrivBinder* GetFallbackLoadContextBinderForRequestingAssembly()
152 {
153 LIMITED_METHOD_CONTRACT;
154
155 return m_pFallbackLoadContextBinder;
156 }
157
158 void SetPreferFallbackLoadContextBinder()
159 {
160 LIMITED_METHOD_CONTRACT;
161
162 m_fPreferFallbackLoadContextBinder = true;
163 }
164
165 bool GetPreferFallbackLoadContextBinder()
166 {
167 LIMITED_METHOD_CONTRACT;
168
169 return m_fPreferFallbackLoadContextBinder;
170 }
171
172 // Note that this method does not clone the fields!
173 void CopyFrom(AssemblySpec* pSource)
174 {
175 CONTRACTL
176 {
177 INSTANCE_CHECK;
178 THROWS;
179 MODE_ANY;
180 }
181 CONTRACTL_END;
182
183 BaseAssemblySpec::CopyFrom(pSource);
184
185 SetParentAssembly(pSource->GetParentAssembly());
186
187 // Copy the details of the fallback load context binder
188 SetFallbackLoadContextBinderForRequestingAssembly(pSource->GetFallbackLoadContextBinderForRequestingAssembly());
189 m_fPreferFallbackLoadContextBinder = pSource->GetPreferFallbackLoadContextBinder();
190
191 m_HashForControl = pSource->m_HashForControl;
192 m_dwHashAlg = pSource->m_dwHashAlg;
193 }
194
195
196 HRESULT CheckFriendAssemblyName();
197
198
199 HRESULT EmitToken(IMetaDataAssemblyEmit *pEmit,
200 mdAssemblyRef *pToken,
201 BOOL fUsePublicKeyToken = TRUE,
202 BOOL fMustBeBindable = FALSE /*(used only by FusionBind's implementation)*/);
203
204 // Make sure this matches in the managed Assembly.DemandPermission()
205 enum FilePermFlag {
206 FILE_PATHDISCOVERY = 0x0,
207 FILE_READ = 0x1,
208 FILE_READANDPATHDISC = 0x2,
209 FILE_WEBPERM = 0x3
210 };
211
212
213
214 VOID Bind(
215 AppDomain* pAppDomain,
216 BOOL fThrowOnFileNotFound,
217 CoreBindResult* pBindResult,
218 BOOL fNgenExplicitBind = FALSE,
219 BOOL fExplicitBindToNativeImage = FALSE);
220
221 Assembly *LoadAssembly(FileLoadLevel targetLevel,
222 BOOL fThrowOnFileNotFound = TRUE);
223 DomainAssembly *LoadDomainAssembly(FileLoadLevel targetLevel,
224 BOOL fThrowOnFileNotFound = TRUE);
225
226 //****************************************************************************************
227 //
228 // Creates and loads an assembly based on the name and context.
229 static Assembly *LoadAssembly(LPCSTR pSimpleName,
230 AssemblyMetaDataInternal* pContext,
231 const BYTE * pbPublicKeyOrToken,
232 DWORD cbPublicKeyOrToken,
233 DWORD dwFlags);
234
235
236 // Load an assembly based on an explicit path
237 static Assembly *LoadAssembly(LPCWSTR pFilePath);
238
239
240 private:
241 void MatchRetargetedPublicKeys(Assembly *pAssembly);
242 public:
243 void MatchPublicKeys(Assembly *pAssembly);
244 PEAssembly *ResolveAssemblyFile(AppDomain *pAppDomain);
245
246 AppDomain *GetAppDomain()
247 {
248 LIMITED_METHOD_CONTRACT;
249 return m_pAppDomain;
250 }
251
252 HRESULT SetHashForControl(PBYTE pHashForControl, DWORD dwHashForControl, DWORD dwHashAlg)
253 {
254 CONTRACTL
255 {
256 THROWS;
257 GC_NOTRIGGER;
258 PRECONDITION(CheckPointer(pHashForControl));
259 }
260 CONTRACTL_END;
261
262 m_HashForControl.Set(pHashForControl, dwHashForControl);
263 m_dwHashAlg=dwHashAlg;
264 return S_OK;
265 }
266
267 void ParseEncodedName();
268
269 void SetWindowsRuntimeType(LPCUTF8 szNamespace, LPCUTF8 szClassName);
270 void SetWindowsRuntimeType(SString const & _ssTypeName);
271
272 inline HRESULT SetContentType(AssemblyContentType type)
273 {
274 LIMITED_METHOD_CONTRACT;
275 if (type == AssemblyContentType_Default)
276 {
277 m_dwFlags = (m_dwFlags & ~afContentType_Mask) | afContentType_Default;
278 return S_OK;
279 }
280 else if (type == AssemblyContentType_WindowsRuntime)
281 {
282 m_dwFlags = (m_dwFlags & ~afContentType_Mask) | afContentType_WindowsRuntime;
283 return S_OK;
284 }
285 else
286 {
287 _ASSERTE(!"Unexpected content type.");
288 return E_UNEXPECTED;
289 }
290 }
291
292 // Returns true if the object can be used to bind to the target assembly.
293 // One case in which this is not true is when the content type is WinRT
294 // but no type name has been set.
295 inline bool HasBindableIdentity() const
296 {
297 STATIC_CONTRACT_LIMITED_METHOD;
298#ifdef FEATURE_COMINTEROP
299 return (HasUniqueIdentity() ||
300 (IsContentType_WindowsRuntime() && (GetWinRtTypeClassName() != NULL)));
301#else
302 return TRUE;
303#endif
304 }
305
306 inline BOOL CanUseWithBindingCache() const
307 {
308 STATIC_CONTRACT_LIMITED_METHOD;
309 return HasUniqueIdentity();
310 }
311
312 inline ICLRPrivBinder *GetHostBinder() const
313 {
314 LIMITED_METHOD_CONTRACT;
315 return m_pHostBinder;
316 }
317
318 inline void SetHostBinder(ICLRPrivBinder *pHostBinder)
319 {
320 LIMITED_METHOD_CONTRACT;
321 m_pHostBinder = pHostBinder;
322 }
323
324};
325
326#define INITIAL_ASM_SPEC_HASH_SIZE 7
327class AssemblySpecHash
328{
329 LoaderHeap *m_pHeap;
330 PtrHashMap m_map;
331
332 public:
333
334#ifndef DACCESS_COMPILE
335 AssemblySpecHash(LoaderHeap *pHeap = NULL)
336 : m_pHeap(pHeap)
337 {
338 CONTRACTL
339 {
340 CONSTRUCTOR_CHECK;
341 THROWS;
342 GC_NOTRIGGER;
343 MODE_ANY;
344 }
345 CONTRACTL_END;
346
347 m_map.Init(INITIAL_ASM_SPEC_HASH_SIZE, CompareSpecs, FALSE, NULL);
348 }
349
350 ~AssemblySpecHash();
351#endif
352
353#ifndef DACCESS_COMPILE
354 //
355 // Returns TRUE if the spec was already in the table
356 //
357
358 BOOL Store(AssemblySpec *pSpec, AssemblySpec **ppStoredSpec = NULL)
359 {
360 CONTRACTL
361 {
362 THROWS;
363 GC_TRIGGERS;
364 MODE_ANY;
365 INJECT_FAULT(COMPlusThrowOM());
366 }
367 CONTRACTL_END
368
369 DWORD key = pSpec->Hash();
370
371 AssemblySpec *entry = (AssemblySpec *) m_map.LookupValue(key, pSpec);
372
373 if (entry == (AssemblySpec*) INVALIDENTRY)
374 {
375 if (m_pHeap != NULL)
376 entry = new (m_pHeap->AllocMem(S_SIZE_T(sizeof(AssemblySpec)))) AssemblySpec;
377 else
378 entry = new AssemblySpec;
379
380 GCX_PREEMP();
381 entry->CopyFrom(pSpec);
382 entry->CloneFields(AssemblySpec::ALL_OWNED);
383
384 m_map.InsertValue(key, entry);
385
386 if (ppStoredSpec != NULL)
387 *ppStoredSpec = entry;
388
389 return FALSE;
390 }
391 else
392 {
393 if (ppStoredSpec != NULL)
394 *ppStoredSpec = entry;
395 return TRUE;
396 }
397 }
398#endif // DACCESS_COMPILE
399
400 DWORD Hash(AssemblySpec *pSpec)
401 {
402 WRAPPER_NO_CONTRACT;
403 return pSpec->Hash();
404 }
405
406 static BOOL CompareSpecs(UPTR u1, UPTR u2);
407};
408
409
410class AssemblySpecBindingCache
411{
412 friend class AssemblyBindingHolder;
413 struct AssemblyBinding
414 {
415 public:
416 ~AssemblyBinding()
417 {
418 WRAPPER_NO_CONTRACT;
419
420 if (m_pFile != NULL)
421 m_pFile->Release();
422
423 if (m_exceptionType==EXTYPE_EE)
424 delete m_pException;
425 };
426
427 void OnAppDomainUnload()
428 {
429 LIMITED_METHOD_CONTRACT;
430 if (m_exceptionType == EXTYPE_EE)
431 {
432 m_exceptionType = EXTYPE_NONE;
433 delete m_pException;
434 m_pException = NULL;
435 }
436 };
437
438 inline DomainAssembly* GetAssembly(){ LIMITED_METHOD_CONTRACT; return m_pAssembly;};
439 inline void SetAssembly(DomainAssembly* pAssembly){ LIMITED_METHOD_CONTRACT; m_pAssembly=pAssembly;};
440 inline PEAssembly* GetFile(){ LIMITED_METHOD_CONTRACT; return m_pFile;};
441 inline BOOL IsError(){ LIMITED_METHOD_CONTRACT; return (m_exceptionType!=EXTYPE_NONE);};
442
443 // bound to the file, but failed later
444 inline BOOL IsPostBindError(){ LIMITED_METHOD_CONTRACT; return IsError() && GetFile()!=NULL;};
445
446 inline void ThrowIfError()
447 {
448 CONTRACTL
449 {
450 THROWS;
451 GC_TRIGGERS;
452 MODE_ANY;
453 }
454 CONTRACTL_END;
455
456 switch(m_exceptionType)
457 {
458 case EXTYPE_NONE: return;
459 case EXTYPE_HR: ThrowHR(m_hr);
460 case EXTYPE_EE: PAL_CPP_THROW(Exception *, m_pException->DomainBoundClone());
461 default: _ASSERTE(!"Unexpected exception type");
462 }
463 };
464 inline void Init(AssemblySpec* pSpec, PEAssembly* pFile, DomainAssembly* pAssembly, Exception* pEx, LoaderHeap *pHeap, AllocMemTracker *pamTracker)
465 {
466 CONTRACTL
467 {
468 THROWS;
469 WRAPPER(GC_TRIGGERS);
470 MODE_ANY;
471 }
472 CONTRACTL_END;
473
474 InitInternal(pSpec,pFile,pAssembly);
475 if (pHeap != NULL)
476 {
477 m_spec.CloneFieldsToLoaderHeap(AssemblySpec::ALL_OWNED,pHeap, pamTracker);
478 }
479 else
480 {
481 m_spec.CloneFields(m_spec.ALL_OWNED);
482 }
483 InitException(pEx);
484
485 }
486
487 inline HRESULT GetHR()
488 {
489 LIMITED_METHOD_CONTRACT;
490 switch(m_exceptionType)
491 {
492 case EXTYPE_NONE: return S_OK;
493 case EXTYPE_HR: return m_hr;
494 case EXTYPE_EE: return m_pException->GetHR();
495 default: _ASSERTE(!"Unexpected exception type");
496 }
497 return E_UNEXPECTED;
498 };
499
500 inline void InitException(Exception* pEx)
501 {
502 CONTRACTL
503 {
504 THROWS;
505 WRAPPER(GC_TRIGGERS);
506 MODE_ANY;
507 }
508 CONTRACTL_END;
509
510 _ASSERTE(m_exceptionType==EXTYPE_NONE);
511
512 if (pEx==NULL)
513 return;
514
515 _ASSERTE(!pEx->IsTransient());
516
517 EX_TRY
518 {
519 m_pException = pEx->DomainBoundClone();
520 _ASSERTE(m_pException);
521 m_exceptionType=EXTYPE_EE;
522 }
523 EX_CATCH
524 {
525 InitException(pEx->GetHR());
526 }
527 EX_END_CATCH(RethrowTransientExceptions);
528
529 };
530
531 inline void InitException(HRESULT hr)
532 {
533 LIMITED_METHOD_CONTRACT;
534 _ASSERTE(m_exceptionType==EXTYPE_NONE);
535 if (FAILED(hr))
536 {
537 m_exceptionType=EXTYPE_HR;
538 m_hr=hr;
539 }
540 };
541 protected:
542
543 inline void InitInternal(AssemblySpec* pSpec, PEAssembly* pFile, DomainAssembly* pAssembly )
544 {
545 WRAPPER_NO_CONTRACT;
546 m_spec.CopyFrom(pSpec);
547 m_pFile = pFile;
548 if (m_pFile)
549 m_pFile->AddRef();
550 m_pAssembly = pAssembly;
551 m_exceptionType=EXTYPE_NONE;
552 }
553
554 AssemblySpec m_spec;
555 PEAssembly *m_pFile;
556 DomainAssembly *m_pAssembly;
557 enum{
558 EXTYPE_NONE = 0x00000000,
559 EXTYPE_HR = 0x00000001,
560 EXTYPE_EE = 0x00000002,
561 };
562 INT m_exceptionType;
563 union
564 {
565 HRESULT m_hr;
566 Exception* m_pException;
567 };
568 };
569
570 PtrHashMap m_map;
571 LoaderHeap *m_pHeap;
572
573 AssemblySpecBindingCache::AssemblyBinding* LookupInternal(AssemblySpec* pSpec, BOOL fThrow = FALSE);
574
575 public:
576
577 AssemblySpecBindingCache() DAC_EMPTY();
578 ~AssemblySpecBindingCache() DAC_EMPTY();
579
580 void Init(CrstBase *pCrst, LoaderHeap *pHeap = NULL);
581 void Clear();
582
583 void OnAppDomainUnload();
584
585 BOOL Contains(AssemblySpec *pSpec);
586
587 DomainAssembly *LookupAssembly(AssemblySpec *pSpec, BOOL fThrow=TRUE);
588 PEAssembly *LookupFile(AssemblySpec *pSpec, BOOL fThrow = TRUE);
589
590 BOOL StoreAssembly(AssemblySpec *pSpec, DomainAssembly *pAssembly);
591 BOOL StoreFile(AssemblySpec *pSpec, PEAssembly *pFile);
592
593 BOOL StoreException(AssemblySpec *pSpec, Exception* pEx);
594
595 BOOL RemoveAssembly(DomainAssembly* pAssembly);
596
597 DWORD Hash(AssemblySpec *pSpec)
598 {
599 WRAPPER_NO_CONTRACT;
600 return pSpec->Hash();
601 }
602
603#if !defined(DACCESS_COMPILE)
604 void GetAllAssemblies(SetSHash<PTR_DomainAssembly>& assemblyList)
605 {
606 PtrHashMap::PtrIterator i = m_map.begin();
607 while (!i.end())
608 {
609 AssemblyBinding *b = (AssemblyBinding*) i.GetValue();
610 if(!b->IsError() && b->GetAssembly() != NULL)
611 assemblyList.AddOrReplace(b->GetAssembly());
612 ++i;
613 }
614 }
615#endif // !defined(DACCESS_COMPILE)
616
617 static BOOL CompareSpecs(UPTR u1, UPTR u2);
618};
619
620#define INITIAL_DOMAIN_ASSEMBLY_CACHE_SIZE 17
621class DomainAssemblyCache
622{
623 struct AssemblyEntry {
624 AssemblySpec spec;
625 LPVOID pData[2]; // Can be an Assembly, PEAssembly, or an Unmanaged DLL
626
627 DWORD Hash()
628 {
629 WRAPPER_NO_CONTRACT;
630 return spec.Hash();
631 }
632 };
633
634 PtrHashMap m_Table;
635 AppDomain* m_pDomain;
636
637public:
638
639 static BOOL CompareBindingSpec(UPTR spec1, UPTR spec2);
640
641 void InitializeTable(AppDomain* pDomain, CrstBase *pCrst)
642 {
643 WRAPPER_NO_CONTRACT;
644 _ASSERTE(pDomain);
645 m_pDomain = pDomain;
646
647 LockOwner lock = {pCrst, IsOwnerOfCrst};
648 m_Table.Init(INITIAL_DOMAIN_ASSEMBLY_CACHE_SIZE, &CompareBindingSpec, true, &lock);
649 }
650
651 AssemblyEntry* LookupEntry(AssemblySpec* pSpec);
652
653 LPVOID LookupEntry(AssemblySpec* pSpec, UINT index)
654 {
655 WRAPPER_NO_CONTRACT;
656 _ASSERTE(index < 2);
657 AssemblyEntry* ptr = LookupEntry(pSpec);
658 if(ptr == NULL)
659 return NULL;
660 else
661 return ptr->pData[index];
662 }
663
664 VOID InsertEntry(AssemblySpec* pSpec, LPVOID pData1, LPVOID pData2 = NULL);
665
666private:
667
668};
669
670#endif
671