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