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.
5 /*============================================================
7 ** Header: LoaderAllocator.hpp
11 ** Purpose: Implements collection of loader heaps
14 ===========================================================*/
16 #ifndef __LoaderAllocator_h__
17 #define __LoaderAllocator_h__
21 #include "ilstubcache.h"
23 #include "callcounter.h"
24 #include "methoddescbackpatchinfo.h"
25 #include "crossloaderallocatorhash.h"
27 #define VPTRU_LoaderAllocator 0x3200
29 enum LoaderAllocatorType
36 typedef SHash<PtrSetSHashTraits<LoaderAllocator *>> LoaderAllocatorSet;
38 class CLRPrivBinderAssemblyLoadContext;
40 // Iterator over a DomainAssembly in the same ALC
41 class DomainAssemblyIterator
43 DomainAssembly* pCurrentAssembly;
44 DomainAssembly* pNextAssembly;
47 DomainAssemblyIterator(DomainAssembly* pFirstAssembly);
51 return pCurrentAssembly == NULL;
54 operator DomainAssembly*() const
56 return pCurrentAssembly;
59 DomainAssembly* operator ->() const
61 return pCurrentAssembly;
66 void operator++(int dummy)
72 class LoaderAllocatorID
76 LoaderAllocatorType m_type;
79 DomainAssembly* m_pDomainAssembly;
86 LoaderAllocatorID(LoaderAllocatorType laType=LAT_Invalid, VOID* value = 0)
92 LoaderAllocatorType GetType();
93 VOID AddDomainAssembly(DomainAssembly* pDomainAssembly);
94 DomainAssemblyIterator GetDomainAssemblyIterator();
95 BOOL Equals(LoaderAllocatorID* pId);
99 // Segmented stack to store freed handle indices
100 class SegmentedHandleIndexStack
102 // Segment of the stack
105 static const int Size = 64;
111 // Segment containing the TOS
112 Segment * m_TOSSegment = NULL;
113 // One free segment to prevent rapid delete / new if pop / push happens rapidly
114 // at the boundary of two segments.
115 Segment * m_freeSegment = NULL;
116 // Index of the top of stack in the TOS segment
117 int m_TOSIndex = Segment::Size;
121 // Push the value to the stack. If the push cannot be done due to OOM, return false;
122 inline bool Push(DWORD value);
124 // Pop the value from the stack
127 // Check if the stack is empty.
128 inline bool IsEmpty();
131 class StringLiteralMap;
132 class VirtualCallStubManager;
133 template <typename ELEMENT>
134 class ListLockEntryBase;
135 typedef ListLockEntryBase<void*> ListLockEntry;
136 class UMEntryThunkCache;
138 #ifdef FEATURE_COMINTEROP
139 class ComCallWrapperCache;
140 #endif // FEATURE_COMINTEROP
141 class EEMarshalingData;
143 class LoaderAllocator
145 VPTR_BASE_VTABLE_CLASS(LoaderAllocator)
146 VPTR_UNIQUE(VPTRU_LoaderAllocator)
149 //****************************************************************************************
150 // #LoaderAllocator Heaps
151 // Heaps for allocating data that persists for the life of the AppDomain
152 // Objects that are allocated frequently should be allocated into the HighFreq heap for
153 // better page management
154 BYTE * m_InitialReservedMemForLoaderHeaps;
155 BYTE m_LowFreqHeapInstance[sizeof(LoaderHeap)];
156 BYTE m_HighFreqHeapInstance[sizeof(LoaderHeap)];
157 BYTE m_StubHeapInstance[sizeof(LoaderHeap)];
158 BYTE m_PrecodeHeapInstance[sizeof(CodeFragmentHeap)];
159 PTR_LoaderHeap m_pLowFrequencyHeap;
160 PTR_LoaderHeap m_pHighFrequencyHeap;
161 PTR_LoaderHeap m_pStubHeap; // stubs for PInvoke, remoting, etc
162 PTR_CodeFragmentHeap m_pPrecodeHeap;
163 PTR_LoaderHeap m_pExecutableHeap;
164 #ifdef FEATURE_READYTORUN
165 PTR_CodeFragmentHeap m_pDynamicHelpersHeap;
167 //****************************************************************************************
168 OBJECTHANDLE m_hLoaderAllocatorObjectHandle;
169 FuncPtrStubs * m_pFuncPtrStubs; // for GetMultiCallableAddrOfCode()
170 // The LoaderAllocator specific string literal map.
171 StringLiteralMap *m_pStringLiteralMap;
172 CrstExplicitInit m_crstLoaderAllocator;
178 bool m_IsCollectible;
180 // Pre-allocated blocks of heap for collectible assemblies. Will be set to NULL as soon as it is
181 // used. See code in GetVSDHeapInitialBlock and GetCodeHeapInitialBlock
182 BYTE * m_pVSDHeapInitialAlloc;
183 BYTE * m_pCodeHeapInitialAlloc;
185 // U->M thunks that are not associated with a delegate.
186 // The cache is keyed by MethodDesc pointers.
187 UMEntryThunkCache * m_pUMEntryThunkCache;
189 // IL stub cache with fabricated MethodTable parented by a random module in this LoaderAllocator.
190 ILStubCache m_ILStubCache;
193 BYTE *GetVSDHeapInitialBlock(DWORD *pSize);
194 BYTE *GetCodeHeapInitialBlock(const BYTE * loAddr, const BYTE * hiAddr, DWORD minimumSize, DWORD *pSize);
196 BaseDomain *m_pDomain;
198 // ExecutionManager caches
199 void * m_pLastUsedCodeHeap;
200 void * m_pLastUsedDynamicCodeHeap;
201 void * m_pJumpStubCache;
203 // LoaderAllocator GC Structures
204 PTR_LoaderAllocator m_pLoaderAllocatorDestroyNext; // Used in LoaderAllocator GC process (during sweeping)
210 #ifdef FAT_DISPATCH_TOKENS
211 struct DispatchTokenFatSHashTraits : public DefaultSHashTraits<DispatchTokenFat*>
213 typedef DispatchTokenFat* key_t;
215 static key_t GetKey(element_t e)
218 static BOOL Equals(key_t k1, key_t k2)
219 { return *k1 == *k2; }
221 static count_t Hash(key_t k)
222 { return (count_t)(size_t)*k; }
225 typedef SHash<DispatchTokenFatSHashTraits> FatTokenSet;
226 SimpleRWLock *m_pFatTokenSetLock;
227 FatTokenSet *m_pFatTokenSet;
230 #ifndef CROSSGEN_COMPILE
231 VirtualCallStubManager *m_pVirtualCallStubManager;
235 LoaderAllocatorSet m_LoaderAllocatorReferences;
236 Volatile<UINT32> m_cReferences;
237 // This will be set by code:LoaderAllocator::Destroy (from managed scout finalizer) and signalizes that
238 // the assembly was collected
239 DomainAssembly * m_pFirstDomainAssemblyFromSameALCToDelete;
241 BOOL CheckAddReference_Unlocked(LoaderAllocator *pOtherLA);
243 static UINT64 cLoaderAllocatorsCreated;
244 UINT64 m_nLoaderAllocator;
246 struct FailedTypeInitCleanupListItem
249 ListLockEntry *m_pListLockEntry;
250 explicit FailedTypeInitCleanupListItem(ListLockEntry *pListLockEntry)
252 m_pListLockEntry(pListLockEntry)
257 SList<FailedTypeInitCleanupListItem> m_failedTypeInitCleanupList;
259 SegmentedHandleIndexStack m_freeHandleIndexesStack;
260 #ifdef FEATURE_COMINTEROP
261 // The wrapper cache for this loader allocator - it has its own CCacheLineAllocator on a per loader allocator basis
262 // to allow the loader allocator to go away and eventually kill the memory when all refs are gone
264 VolatilePtr<ComCallWrapperCache> m_pComCallWrapperCache;
265 // Used for synchronizing creation of the m_pComCallWrapperCache
266 CrstExplicitInit m_ComCallWrapperCrst;
267 // Hash table that maps a MethodTable to COM Interop compatibility data.
268 PtrHashMap m_interopDataHash;
272 // Used for synchronizing access to the m_interopDataHash and m_pMarshalingData
273 CrstExplicitInit m_InteropDataCrst;
274 EEMarshalingData* m_pMarshalingData;
276 #ifdef FEATURE_TIERED_COMPILATION
277 CallCounter m_callCounter;
280 #ifndef CROSSGEN_COMPILE
281 MethodDescBackpatchInfoTracker m_methodDescBackpatchInfoTracker;
284 #ifndef DACCESS_COMPILE
287 // CleanupFailedTypeInit is called from AppDomain
288 // This method accesses loader allocator state in a thread unsafe manner.
289 // It expects to be called only from Terminate.
290 void CleanupFailedTypeInit();
291 #endif //!DACCESS_COMPILE
293 // Collect unreferenced assemblies, remove them from the assembly list and return their loader allocator
295 static LoaderAllocator * GCLoaderAllocators_RemoveAssemblies(AppDomain * pAppDomain);
300 // The scheme for ensuring that LoaderAllocators are destructed correctly is substantially
301 // complicated by the requirement that LoaderAllocators that are eligible for destruction
302 // must be destroyed as a group due to issues where there may be ordering issues in destruction
303 // of LoaderAllocators.
304 // Thus, while there must be a complete web of references keeping the LoaderAllocator alive in
305 // managed memory, we must also have an analogous web in native memory to manage the specific
306 // ordering requirements.
308 // Thus we have an extra garbage collector here to manage the native web of LoaderAllocator references
309 // Also, we have a reference count scheme so that LCG methods keep their associated LoaderAllocator
310 // alive. LCG methods cannot be referenced by LoaderAllocators, so they do not need to participate
311 // in the garbage collection scheme except by using AddRef/Release to adjust the root set of this
312 // garbage collector.
316 // The phases of unloadable assembly are:
318 // 1. Managed LoaderAllocator is alive.
319 // - Assembly is visible to managed world, the managed scout is alive and was not finalized yet.
320 // Note that the fact that the managed scout is in the finalizer queue is not important as it can
321 // (and in certain cases has to) ressurect itself.
323 // code:IsAlive ... TRUE
324 // code:IsManagedScoutAlive ... TRUE
325 // code:DomainAssembly::GetExposedAssemblyObject ... non-NULL (may need to allocate GC object)
327 // code:AddReferenceIfAlive ... TRUE (+ adds reference)
329 // 2. Managed scout is alive, managed LoaderAllocator is collected.
330 // - All managed object related to this assembly (types, their instances, Assembly/AssemblyBuilder)
331 // are dead and/or about to disappear and cannot be recreated anymore. We are just waiting for the
332 // managed scout to run its finalizer.
334 // code:IsAlive ... TRUE
335 // code:IsManagedScoutAlive ... TRUE
336 // code:DomainAssembly::GetExposedAssemblyObject ... NULL (change from phase #1)
338 // code:AddReferenceIfAlive ... TRUE (+ adds reference)
340 // 3. Native LoaderAllocator is alive, managed scout is collected.
341 // - The native LoaderAllocator can be kept alive via native reference with code:AddRef call, e.g.:
342 // * Reference from LCG method,
343 // * Reference recieved from assembly iterator code:AppDomain::AssemblyIterator::Next and/or
344 // held by code:CollectibleAssemblyHolder.
345 // - Other LoaderAllocator can have this LoaderAllocator in its reference list
346 // (code:m_LoaderAllocatorReferences), but without call to code:AddRef.
347 // - LoaderAllocator cannot ever go back to phase #1 or #2, but it can skip this phase if there are
348 // not any LCG method references keeping it alive at the time of manged scout finalization.
350 // code:IsAlive ... TRUE
351 // code:IsManagedScoutAlive ... FALSE (change from phase #2)
352 // code:DomainAssembly::GetExposedAssemblyObject ... NULL
354 // code:AddReferenceIfAlive ... TRUE (+ adds reference)
356 // 4. LoaderAllocator is dead.
357 // - The managed scout was collected. No one holds a native reference with code:AddRef to this
359 // - Other LoaderAllocator can have this LoaderAllocator in its reference list
360 // (code:m_LoaderAllocatorReferences), but without call to code:AddRef.
361 // - LoaderAllocator cannot ever become alive again (i.e. go back to phase #3, #2 or #1).
363 // code:IsAlive ... FALSE (change from phase #3, #2 and #1)
365 // code:AddReferenceIfAlive ... FALSE (change from phase #3, #2 and #1)
369 // Adds reference if the native object is alive - code:#AssemblyPhases.
370 // Returns TRUE if the reference was added.
371 BOOL AddReferenceIfAlive();
373 // Checks if the native object is alive - see code:#AssemblyPhases.
374 BOOL IsAlive() { LIMITED_METHOD_DAC_CONTRACT; return (m_cReferences != (UINT32)0); }
375 // Checks if managed scout is alive - see code:#AssemblyPhases.
376 BOOL IsManagedScoutAlive()
378 return (m_pFirstDomainAssemblyFromSameALCToDelete == NULL);
381 // Collect unreferenced assemblies, delete all their remaining resources.
382 static void GCLoaderAllocators(LoaderAllocator* firstLoaderAllocator);
384 UINT64 GetCreationNumber() { LIMITED_METHOD_DAC_CONTRACT; return m_nLoaderAllocator; }
386 // Ensure this LoaderAllocator has a reference to another LoaderAllocator
387 BOOL EnsureReference(LoaderAllocator *pOtherLA);
389 // Ensure this LoaderAllocator has a reference to every LoaderAllocator of the types
390 // in an instantiation
391 BOOL EnsureInstantiation(Module *pDefiningModule, Instantiation inst);
393 // Given typeId and slotNumber, GetDispatchToken will return a DispatchToken
394 // representing <typeId, slotNumber>. If the typeId is big enough, this
395 // method will automatically allocate a DispatchTokenFat and encapsulate it
396 // in the return value.
397 DispatchToken GetDispatchToken(UINT32 typeId, UINT32 slotNumber);
399 // Same as GetDispatchToken, but returns invalid DispatchToken when the
400 // value doesn't exist or a transient exception (OOM, stack overflow) is
401 // encountered. To check if the token is valid, use DispatchToken::IsValid
402 DispatchToken TryLookupDispatchToken(UINT32 typeId, UINT32 slotNumber);
404 virtual LoaderAllocatorID* Id() =0;
405 BOOL IsCollectible() { WRAPPER_NO_CONTRACT; return m_IsCollectible; }
407 #ifdef DACCESS_COMPILE
408 void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
411 PTR_LoaderHeap GetLowFrequencyHeap()
413 LIMITED_METHOD_CONTRACT;
414 return m_pLowFrequencyHeap;
417 PTR_LoaderHeap GetHighFrequencyHeap()
419 LIMITED_METHOD_CONTRACT;
420 return m_pHighFrequencyHeap;
423 PTR_LoaderHeap GetStubHeap()
425 LIMITED_METHOD_CONTRACT;
429 PTR_CodeFragmentHeap GetPrecodeHeap()
431 LIMITED_METHOD_CONTRACT;
432 return m_pPrecodeHeap;
435 // The executable heap is intended to only be used by the global loader allocator.
436 // It refers to executable memory that is not associated with a rangelist.
437 PTR_LoaderHeap GetExecutableHeap()
439 LIMITED_METHOD_CONTRACT;
440 return m_pExecutableHeap;
443 PTR_CodeFragmentHeap GetDynamicHelpersHeap();
445 FuncPtrStubs * GetFuncPtrStubs();
447 FuncPtrStubs * GetFuncPtrStubsNoCreate()
449 LIMITED_METHOD_CONTRACT;
450 return m_pFuncPtrStubs;
453 OBJECTHANDLE GetLoaderAllocatorObjectHandle()
455 LIMITED_METHOD_CONTRACT;
456 return m_hLoaderAllocatorObjectHandle;
459 LOADERALLOCATORREF GetExposedObject();
461 #ifndef DACCESS_COMPILE
462 LOADERHANDLE AllocateHandle(OBJECTREF value);
464 void SetHandleValue(LOADERHANDLE handle, OBJECTREF value);
465 OBJECTREF CompareExchangeValueInHandle(LOADERHANDLE handle, OBJECTREF value, OBJECTREF compare);
466 void FreeHandle(LOADERHANDLE handle);
468 // The default implementation is a no-op. Only collectible loader allocators implement this method.
469 virtual void RegisterHandleForCleanup(OBJECTHANDLE /* objHandle */) { }
470 virtual void CleanupHandles() { }
472 void RegisterFailedTypeInitForCleanup(ListLockEntry *pListLockEntry);
473 #endif // !defined(DACCESS_COMPILE)
476 // This function is only safe to call if the handle is known to be a handle in a collectible
477 // LoaderAllocator, and the handle is allocated, and the LoaderAllocator is also not collected.
478 FORCEINLINE OBJECTREF GetHandleValueFastCannotFailType2(LOADERHANDLE handle);
480 // These functions are designed to be used for maximum performance to access handle values
481 // The GetHandleValueFast will handle the scenario where a loader allocator pointer does not
482 // need to be acquired to do the handle lookup, and the GetHandleValueFastPhase2 handles
483 // the scenario where the LoaderAllocator pointer is required.
484 // Do not use these functions directly - use GET_LOADERHANDLE_VALUE_FAST macro instead.
485 FORCEINLINE static BOOL GetHandleValueFast(LOADERHANDLE handle, OBJECTREF *pValue);
486 FORCEINLINE BOOL GetHandleValueFastPhase2(LOADERHANDLE handle, OBJECTREF *pValue);
488 #define GET_LOADERHANDLE_VALUE_FAST(pLoaderAllocator, handle, pRetVal) \
490 LOADERHANDLE __handle__ = handle; \
491 if (!LoaderAllocator::GetHandleValueFast(__handle__, pRetVal) && \
492 !pLoaderAllocator->GetHandleValueFastPhase2(__handle__, pRetVal)) \
498 OBJECTREF GetHandleValue(LOADERHANDLE handle);
501 virtual ~LoaderAllocator();
502 BaseDomain *GetDomain() { LIMITED_METHOD_CONTRACT; return m_pDomain; }
503 virtual BOOL CanUnload() = 0;
504 void Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory = NULL);
506 virtual void ReleaseManagedAssemblyLoadContext() {}
508 SIZE_T EstimateSize();
510 void SetupManagedTracking(LOADERALLOCATORREF *pLoaderAllocatorKeepAlive);
511 void ActivateManagedTracking();
513 // Unloaded in this context means that there is no managed code running against this loader allocator.
514 // This flag is used by debugger to filter out methods in modules that are being destructed.
515 bool IsUnloaded() { LIMITED_METHOD_CONTRACT; return m_fUnloaded; }
516 void SetIsUnloaded() { LIMITED_METHOD_CONTRACT; m_fUnloaded = true; }
518 void SetGCRefPoint(int gccounter)
520 LIMITED_METHOD_CONTRACT;
521 m_nGCCount=gccounter;
525 LIMITED_METHOD_CONTRACT;
529 static BOOL QCALLTYPE Destroy(QCall::LoaderAllocatorHandle pLoaderAllocator);
531 //****************************************************************************************
532 // Methods to retrieve a pointer to the COM+ string STRINGREF for a string constant.
533 // If the string is not currently in the hash table it will be added and if the
534 // copy string flag is set then the string will be copied before it is inserted.
535 STRINGREF *GetStringObjRefPtrFromUnicodeString(EEStringData *pStringData);
536 void LazyInitStringLiteralMap();
537 STRINGREF *IsStringInterned(STRINGREF *pString);
538 STRINGREF *GetOrInternString(STRINGREF *pString);
539 void CleanupStringLiteralMap();
541 void InitVirtualCallStubManager(BaseDomain *pDomain);
542 void UninitVirtualCallStubManager();
544 #ifndef CROSSGEN_COMPILE
545 inline VirtualCallStubManager *GetVirtualCallStubManager()
547 LIMITED_METHOD_CONTRACT;
548 return m_pVirtualCallStubManager;
551 UMEntryThunkCache *GetUMEntryThunkCache();
555 static LoaderAllocator* GetLoaderAllocator(ILStubCache* pILStubCache)
557 return CONTAINING_RECORD(pILStubCache, LoaderAllocator, m_ILStubCache);
560 ILStubCache* GetILStubCache()
562 LIMITED_METHOD_CONTRACT;
563 return &m_ILStubCache;
566 //****************************************************************************************
567 // This method returns marshaling data that the EE uses that is stored on a per LoaderAllocator
569 EEMarshalingData *GetMarshalingData();
572 // Deletes marshaling data at shutdown (which contains cached factories that needs to be released)
573 void DeleteMarshalingData();
577 #ifdef FEATURE_COMINTEROP
579 ComCallWrapperCache * GetComCallWrapperCache();
581 void ResetComCallWrapperCache()
583 LIMITED_METHOD_CONTRACT;
584 m_pComCallWrapperCache = NULL;
587 #ifndef DACCESS_COMPILE
589 // Look up interop data for a method table
590 // Returns the data pointer if present, NULL otherwise
591 InteropMethodTableData *LookupComInteropData(MethodTable *pMT);
593 // Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry
594 BOOL InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData);
596 #endif // DACCESS_COMPILE
598 #endif // FEATURE_COMINTEROP
600 #ifdef FEATURE_TIERED_COMPILATION
602 CallCounter* GetCallCounter()
604 LIMITED_METHOD_CONTRACT;
605 return &m_callCounter;
607 #endif // FEATURE_TIERED_COMPILATION
609 #ifndef CROSSGEN_COMPILE
610 MethodDescBackpatchInfoTracker *GetMethodDescBackpatchInfoTracker()
612 LIMITED_METHOD_CONTRACT;
613 return &m_methodDescBackpatchInfoTracker;
616 }; // class LoaderAllocator
618 typedef VPTR(LoaderAllocator) PTR_LoaderAllocator;
620 class GlobalLoaderAllocator : public LoaderAllocator
622 VPTR_VTABLE_CLASS(GlobalLoaderAllocator, LoaderAllocator)
623 VPTR_UNIQUE(VPTRU_LoaderAllocator+1)
625 BYTE m_ExecutableHeapInstance[sizeof(LoaderHeap)];
628 LoaderAllocatorID m_Id;
631 void Init(BaseDomain *pDomain);
632 GlobalLoaderAllocator() : m_Id(LAT_Global, (void*)1) { LIMITED_METHOD_CONTRACT;};
633 virtual LoaderAllocatorID* Id();
634 virtual BOOL CanUnload();
637 typedef VPTR(GlobalLoaderAllocator) PTR_GlobalLoaderAllocator;
639 class ShuffleThunkCache;
641 class AssemblyLoaderAllocator : public LoaderAllocator
643 VPTR_VTABLE_CLASS(AssemblyLoaderAllocator, LoaderAllocator)
644 VPTR_UNIQUE(VPTRU_LoaderAllocator+3)
647 LoaderAllocatorID m_Id;
648 ShuffleThunkCache* m_pShuffleThunkCache;
650 virtual LoaderAllocatorID* Id();
651 AssemblyLoaderAllocator() : m_Id(LAT_Assembly), m_pShuffleThunkCache(NULL)
652 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
653 , m_binderToRelease(NULL)
655 { LIMITED_METHOD_CONTRACT; }
656 void Init(AppDomain *pAppDomain);
657 virtual BOOL CanUnload();
659 void SetCollectible();
661 void AddDomainAssembly(DomainAssembly *pDomainAssembly)
664 m_Id.AddDomainAssembly(pDomainAssembly);
667 ShuffleThunkCache* GetShuffleThunkCache()
669 return m_pShuffleThunkCache;
672 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
673 virtual void RegisterHandleForCleanup(OBJECTHANDLE objHandle);
674 virtual void CleanupHandles();
675 CLRPrivBinderAssemblyLoadContext* GetBinder()
677 return m_binderToRelease;
679 virtual ~AssemblyLoaderAllocator();
680 void RegisterBinder(CLRPrivBinderAssemblyLoadContext* binderToRelease);
681 virtual void ReleaseManagedAssemblyLoadContext();
682 #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
685 struct HandleCleanupListItem
688 OBJECTHANDLE m_handle;
689 explicit HandleCleanupListItem(OBJECTHANDLE handle)
696 SList<HandleCleanupListItem> m_handleCleanupList;
697 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
698 CLRPrivBinderAssemblyLoadContext* m_binderToRelease;
702 typedef VPTR(AssemblyLoaderAllocator) PTR_AssemblyLoaderAllocator;
705 #include "loaderallocator.inl"
707 #endif // __LoaderAllocator_h__