summaryrefslogtreecommitdiff
path: root/src/vm/appdomain.cpp
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/vm/appdomain.cpp
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/vm/appdomain.cpp')
-rw-r--r--src/vm/appdomain.cpp14994
1 files changed, 14994 insertions, 0 deletions
diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp
new file mode 100644
index 0000000000..de343848be
--- /dev/null
+++ b/src/vm/appdomain.cpp
@@ -0,0 +1,14994 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "common.h"
+
+#include "appdomain.hpp"
+#include "peimagelayout.inl"
+#include "field.h"
+#include "security.h"
+#include "strongnameinternal.h"
+#include "excep.h"
+#include "eeconfig.h"
+#include "gc.h"
+#include "gcenv.h"
+#include "eventtrace.h"
+#ifdef FEATURE_FUSION
+#include "assemblysink.h"
+#include "fusion.h"
+#include "fusionbind.h"
+#include "fusionlogging.h"
+#endif
+#include "perfcounters.h"
+#include "assemblyname.hpp"
+#include "eeprofinterfaces.h"
+#include "dbginterface.h"
+#ifndef DACCESS_COMPILE
+#include "eedbginterfaceimpl.h"
+#endif
+#include "comdynamic.h"
+#include "mlinfo.h"
+#ifdef FEATURE_REMOTING
+#include "remoting.h"
+#endif
+#include "posterror.h"
+#include "assemblynative.hpp"
+#include "shimload.h"
+#include "stringliteralmap.h"
+#include "codeman.h"
+#include "comcallablewrapper.h"
+#include "apithreadstress.h"
+#include "eventtrace.h"
+#include "comdelegate.h"
+#include "siginfo.hpp"
+#ifdef FEATURE_REMOTING
+#include "appdomainhelper.h"
+#include "objectclone.h"
+#endif
+#include "typekey.h"
+
+#include "caparser.h"
+#include "ecall.h"
+#include "finalizerthread.h"
+#include "threadsuspend.h"
+
+#ifdef FEATURE_PREJIT
+#include "corcompile.h"
+#include "compile.h"
+#endif // FEATURE_PREJIT
+
+#ifdef FEATURE_COMINTEROP
+#include "comtoclrcall.h"
+#include "sxshelpers.h"
+#include "runtimecallablewrapper.h"
+#include "mngstdinterfaces.h"
+#include "olevariant.h"
+#include "rcwrefcache.h"
+#include "olecontexthelpers.h"
+#endif // FEATURE_COMINTEROP
+#ifdef FEATURE_TYPEEQUIVALENCE
+#include "typeequivalencehash.hpp"
+#endif
+
+#include "listlock.inl"
+#include "appdomain.inl"
+#include "typeparse.h"
+#include "mdaassistants.h"
+#include "stackcompressor.h"
+#ifdef FEATURE_REMOTING
+#include "mscorcfg.h"
+#include "appdomainconfigfactory.hpp"
+#include "crossdomaincalls.h"
+#endif
+#include "threadpoolrequest.h"
+
+#include "nativeoverlapped.h"
+
+#include "compatibilityflags.h"
+
+#ifndef FEATURE_PAL
+#include "dwreport.h"
+#endif // !FEATURE_PAL
+
+#include "stringarraylist.h"
+
+#ifdef FEATURE_VERSIONING
+#include "../binder/inc/clrprivbindercoreclr.h"
+#endif
+
+#if defined(FEATURE_APPX_BINDER) && defined(FEATURE_HOSTED_BINDER)
+#include "appxutil.h"
+#include "clrprivbinderappx.h"
+#endif
+
+#ifdef FEATURE_HOSTED_BINDER
+#include "clrprivtypecachewinrt.h"
+#endif
+
+#ifndef FEATURE_CORECLR
+#include "nlsinfo.h"
+#endif
+
+#ifdef FEATURE_RANDOMIZED_STRING_HASHING
+#pragma warning(push)
+#pragma warning(disable:4324)
+#include "marvin32.h"
+#pragma warning(pop)
+#endif
+
+// this file handles string conversion errors for itself
+#undef MAKE_TRANSLATIONFAILED
+
+// Define these macro's to do strict validation for jit lock and class
+// init entry leaks. This defines determine if the asserts that
+// verify for these leaks are defined or not. These asserts can
+// sometimes go off even if no entries have been leaked so this
+// defines should be used with caution.
+//
+// If we are inside a .cctor when the application shut's down then the
+// class init lock's head will be set and this will cause the assert
+// to go off.
+//
+// If we are jitting a method when the application shut's down then
+// the jit lock's head will be set causing the assert to go off.
+
+//#define STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
+
+static const WCHAR DEFAULT_DOMAIN_FRIENDLY_NAME[] = W("DefaultDomain");
+static const WCHAR OTHER_DOMAIN_FRIENDLY_NAME_PREFIX[] = W("Domain");
+
+#define STATIC_OBJECT_TABLE_BUCKET_SIZE 1020
+
+#define MAX_URL_LENGTH 2084 // same as INTERNET_MAX_URL_LENGTH
+
+//#define _DEBUG_ADUNLOAD 1
+
+HRESULT RunDllMain(MethodDesc *pMD, HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved); // clsload.cpp
+
+
+
+
+
+// Statics
+
+SPTR_IMPL(SystemDomain, SystemDomain, m_pSystemDomain);
+SVAL_IMPL(ArrayListStatic, SystemDomain, m_appDomainIndexList);
+SPTR_IMPL(SharedDomain, SharedDomain, m_pSharedDomain);
+SVAL_IMPL(BOOL, SystemDomain, s_fForceDebug);
+SVAL_IMPL(BOOL, SystemDomain, s_fForceProfiling);
+SVAL_IMPL(BOOL, SystemDomain, s_fForceInstrument);
+
+#ifndef DACCESS_COMPILE
+
+// Base Domain Statics
+CrstStatic BaseDomain::m_SpecialStaticsCrst;
+
+int BaseDomain::m_iNumberOfProcessors = 0;
+
+// Shared Domain Statics
+static BYTE g_pSharedDomainMemory[sizeof(SharedDomain)];
+
+// System Domain Statics
+GlobalStringLiteralMap* SystemDomain::m_pGlobalStringLiteralMap = NULL;
+
+static BYTE g_pSystemDomainMemory[sizeof(SystemDomain)];
+
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+size_t SystemDomain::m_totalSurvivedBytes = 0;
+#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+CrstStatic SystemDomain::m_SystemDomainCrst;
+CrstStatic SystemDomain::m_DelayedUnloadCrst;
+
+ULONG SystemDomain::s_dNumAppDomains = 0;
+
+AppDomain * SystemDomain::m_pAppDomainBeingUnloaded = NULL;
+ADIndex SystemDomain::m_dwIndexOfAppDomainBeingUnloaded;
+Thread *SystemDomain::m_pAppDomainUnloadRequestingThread = 0;
+Thread *SystemDomain::m_pAppDomainUnloadingThread = 0;
+
+ArrayListStatic SystemDomain::m_appDomainIdList;
+
+DWORD SystemDomain::m_dwLowestFreeIndex = 0;
+
+
+
+// comparison function to be used for matching clsids in our clsid hash table
+BOOL CompareCLSID(UPTR u1, UPTR u2)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ SO_INTOLERANT;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GUID *pguid = (GUID *)(u1 << 1);
+ _ASSERTE(pguid != NULL);
+
+ MethodTable *pMT= (MethodTable *)u2;
+ _ASSERTE(pMT!= NULL);
+
+ GUID guid;
+ pMT->GetGuid(&guid, TRUE);
+ if (!IsEqualIID(guid, *pguid))
+ return FALSE;
+
+ return TRUE;
+}
+
+#ifndef CROSSGEN_COMPILE
+// Constructor for the LargeHeapHandleBucket class.
+LargeHeapHandleBucket::LargeHeapHandleBucket(LargeHeapHandleBucket *pNext, DWORD Size, BaseDomain *pDomain, BOOL bCrossAD)
+: m_pNext(pNext)
+, m_ArraySize(Size)
+, m_CurrentPos(0)
+, m_CurrentEmbeddedFreePos(0) // hint for where to start a search for an embedded free item
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pDomain));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ PTRARRAYREF HandleArrayObj;
+
+ // Allocate the array in the large object heap.
+ if (!bCrossAD)
+ {
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+ HandleArrayObj = (PTRARRAYREF)AllocateObjectArray(Size, g_pObjectClass, TRUE);
+ }
+ else
+ {
+ // During AD creation we don't want to assign the handle array to the currently running AD but
+ // to the AD being created. Ensure that AllocateArrayEx doesn't set the AD and then set it here.
+ AppDomain *pAD = pDomain->AsAppDomain();
+ _ASSERTE(pAD);
+ _ASSERTE(pAD->IsBeingCreated());
+
+ OBJECTREF array;
+ {
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+ array = AllocateArrayEx(
+ ClassLoader::LoadArrayTypeThrowing(g_pObjectClass),
+ (INT32 *)(&Size),
+ 1,
+ TRUE
+ DEBUG_ARG(TRUE));
+ }
+
+ array->SetAppDomain(pAD);
+
+ HandleArrayObj = (PTRARRAYREF)array;
+ }
+
+ // Retrieve the pointer to the data inside the array. This is legal since the array
+ // is located in the large object heap and is guaranteed not to move.
+ m_pArrayDataPtr = (OBJECTREF *)HandleArrayObj->GetDataPtr();
+
+ // Store the array in a strong handle to keep it alive.
+ m_hndHandleArray = pDomain->CreatePinningHandle((OBJECTREF)HandleArrayObj);
+}
+
+
+// Destructor for the LargeHeapHandleBucket class.
+LargeHeapHandleBucket::~LargeHeapHandleBucket()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (m_hndHandleArray)
+ {
+ DestroyPinningHandle(m_hndHandleArray);
+ m_hndHandleArray = NULL;
+ }
+}
+
+
+// Allocate handles from the bucket.
+OBJECTREF *LargeHeapHandleBucket::AllocateHandles(DWORD nRequested)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(nRequested > 0 && nRequested <= GetNumRemainingHandles());
+ _ASSERTE(m_pArrayDataPtr == (OBJECTREF*)((PTRARRAYREF)ObjectFromHandle(m_hndHandleArray))->GetDataPtr());
+
+ // Store the handles in the buffer that was passed in
+ OBJECTREF* ret = &m_pArrayDataPtr[m_CurrentPos];
+ m_CurrentPos += nRequested;
+
+ return ret;
+}
+
+// look for a free item embedded in the table
+OBJECTREF *LargeHeapHandleBucket::TryAllocateEmbeddedFreeHandle()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ OBJECTREF pPreallocatedSentinalObject = ObjectFromHandle(g_pPreallocatedSentinelObject);
+ _ASSERTE(pPreallocatedSentinalObject != NULL);
+
+ for (int i = m_CurrentEmbeddedFreePos; i < m_CurrentPos; i++)
+ {
+ if (m_pArrayDataPtr[i] == pPreallocatedSentinalObject)
+ {
+ m_CurrentEmbeddedFreePos = i;
+ m_pArrayDataPtr[i] = NULL;
+ return &m_pArrayDataPtr[i];
+ }
+ }
+
+ // didn't find it (we don't bother wrapping around for a full search, it's not worth it to try that hard, we'll get it next time)
+
+ m_CurrentEmbeddedFreePos = 0;
+ return NULL;
+}
+
+
+// Maximum bucket size will be 64K on 32-bit and 128K on 64-bit.
+// We subtract out a small amount to leave room for the object
+// header and length of the array.
+
+#define MAX_BUCKETSIZE (16384 - 4)
+
+// Constructor for the LargeHeapHandleTable class.
+LargeHeapHandleTable::LargeHeapHandleTable(BaseDomain *pDomain, DWORD InitialBucketSize)
+: m_pHead(NULL)
+, m_pDomain(pDomain)
+, m_NextBucketSize(InitialBucketSize)
+, m_pFreeSearchHint(NULL)
+, m_cEmbeddedFree(0)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pDomain));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ m_pCrstDebug = NULL;
+#endif
+}
+
+
+// Destructor for the LargeHeapHandleTable class.
+LargeHeapHandleTable::~LargeHeapHandleTable()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Delete the buckets.
+ while (m_pHead)
+ {
+ LargeHeapHandleBucket *pOld = m_pHead;
+ m_pHead = pOld->GetNext();
+ delete pOld;
+ }
+}
+
+//*****************************************************************************
+//
+// LOCKING RULES FOR AllocateHandles() and ReleaseHandles() 12/08/2004
+//
+//
+// These functions are not protected by any locking in this location but rather the callers are
+// assumed to be doing suitable locking for the handle table. The handle table itself is
+// behaving rather like a thread-agnostic collection class -- it doesn't want to know
+// much about the outside world and so it is just doing its job with no awareness of
+// thread notions.
+//
+// The instance in question is
+// There are two locations you can find a LargeHeapHandleTable
+// 1) there is one in every BaseDomain, it is used to keep track of the static members
+// in that domain
+// 2) there is one in the System Domain that is used for the GlobalStringLiteralMap
+//
+// the one in (2) is not the same as the one that is in the BaseDomain object that corresponds
+// to the SystemDomain -- that one is basically stilborn because the string literals don't go
+// there and of course the System Domain has no code loaded into it -- only regular
+// AppDomains (like Domain 0) actually execute code. As a result handle tables are in
+// practice used either for string literals or for static members but never for both.
+// At least not at this writing.
+//
+// Now it's useful to consider what the locking discipline is for these classes.
+//
+// ---------
+//
+// First case: (easiest) is the statics members
+//
+// Each BaseDomain has its own critical section
+//
+// BaseDomain::AllocateObjRefPtrsInLargeTable takes a lock with
+// CrstHolder ch(&m_LargeHeapHandleTableCrst);
+//
+// it does this before it calls AllocateHandles which suffices. It does not call ReleaseHandles
+// at any time (although ReleaseHandles may be called via AllocateHandles if the request
+// doesn't fit in the current block, the remaining handles at the end of the block are released
+// automatically as part of allocation/recycling)
+//
+// note: Recycled handles are only used during String Literal allocation because we only try
+// to recycle handles if the allocation request is for exactly one handle.
+//
+// The handles in the BaseDomain handle table are released when the Domain is unloaded
+// as the GC objects become rootless at that time.
+//
+// This dispenses with all of the Handle tables except the one that is used for string literals
+//
+// ---------
+//
+// Second case: Allocation for use in a string literal
+//
+// AppDomainStringLiteralMap::GetStringLiteral
+// leads to calls to
+// LargeHeapHandleBlockHolder constructor
+// leads to calls to
+// m_Data = pOwner->AllocateHandles(nCount);
+//
+// before doing this AppDomainStringLiteralMap::GetStringLiteral takes this lock
+//
+// CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
+//
+// which is the lock for the hash table that it owns
+//
+// STRINGREF *AppDomainStringLiteralMap::GetInternedString
+//
+// has a similar call path and uses the same approach and the same lock
+// this covers all the paths which allocate
+//
+// ---------
+//
+// Third case: Releases for use in a string literal entry
+//
+// CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
+// taken in the AppDomainStringLiteralMap functions below protects the 4 ways that this can happen
+//
+// case 3a)
+//
+// in an appdomain unload case
+//
+// AppDomainStringLiteralMap::~AppDomainStringLiteralMap() takes the lock then
+// leads to calls to
+// StringLiteralEntry::Release
+// which leads to
+// SystemDomain::GetGlobalStringLiteralMapNoCreate()->RemoveStringLiteralEntry(this)
+// which leads to
+// m_LargeHeapHandleTable.ReleaseHandles((OBJECTREF*)pObjRef, 1);
+//
+// case 3b)
+//
+// AppDomainStringLiteralMap::GetStringLiteral() can call StringLiteralEntry::Release in some
+// error cases, leading to the same stack as above
+//
+// case 3c)
+//
+// AppDomainStringLiteralMap::GetInternedString() can call StringLiteralEntry::Release in some
+// error cases, leading to the same stack as above
+//
+// case 3d)
+//
+// The same code paths in 3b and 3c and also end up releasing if an exception is thrown
+// during their processing. Both these paths use a StringLiteralEntryHolder to assist in cleanup,
+// the StaticRelease method of the StringLiteralEntry gets called, which in turn calls the
+// Release method.
+
+
+// Allocate handles from the large heap handle table.
+OBJECTREF* LargeHeapHandleTable::AllocateHandles(DWORD nRequested, BOOL bCrossAD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(nRequested > 0);
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // SEE "LOCKING RULES FOR AllocateHandles() and ReleaseHandles()" above
+
+ // the lock must be registered and already held by the caller per contract
+#ifdef _DEBUG
+ _ASSERTE(m_pCrstDebug != NULL);
+ _ASSERTE(m_pCrstDebug->OwnedByCurrentThread());
+#endif
+
+ if (nRequested == 1 && m_cEmbeddedFree != 0)
+ {
+ // special casing singleton requests to look for slots that can be re-used
+
+ // we need to do this because string literals are allocated one at a time and then sometimes
+ // released. we do not wish for the number of handles consumed by string literals to
+ // increase forever as assemblies are loaded and unloaded
+
+ if (m_pFreeSearchHint == NULL)
+ m_pFreeSearchHint = m_pHead;
+
+ while (m_pFreeSearchHint)
+ {
+ OBJECTREF* pObjRef = m_pFreeSearchHint->TryAllocateEmbeddedFreeHandle();
+ if (pObjRef != NULL)
+ {
+ // the slot is to have been prepared with a null ready to go
+ _ASSERTE(*pObjRef == NULL);
+ m_cEmbeddedFree--;
+ return pObjRef;
+ }
+ m_pFreeSearchHint = m_pFreeSearchHint->GetNext();
+ }
+
+ // the search doesn't wrap around so it's possible that we might have embedded free items
+ // and not find them but that's ok, we'll get them on the next alloc... all we're trying to do
+ // is to not have big leaks over time.
+ }
+
+
+ // Retrieve the remaining number of handles in the bucket.
+ DWORD NumRemainingHandlesInBucket = (m_pHead != NULL) ? m_pHead->GetNumRemainingHandles() : 0;
+
+ // create a new block if this request doesn't fit in the current block
+ if (nRequested > NumRemainingHandlesInBucket)
+ {
+ if (m_pHead != NULL)
+ {
+ // mark the handles in that remaining region as available for re-use
+ ReleaseHandles(m_pHead->CurrentPos(), NumRemainingHandlesInBucket);
+
+ // mark what's left as having been used
+ m_pHead->ConsumeRemaining();
+ }
+
+ // create a new bucket for this allocation
+
+ // We need a block big enough to hold the requested handles
+ DWORD NewBucketSize = max(m_NextBucketSize, nRequested);
+
+ m_pHead = new LargeHeapHandleBucket(m_pHead, NewBucketSize, m_pDomain, bCrossAD);
+
+ m_NextBucketSize = min(m_NextBucketSize * 2, MAX_BUCKETSIZE);
+ }
+
+ return m_pHead->AllocateHandles(nRequested);
+}
+
+//*****************************************************************************
+// Release object handles allocated using AllocateHandles().
+void LargeHeapHandleTable::ReleaseHandles(OBJECTREF *pObjRef, DWORD nReleased)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pObjRef));
+ }
+ CONTRACTL_END;
+
+ // SEE "LOCKING RULES FOR AllocateHandles() and ReleaseHandles()" above
+
+ // the lock must be registered and already held by the caller per contract
+#ifdef _DEBUG
+ _ASSERTE(m_pCrstDebug != NULL);
+ _ASSERTE(m_pCrstDebug->OwnedByCurrentThread());
+#endif
+
+ OBJECTREF pPreallocatedSentinalObject = ObjectFromHandle(g_pPreallocatedSentinelObject);
+ _ASSERTE(pPreallocatedSentinalObject != NULL);
+
+
+ // Add the released handles to the list of available handles.
+ for (DWORD i = 0; i < nReleased; i++)
+ {
+ SetObjectReference(&pObjRef[i], pPreallocatedSentinalObject, NULL);
+ }
+
+ m_cEmbeddedFree += nReleased;
+}
+
+
+
+
+// Constructor for the ThreadStaticHandleBucket class.
+ThreadStaticHandleBucket::ThreadStaticHandleBucket(ThreadStaticHandleBucket *pNext, DWORD Size, BaseDomain *pDomain)
+: m_pNext(pNext)
+, m_ArraySize(Size)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pDomain));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ PTRARRAYREF HandleArrayObj;
+
+ // Allocate the array on the GC heap.
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+ HandleArrayObj = (PTRARRAYREF)AllocateObjectArray(Size, g_pObjectClass, FALSE);
+
+ // Store the array in a strong handle to keep it alive.
+ m_hndHandleArray = pDomain->CreateStrongHandle((OBJECTREF)HandleArrayObj);
+}
+
+// Destructor for the ThreadStaticHandleBucket class.
+ThreadStaticHandleBucket::~ThreadStaticHandleBucket()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ if (m_hndHandleArray)
+ {
+ DestroyStrongHandle(m_hndHandleArray);
+ m_hndHandleArray = NULL;
+ }
+}
+
+// Allocate handles from the bucket.
+OBJECTHANDLE ThreadStaticHandleBucket::GetHandles()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ return m_hndHandleArray;
+}
+
+// Constructor for the ThreadStaticHandleTable class.
+ThreadStaticHandleTable::ThreadStaticHandleTable(BaseDomain *pDomain)
+: m_pHead(NULL)
+, m_pDomain(pDomain)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pDomain));
+ }
+ CONTRACTL_END;
+}
+
+// Destructor for the ThreadStaticHandleTable class.
+ThreadStaticHandleTable::~ThreadStaticHandleTable()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Delete the buckets.
+ while (m_pHead)
+ {
+ ThreadStaticHandleBucket *pOld = m_pHead;
+ m_pHead = pOld->GetNext();
+ delete pOld;
+ }
+}
+
+// Allocate handles from the large heap handle table.
+OBJECTHANDLE ThreadStaticHandleTable::AllocateHandles(DWORD nRequested)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(nRequested > 0);
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // create a new bucket for this allocation
+ m_pHead = new ThreadStaticHandleBucket(m_pHead, nRequested, m_pDomain);
+
+ return m_pHead->GetHandles();
+}
+
+#endif // CROSSGEN_COMPILE
+
+
+//*****************************************************************************
+// BaseDomain
+//*****************************************************************************
+void BaseDomain::Attach()
+{
+#ifdef FEATURE_RANDOMIZED_STRING_HASHING
+#ifdef FEATURE_CORECLR
+ // Randomized string hashing is on by default for String.GetHashCode in coreclr.
+ COMNlsHashProvider::s_NlsHashProvider.SetUseRandomHashing((CorHost2::GetStartupFlags() & STARTUP_DISABLE_RANDOMIZED_STRING_HASHING) == 0);
+#endif // FEATURE_CORECLR
+#endif // FEATURE_RANDOMIZED_STRING_HASHING
+ m_SpecialStaticsCrst.Init(CrstSpecialStatics);
+}
+
+BaseDomain::BaseDomain()
+{
+ // initialize fields so the domain can be safely destructed
+ // shouldn't call anything that can fail here - use ::Init instead
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ m_fDisableInterfaceCache = FALSE;
+
+ m_pFusionContext = NULL;
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+ m_pTPABinderContext = NULL;
+#endif
+
+ // Make sure the container is set to NULL so that it gets loaded when it is used.
+ m_pLargeHeapHandleTable = NULL;
+
+#ifndef CROSSGEN_COMPILE
+ // Note that m_hHandleTableBucket is overridden by app domains
+ m_hHandleTableBucket = g_HandleTableMap.pBuckets[0];
+#else
+ m_hHandleTableBucket = NULL;
+#endif
+
+ m_pMarshalingData = NULL;
+
+ m_dwContextStatics = 0;
+#ifdef FEATURE_COMINTEROP
+ m_pMngStdInterfacesInfo = NULL;
+ m_pWinRtBinder = NULL;
+#endif
+ m_FileLoadLock.PreInit();
+ m_JITLock.PreInit();
+ m_ClassInitLock.PreInit();
+ m_ILStubGenLock.PreInit();
+
+#ifdef FEATURE_REJIT
+ m_reJitMgr.PreInit(this == (BaseDomain *) g_pSharedDomainMemory);
+#endif
+
+#ifdef FEATURE_CORECLR
+ m_CompatMode = APPDOMAINCOMPAT_NONE;
+#endif
+
+} //BaseDomain::BaseDomain
+
+//*****************************************************************************
+void BaseDomain::Init()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ //
+ // Initialize the domain locks
+ //
+
+ if (this == reinterpret_cast<BaseDomain*>(&g_pSharedDomainMemory[0]))
+ m_DomainCrst.Init(CrstSharedBaseDomain);
+ else if (this == reinterpret_cast<BaseDomain*>(&g_pSystemDomainMemory[0]))
+ m_DomainCrst.Init(CrstSystemBaseDomain);
+ else
+ m_DomainCrst.Init(CrstBaseDomain);
+
+ m_DomainCacheCrst.Init(CrstAppDomainCache);
+ m_DomainLocalBlockCrst.Init(CrstDomainLocalBlock);
+
+ m_InteropDataCrst.Init(CrstInteropData, CRST_REENTRANCY);
+
+ m_WinRTFactoryCacheCrst.Init(CrstWinRTFactoryCache, CRST_UNSAFE_COOPGC);
+
+ // NOTE: CRST_UNSAFE_COOPGC prevents a GC mode switch to preemptive when entering this crst.
+ // If you remove this flag, we will switch to preemptive mode when entering
+ // m_FileLoadLock, which means all functions that enter it will become
+ // GC_TRIGGERS. (This includes all uses of PEFileListLockHolder, LoadLockHolder, etc.) So be sure
+ // to update the contracts if you remove this flag.
+ m_FileLoadLock.Init(CrstAssemblyLoader,
+ CrstFlags(CRST_HOST_BREAKABLE), TRUE);
+
+ //
+ // The JIT lock and the CCtor locks are at the same level (and marked as
+ // UNSAFE_SAME_LEVEL) because they are all part of the same deadlock detection mechanism. We
+ // see through cycles of JITting and .cctor execution and then explicitly allow the cycle to
+ // be broken by giving access to uninitialized classes. If there is no cycle or if the cycle
+ // involves other locks that arent part of this special deadlock-breaking semantics, then
+ // we continue to block.
+ //
+ m_JITLock.Init(CrstJit, CrstFlags(CRST_REENTRANCY | CRST_UNSAFE_SAMELEVEL), TRUE);
+ m_ClassInitLock.Init(CrstClassInit, CrstFlags(CRST_REENTRANCY | CRST_UNSAFE_SAMELEVEL), TRUE);
+
+ m_ILStubGenLock.Init(CrstILStubGen, CrstFlags(CRST_REENTRANCY), TRUE);
+
+ // Large heap handle table CRST.
+ m_LargeHeapHandleTableCrst.Init(CrstAppDomainHandleTable);
+
+ m_crstLoaderAllocatorReferences.Init(CrstLoaderAllocatorReferences);
+ // Has to switch thread to GC_NOTRIGGER while being held (see code:BaseDomain#AssemblyListLock)
+ m_crstAssemblyList.Init(CrstAssemblyList, CrstFlags(
+ CRST_GC_NOTRIGGER_WHEN_TAKEN | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
+
+ // Initialize the EE marshaling data to NULL.
+ m_pMarshalingData = NULL;
+
+#ifdef FEATURE_COMINTEROP
+ // Allocate the managed standard interfaces information.
+ m_pMngStdInterfacesInfo = new MngStdInterfacesInfo();
+
+#if defined(FEATURE_APPX_BINDER)
+ if (!AppX::IsAppXProcess())
+#endif
+ {
+ CLRPrivBinderWinRT::NamespaceResolutionKind fNamespaceResolutionKind = CLRPrivBinderWinRT::NamespaceResolutionKind_WindowsAPI;
+ if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DesignerNamespaceResolutionEnabled) != FALSE)
+ {
+ fNamespaceResolutionKind = CLRPrivBinderWinRT::NamespaceResolutionKind_DesignerResolveEvent;
+ }
+ CLRPrivTypeCacheWinRT * pWinRtTypeCache = CLRPrivTypeCacheWinRT::GetOrCreateTypeCache();
+ m_pWinRtBinder = CLRPrivBinderWinRT::GetOrCreateBinder(pWinRtTypeCache, fNamespaceResolutionKind);
+ }
+#endif // FEATURE_COMINTEROP
+
+ // Init the COM Interop data hash
+ {
+ LockOwner lock = {&m_InteropDataCrst, IsOwnerOfCrst};
+ m_interopDataHash.Init(0, NULL, false, &lock);
+ }
+
+ m_dwSizedRefHandles = 0;
+ if (!m_iNumberOfProcessors)
+ {
+ m_iNumberOfProcessors = GetCurrentProcessCpuCount();
+ }
+}
+
+#undef LOADERHEAP_PROFILE_COUNTER
+
+#ifndef CROSSGEN_COMPILE
+//*****************************************************************************
+void BaseDomain::Terminate()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_crstLoaderAllocatorReferences.Destroy();
+ m_DomainCrst.Destroy();
+ m_DomainCacheCrst.Destroy();
+ m_DomainLocalBlockCrst.Destroy();
+ m_InteropDataCrst.Destroy();
+
+ ListLockEntry* pElement;
+
+ // All the threads that are in this domain had better be stopped by this
+ // point.
+ //
+ // We might be jitting or running a .cctor so we need to empty that queue.
+ pElement = m_JITLock.Pop(TRUE);
+ while (pElement)
+ {
+#ifdef STRICT_JITLOCK_ENTRY_LEAK_DETECTION
+ _ASSERTE ((m_JITLock.m_pHead->m_dwRefCount == 1
+ && m_JITLock.m_pHead->m_hrResultCode == E_FAIL) ||
+ dbg_fDrasticShutdown || g_fInControlC);
+#endif // STRICT_JITLOCK_ENTRY_LEAK_DETECTION
+ delete(pElement);
+ pElement = m_JITLock.Pop(TRUE);
+
+ }
+ m_JITLock.Destroy();
+
+ pElement = m_ClassInitLock.Pop(TRUE);
+ while (pElement)
+ {
+#ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
+ _ASSERTE (dbg_fDrasticShutdown || g_fInControlC);
+#endif
+ delete(pElement);
+ pElement = m_ClassInitLock.Pop(TRUE);
+ }
+ m_ClassInitLock.Destroy();
+
+ FileLoadLock* pFileElement;
+ pFileElement = (FileLoadLock*) m_FileLoadLock.Pop(TRUE);
+ while (pFileElement)
+ {
+#ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
+ _ASSERTE (dbg_fDrasticShutdown || g_fInControlC);
+#endif
+ pFileElement->Release();
+ pFileElement = (FileLoadLock*) m_FileLoadLock.Pop(TRUE);
+ }
+ m_FileLoadLock.Destroy();
+
+ pElement = m_ILStubGenLock.Pop(TRUE);
+ while (pElement)
+ {
+#ifdef STRICT_JITLOCK_ENTRY_LEAK_DETECTION
+ _ASSERTE ((m_ILStubGenLock.m_pHead->m_dwRefCount == 1
+ && m_ILStubGenLock.m_pHead->m_hrResultCode == E_FAIL) ||
+ dbg_fDrasticShutdown || g_fInControlC);
+#endif // STRICT_JITLOCK_ENTRY_LEAK_DETECTION
+ delete(pElement);
+ pElement = m_ILStubGenLock.Pop(TRUE);
+ }
+ m_ILStubGenLock.Destroy();
+
+ m_LargeHeapHandleTableCrst.Destroy();
+
+ if (m_pLargeHeapHandleTable != NULL)
+ {
+ delete m_pLargeHeapHandleTable;
+ m_pLargeHeapHandleTable = NULL;
+ }
+
+ if (!IsAppDomain())
+ {
+ // Kind of a workaround - during unloading, we need to have an EE halt
+ // around deleting this stuff. So it gets deleted in AppDomain::Terminate()
+ // for those things (because there is a convenient place there.)
+ GetLoaderAllocator()->CleanupStringLiteralMap();
+ }
+
+#ifdef FEATURE_COMINTEROP
+ if (m_pMngStdInterfacesInfo)
+ {
+ delete m_pMngStdInterfacesInfo;
+ m_pMngStdInterfacesInfo = NULL;
+ }
+
+ if (m_pWinRtBinder != NULL)
+ {
+ m_pWinRtBinder->Release();
+ }
+#endif // FEATURE_COMINTEROP
+
+ ClearFusionContext();
+
+ m_dwSizedRefHandles = 0;
+}
+#endif // CROSSGEN_COMPILE
+
+void BaseDomain::InitVSD()
+{
+ STANDARD_VM_CONTRACT;
+
+ // This is a workaround for gcc, since it fails to successfully resolve
+ // "TypeIDMap::STARTING_SHARED_DOMAIN_ID" when used within the ?: operator.
+ UINT32 startingId;
+ if (IsSharedDomain())
+ {
+ startingId = TypeIDMap::STARTING_SHARED_DOMAIN_ID;
+ }
+ else
+ {
+ startingId = TypeIDMap::STARTING_UNSHARED_DOMAIN_ID;
+ }
+
+ // By passing false as the last parameter, interfaces loaded in the
+ // shared domain will not be given fat type ids if RequiresFatDispatchTokens
+ // is set. This is correct, as the fat dispatch tokens are only needed to solve
+ // uniqueness problems involving domain specific types.
+ m_typeIDMap.Init(startingId, 2, !IsSharedDomain());
+
+#ifndef CROSSGEN_COMPILE
+ GetLoaderAllocator()->InitVirtualCallStubManager(this);
+#endif
+}
+
+#ifndef CROSSGEN_COMPILE
+BOOL BaseDomain::ContainsOBJECTHANDLE(OBJECTHANDLE handle)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return Ref_ContainHandle(m_hHandleTableBucket,handle);
+}
+
+DWORD BaseDomain::AllocateContextStaticsOffset(DWORD* pOffsetSlot)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ CrstHolder ch(&m_SpecialStaticsCrst);
+
+ DWORD dwOffset = *pOffsetSlot;
+
+ if (dwOffset == (DWORD)-1)
+ {
+ // Allocate the slot
+ dwOffset = m_dwContextStatics++;
+ *pOffsetSlot = dwOffset;
+ }
+
+ return dwOffset;
+}
+
+void BaseDomain::ClearFusionContext()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ if(m_pFusionContext) {
+ m_pFusionContext->Release();
+ m_pFusionContext = NULL;
+ }
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+ if (m_pTPABinderContext) {
+ m_pTPABinderContext->Release();
+ m_pTPABinderContext = NULL;
+ }
+#endif
+}
+
+#ifdef FEATURE_PREJIT
+void AppDomain::DeleteNativeCodeRanges()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ // Fast path to skip using the assembly iterator when the appdomain has not yet completely been initialized
+ // and yet we are destroying it. (This is the case if we OOM during AppDomain creation.)
+ if (m_Assemblies.IsEmpty())
+ return;
+
+ // Shutdown assemblies
+ AssemblyIterator i = IterateAssembliesEx( (AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeIntrospection | kIncludeFailedToLoad) );
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (i.Next(pDomainAssembly.This()))
+ {
+ Assembly * assembly = pDomainAssembly->m_pAssembly;
+ if ((assembly != NULL) && !assembly->IsDomainNeutral())
+ assembly->DeleteNativeCodeRanges();
+ }
+}
+#endif
+
+void AppDomain::ShutdownAssemblies()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Fast path to skip using the assembly iterator when the appdomain has not yet completely been initialized
+ // and yet we are destroying it. (This is the case if we OOM during AppDomain creation.)
+ if (m_Assemblies.IsEmpty())
+ return;
+
+ // Shutdown assemblies
+ // has two stages because Terminate needs info from the Assembly's dependencies
+
+ // Stage 1: call code:Assembly::Terminate
+ AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeIntrospection | kIncludeFailedToLoad | kIncludeCollected));
+ DomainAssembly * pDomainAssembly = NULL;
+
+ while (i.Next_UnsafeNoAddRef(&pDomainAssembly))
+ {
+ // Note: cannot use DomainAssembly::GetAssembly() here as it asserts that the assembly has been
+ // loaded to at least the FILE_LOAD_ALLOCATE level. Since domain shutdown can take place
+ // asynchronously this property cannot be guaranteed. Access the m_pAssembly field directly instead.
+ Assembly * assembly = pDomainAssembly->m_pAssembly;
+ if (assembly && !assembly->IsDomainNeutral())
+ assembly->Terminate();
+ }
+
+ // Stage 2: Clear the list of assemblies
+ i = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeIntrospection | kIncludeFailedToLoad | kIncludeCollected));
+ while (i.Next_UnsafeNoAddRef(&pDomainAssembly))
+ {
+ // We are in shutdown path, no one else can get to the list anymore
+ delete pDomainAssembly;
+ }
+ m_Assemblies.Clear(this);
+
+ // Stage 2: Clear the loader allocators registered for deletion from code:Assembly:Terminate calls in
+ // stage 1
+ // Note: It is not clear to me why we cannot delete the loader allocator from within
+ // code:DomainAssembly::~DomainAssembly
+ ShutdownFreeLoaderAllocators(FALSE);
+} // AppDomain::ShutdownAssemblies
+
+void AppDomain::ShutdownFreeLoaderAllocators(BOOL bFromManagedCode)
+{
+ // If we're called from managed code (i.e. the finalizer thread) we take a lock in
+ // LoaderAllocator::CleanupFailedTypeInit, which may throw. Otherwise we're called
+ // from the app-domain shutdown path in which we can avoid taking the lock.
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ if (bFromManagedCode) THROWS; else NOTHROW;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ CrstHolder ch(GetLoaderAllocatorReferencesLock());
+
+ // Shutdown the LoaderAllocators associated with collectible assemblies
+ while (m_pDelayedLoaderAllocatorUnloadList != NULL)
+ {
+ LoaderAllocator * pCurrentLoaderAllocator = m_pDelayedLoaderAllocatorUnloadList;
+ // Remove next loader allocator from the list
+ m_pDelayedLoaderAllocatorUnloadList = m_pDelayedLoaderAllocatorUnloadList->m_pLoaderAllocatorDestroyNext;
+
+ if (bFromManagedCode)
+ {
+ // For loader allocator finalization, we need to be careful about cleaning up per-appdomain allocations
+ // and synchronizing with GC using delay unload list. We need to wait for next Gen2 GC to finish to ensure
+ // that GC heap does not have any references to the MethodTables being unloaded.
+
+ pCurrentLoaderAllocator->CleanupFailedTypeInit();
+
+ pCurrentLoaderAllocator->CleanupHandles();
+
+ GCX_COOP();
+ SystemDomain::System()->AddToDelayedUnloadList(pCurrentLoaderAllocator);
+ }
+ else
+ {
+ // For appdomain unload, delete the loader allocator right away
+ delete pCurrentLoaderAllocator;
+ }
+ }
+} // AppDomain::ShutdownFreeLoaderAllocators
+
+//---------------------------------------------------------------------------------------
+//
+// Register the loader allocator for deletion in code:AppDomain::ShutdownFreeLoaderAllocators.
+//
+void AppDomain::RegisterLoaderAllocatorForDeletion(LoaderAllocator * pLoaderAllocator)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ NOTHROW;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ CrstHolder ch(GetLoaderAllocatorReferencesLock());
+
+ pLoaderAllocator->m_pLoaderAllocatorDestroyNext = m_pDelayedLoaderAllocatorUnloadList;
+ m_pDelayedLoaderAllocatorUnloadList = pLoaderAllocator;
+}
+
+#ifdef FEATURE_CORECLR
+void AppDomain::ShutdownNativeDllSearchDirectories()
+{
+ LIMITED_METHOD_CONTRACT;
+ // Shutdown assemblies
+ PathIterator i = IterateNativeDllSearchDirectories();
+
+ while (i.Next())
+ {
+ delete i.GetPath();
+ }
+
+ m_NativeDllSearchDirectories.Clear();
+}
+#endif
+
+void AppDomain::ReleaseDomainBoundInfo()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;;
+ // Shutdown assemblies
+ m_AssemblyCache.OnAppDomainUnload();
+
+ AssemblyIterator i = IterateAssembliesEx( (AssemblyIterationFlags)(kIncludeFailedToLoad) );
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (i.Next(pDomainAssembly.This()))
+ {
+ pDomainAssembly->ReleaseManagedData();
+ }
+}
+
+void AppDomain::ReleaseFiles()
+{
+ STANDARD_VM_CONTRACT;
+
+ // Shutdown assemblies
+ AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded | kIncludeExecution | kIncludeIntrospection | kIncludeFailedToLoad | kIncludeLoading));
+ CollectibleAssemblyHolder<DomainAssembly *> pAsm;
+
+ while (i.Next(pAsm.This()))
+ {
+ if (pAsm->GetCurrentAssembly() == NULL)
+ {
+ // Might be domain neutral or not, but should have no live objects as it has not been
+ // really loaded yet. Just reset it.
+ _ASSERTE(FitsIn<DWORD>(i.GetIndex()));
+ m_Assemblies.Set(this, static_cast<DWORD>(i.GetIndex()), NULL);
+ delete pAsm.Extract();
+ }
+ else
+ {
+ if (!pAsm->GetCurrentAssembly()->IsDomainNeutral())
+ pAsm->ReleaseFiles();
+ }
+ }
+} // AppDomain::ReleaseFiles
+
+
+OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate, BOOL bCrossAD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION((nRequested > 0));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (ppLazyAllocate && *ppLazyAllocate)
+ {
+ // Allocation already happened
+ return *ppLazyAllocate;
+ }
+
+ // Enter preemptive state, take the lock and go back to cooperative mode.
+ {
+ CrstHolder ch(&m_LargeHeapHandleTableCrst);
+ GCX_COOP();
+
+ if (ppLazyAllocate && *ppLazyAllocate)
+ {
+ // Allocation already happened
+ return *ppLazyAllocate;
+ }
+
+ // Make sure the large heap handle table is initialized.
+ if (!m_pLargeHeapHandleTable)
+ InitLargeHeapHandleTable();
+
+ // Allocate the handles.
+ OBJECTREF* result = m_pLargeHeapHandleTable->AllocateHandles(nRequested, bCrossAD);
+
+ if (ppLazyAllocate)
+ {
+ *ppLazyAllocate = result;
+ }
+
+ return result;
+ }
+}
+#endif // CROSSGEN_COMPILE
+
+#endif // !DACCESS_COMPILE
+
+/*static*/
+PTR_BaseDomain BaseDomain::ComputeBaseDomain(
+ BaseDomain * pGenericDefinitionDomain, // the domain that owns the generic type or method
+ Instantiation classInst, // the type arguments to the type (if any)
+ Instantiation methodInst) // the type arguments to the method (if any)
+{
+ CONTRACT(PTR_BaseDomain)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_END
+
+ if (pGenericDefinitionDomain && pGenericDefinitionDomain->IsAppDomain())
+ RETURN PTR_BaseDomain(pGenericDefinitionDomain);
+
+ for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
+ {
+ PTR_BaseDomain pArgDomain = classInst[i].GetDomain();
+ if (pArgDomain->IsAppDomain())
+ RETURN pArgDomain;
+ }
+
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ {
+ PTR_BaseDomain pArgDomain = methodInst[i].GetDomain();
+ if (pArgDomain->IsAppDomain())
+ RETURN pArgDomain;
+ }
+ RETURN (pGenericDefinitionDomain ?
+ PTR_BaseDomain(pGenericDefinitionDomain) :
+ PTR_BaseDomain(SystemDomain::System()));
+}
+
+PTR_BaseDomain BaseDomain::ComputeBaseDomain(TypeKey * pKey)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+
+ if (pKey->GetKind() == ELEMENT_TYPE_CLASS)
+ return BaseDomain::ComputeBaseDomain(pKey->GetModule()->GetDomain(),
+ pKey->GetInstantiation());
+ else if (pKey->GetKind() != ELEMENT_TYPE_FNPTR)
+ return pKey->GetElementType().GetDomain();
+ else
+ return BaseDomain::ComputeBaseDomain(NULL,Instantiation(pKey->GetRetAndArgTypes(), pKey->GetNumArgs()+1));
+}
+
+
+
+
+
+#ifndef DACCESS_COMPILE
+
+// Insert class in the hash table
+void AppDomain::InsertClassForCLSID(MethodTable* pMT, BOOL fForceInsert /*=FALSE*/)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ MODE_ANY;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ CVID cvid;
+
+ // Ensure that registered classes are activated for allocation
+ pMT->EnsureInstanceActive();
+
+ // Note that it is possible for multiple classes to claim the same CLSID, and in such a
+ // case it is arbitrary which one we will return for a future query for a given app domain.
+
+ pMT->GetGuid(&cvid, fForceInsert);
+
+ if (!IsEqualIID(cvid, GUID_NULL))
+ {
+ //<TODO>@todo get a better key</TODO>
+ LPVOID val = (LPVOID)pMT;
+ {
+ LockHolder lh(this);
+
+ if (LookupClass(cvid) != pMT)
+ {
+ m_clsidHash.InsertValue(GetKeyFromGUID(&cvid), val);
+ }
+ }
+ }
+}
+
+void AppDomain::InsertClassForCLSID(MethodTable* pMT, GUID *pGuid)
+{
+ CONTRACT_VOID
+ {
+ NOTHROW;
+ PRECONDITION(CheckPointer(pMT));
+ PRECONDITION(CheckPointer(pGuid));
+ }
+ CONTRACT_END;
+
+ LPVOID val = (LPVOID)pMT;
+ {
+ LockHolder lh(this);
+
+ CVID* cvid = pGuid;
+ if (LookupClass(*cvid) != pMT)
+ {
+ m_clsidHash.InsertValue(GetKeyFromGUID(pGuid), val);
+ }
+ }
+
+ RETURN;
+}
+#endif // DACCESS_COMPILE
+
+#ifdef FEATURE_COMINTEROP
+
+#ifndef DACCESS_COMPILE
+void AppDomain::CacheTypeByName(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE bFlags, BOOL bReplaceExisting /*= FALSE*/)
+{
+ WRAPPER_NO_CONTRACT;
+ LockHolder lh(this);
+ CacheTypeByNameWorker(ssClassName, vCacheVersion, typeHandle, bFlags, bReplaceExisting);
+}
+
+void AppDomain::CacheTypeByNameWorker(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE bFlags, BOOL bReplaceExisting /*= FALSE*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(!typeHandle.IsNull());
+ }
+ CONTRACTL_END;
+
+ NewArrayHolder<WCHAR> wzClassName(DuplicateStringThrowing(ssClassName.GetUnicode()));
+
+ if (m_vNameToTypeMapVersion != vCacheVersion)
+ return;
+
+ if (m_pNameToTypeMap == nullptr)
+ {
+ m_pNameToTypeMap = new NameToTypeMapTable();
+ }
+
+ NameToTypeMapEntry e;
+ e.m_key.m_wzName = wzClassName;
+ e.m_key.m_cchName = ssClassName.GetCount();
+ e.m_typeHandle = typeHandle;
+ e.m_nEpoch = this->m_nEpoch;
+ e.m_bFlags = bFlags;
+ if (!bReplaceExisting)
+ m_pNameToTypeMap->Add(e);
+ else
+ m_pNameToTypeMap->AddOrReplace(e);
+
+ wzClassName.SuppressRelease();
+}
+#endif // DACCESS_COMPILE
+
+TypeHandle AppDomain::LookupTypeByName(const SString &ssClassName, UINT* pvCacheVersion, BYTE *pbFlags)
+{
+ WRAPPER_NO_CONTRACT;
+ LockHolder lh(this);
+ return LookupTypeByNameWorker(ssClassName, pvCacheVersion, pbFlags);
+}
+
+TypeHandle AppDomain::LookupTypeByNameWorker(const SString &ssClassName, UINT* pvCacheVersion, BYTE *pbFlags)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ SUPPORTS_DAC;
+ PRECONDITION(CheckPointer(pbFlags, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ *pvCacheVersion = m_vNameToTypeMapVersion;
+
+ if (m_pNameToTypeMap == nullptr)
+ return TypeHandle(); // a null TypeHandle
+
+ NameToTypeMapEntry::Key key;
+ key.m_cchName = ssClassName.GetCount();
+ key.m_wzName = ssClassName.GetUnicode();
+
+ const NameToTypeMapEntry * pEntry = m_pNameToTypeMap->LookupPtr(key);
+ if (pEntry == NULL)
+ return TypeHandle(); // a null TypeHandle
+
+ if (pbFlags != NULL)
+ *pbFlags = pEntry->m_bFlags;
+
+ return pEntry->m_typeHandle;
+}
+
+PTR_MethodTable AppDomain::LookupTypeByGuid(const GUID & guid)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ SString sGuid;
+ {
+ WCHAR wszGuid[64];
+ GuidToLPWSTR(guid, wszGuid, _countof(wszGuid));
+ sGuid.Append(wszGuid);
+ }
+ UINT ver;
+ TypeHandle th = LookupTypeByName(sGuid, &ver, NULL);
+
+ if (!th.IsNull())
+ {
+ _ASSERTE(!th.IsTypeDesc());
+ return th.AsMethodTable();
+ }
+
+#ifdef FEATURE_PREJIT
+ else
+ {
+ // Next look in each ngen'ed image in turn
+ AssemblyIterator assemblyIterator = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded | kIncludeExecution));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+ while (assemblyIterator.Next(pDomainAssembly.This()))
+ {
+ CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
+
+ DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
+ while (i.Next())
+ {
+ Module * pModule = i.GetLoadedModule();
+ if (!pModule->HasNativeImage())
+ continue;
+ _ASSERTE(!pModule->IsCollectible());
+ PTR_MethodTable pMT = pModule->LookupTypeByGuid(guid);
+ if (pMT != NULL)
+ {
+ return pMT;
+ }
+ }
+ }
+ }
+#endif // FEATURE_PREJIT
+ return NULL;
+}
+
+#ifndef DACCESS_COMPILE
+void AppDomain::CacheWinRTTypeByGuid(TypeHandle typeHandle)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(!typeHandle.IsTypeDesc());
+ PRECONDITION(CanCacheWinRTTypeByGuid(typeHandle));
+ }
+ CONTRACTL_END;
+
+ PTR_MethodTable pMT = typeHandle.AsMethodTable();
+
+ GUID guid;
+ if (pMT->GetGuidForWinRT(&guid))
+ {
+ SString sGuid;
+
+ {
+ WCHAR wszGuid[64];
+ GuidToLPWSTR(guid, wszGuid, _countof(wszGuid));
+ sGuid.Append(wszGuid);
+ }
+
+ BYTE bFlags = 0x80;
+ TypeHandle th;
+ UINT vCacheVersion;
+ {
+ LockHolder lh(this);
+ th = LookupTypeByNameWorker(sGuid, &vCacheVersion, &bFlags);
+
+ if (th.IsNull())
+ {
+ // no other entry with the same GUID exists in the cache
+ CacheTypeByNameWorker(sGuid, vCacheVersion, typeHandle, bFlags);
+ }
+ else if (typeHandle.AsMethodTable() != th.AsMethodTable() && th.IsProjectedFromWinRT())
+ {
+ // If we found a native WinRT type cached with the same GUID, replace it.
+ // Otherwise simply add the new mapping to the cache.
+ CacheTypeByNameWorker(sGuid, vCacheVersion, typeHandle, bFlags, TRUE);
+ }
+ }
+ }
+}
+#endif // DACCESS_COMPILE
+
+void AppDomain::GetCachedWinRTTypes(
+ SArray<PTR_MethodTable> * pTypes,
+ SArray<GUID> * pGuids,
+ UINT minEpoch,
+ UINT * pCurEpoch)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ LockHolder lh(this);
+
+ for (auto it = m_pNameToTypeMap->Begin(), end = m_pNameToTypeMap->End();
+ it != end;
+ ++it)
+ {
+ NameToTypeMapEntry entry = (NameToTypeMapEntry)(*it);
+ TypeHandle th = entry.m_typeHandle;
+ if (th.AsMethodTable() != NULL &&
+ entry.m_key.m_wzName[0] == W('{') &&
+ entry.m_nEpoch >= minEpoch)
+ {
+ _ASSERTE(!th.IsTypeDesc());
+ PTR_MethodTable pMT = th.AsMethodTable();
+ // we're parsing the GUID value from the cache, because projected types do not cache the
+ // COM GUID in their GetGuid() but rather the legacy GUID
+ GUID iid;
+ if (LPWSTRToGuid(&iid, entry.m_key.m_wzName, 38) && iid != GUID_NULL)
+ {
+ pTypes->Append(pMT);
+ pGuids->Append(iid);
+ }
+ }
+ }
+
+#ifdef FEATURE_PREJIT
+ // Next look in each ngen'ed image in turn
+ AssemblyIterator assemblyIterator = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded | kIncludeExecution));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+ while (assemblyIterator.Next(pDomainAssembly.This()))
+ {
+ CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
+
+ DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
+ while (i.Next())
+ {
+ Module * pModule = i.GetLoadedModule();
+ if (!pModule->HasNativeImage())
+ continue;
+ _ASSERTE(!pModule->IsCollectible());
+
+ pModule->GetCachedWinRTTypes(pTypes, pGuids);
+ }
+ }
+#endif // FEATURE_PREJIT
+
+ if (pCurEpoch != NULL)
+ *pCurEpoch = m_nEpoch;
+ ++m_nEpoch;
+}
+
+#ifndef CROSSGEN_COMPILE
+#ifndef DACCESS_COMPILE
+// static
+void WinRTFactoryCacheTraits::OnDestructPerEntryCleanupAction(const WinRTFactoryCacheEntry& e)
+{
+ WRAPPER_NO_CONTRACT;
+ if (e.m_pCtxEntry != NULL)
+ {
+ e.m_pCtxEntry->Release();
+ }
+ // the AD is going away, no need to destroy the OBJECTHANDLE
+}
+
+void AppDomain::CacheWinRTFactoryObject(MethodTable *pClassMT, OBJECTREF *refFactory, LPVOID lpCtxCookie)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pClassMT));
+ }
+ CONTRACTL_END;
+
+ CtxEntryHolder pNewCtxEntry;
+ if (lpCtxCookie != NULL)
+ {
+ // We don't want to insert the context cookie in the cache because it's just an address
+ // of an internal COM data structure which will be freed when the apartment is torn down.
+ // What's worse, if another apartment is later created, its context cookie may have exactly
+ // the same value leading to incorrect cache hits. We'll use our CtxEntry instead which
+ // is ref-counted and keeps the COM data structure alive even after the apartment ceases
+ // to exist.
+ pNewCtxEntry = CtxEntryCache::GetCtxEntryCache()->FindCtxEntry(lpCtxCookie, GetThread());
+ }
+
+ WinRTFactoryCacheLockHolder lh(this);
+
+ if (m_pWinRTFactoryCache == nullptr)
+ {
+ m_pWinRTFactoryCache = new WinRTFactoryCache();
+ }
+
+ WinRTFactoryCacheEntry *pEntry = const_cast<WinRTFactoryCacheEntry*>(m_pWinRTFactoryCache->LookupPtr(pClassMT));
+ if (!pEntry)
+ {
+ //
+ // No existing entry for this cache
+ // Create a new one
+ //
+ WinRTFactoryCacheEntry e;
+
+ OBJECTHANDLEHolder ohNewHandle(CreateHandle(*refFactory));
+
+ e.key = pClassMT;
+ e.m_pCtxEntry = pNewCtxEntry;
+ e.m_ohFactoryObject = ohNewHandle;
+
+ m_pWinRTFactoryCache->Add(e);
+
+ // suppress release of the CtxEntry and handle after we successfully inserted the new entry
+ pNewCtxEntry.SuppressRelease();
+ ohNewHandle.SuppressRelease();
+ }
+ else
+ {
+ //
+ // Existing entry
+ //
+ // release the old CtxEntry and update the entry
+ CtxEntry *pTemp = pNewCtxEntry.Extract();
+ pNewCtxEntry = pEntry->m_pCtxEntry;
+ pEntry->m_pCtxEntry = pTemp;
+
+ HndAssignHandle(pEntry->m_ohFactoryObject, *refFactory);
+ }
+}
+
+OBJECTREF AppDomain::LookupWinRTFactoryObject(MethodTable *pClassMT, LPVOID lpCtxCookie)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pClassMT));
+ PRECONDITION(CheckPointer(m_pWinRTFactoryCache, NULL_OK));
+ }
+ CONTRACTL_END;
+
+
+ if (m_pWinRTFactoryCache == nullptr)
+ return NULL;
+
+ //
+ // Retrieve cached factory
+ //
+ WinRTFactoryCacheLockHolder lh(this);
+
+ const WinRTFactoryCacheEntry *pEntry = m_pWinRTFactoryCache->LookupPtr(pClassMT);
+ if (pEntry == NULL)
+ return NULL;
+
+ //
+ // Ignore factories from a different context, unless lpCtxCookie == NULL,
+ // which means the factory is free-threaded
+ // Note that we cannot touch the RCW to retrieve cookie at this point
+ // because the RCW might belong to a STA thread and that STA thread might die
+ // and take the RCW with it. Therefore we have to save cookie in this cache
+ //
+ if (pEntry->m_pCtxEntry == NULL || pEntry->m_pCtxEntry->GetCtxCookie() == lpCtxCookie)
+ return ObjectFromHandle(pEntry->m_ohFactoryObject);
+
+ return NULL;
+}
+
+void AppDomain::RemoveWinRTFactoryObjects(LPVOID pCtxCookie)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pWinRTFactoryCache == nullptr)
+ return;
+
+ // helper class for delayed CtxEntry cleanup
+ class CtxEntryListReleaseHolder
+ {
+ public:
+ CQuickArrayList<CtxEntry *> m_list;
+
+ ~CtxEntryListReleaseHolder()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ for (SIZE_T i = 0; i < m_list.Size(); i++)
+ {
+ m_list[i]->Release();
+ }
+ }
+ } ctxEntryListReleaseHolder;
+
+ GCX_COOP();
+ {
+ WinRTFactoryCacheLockHolder lh(this);
+
+ // Go through the hash table and remove items in the given context
+ for (WinRTFactoryCache::Iterator it = m_pWinRTFactoryCache->Begin(); it != m_pWinRTFactoryCache->End(); it++)
+ {
+ if (it->m_pCtxEntry != NULL && it->m_pCtxEntry->GetCtxCookie() == pCtxCookie)
+ {
+ // Releasing the CtxEntry may trigger GC which we can't do under the lock so we push
+ // it on our local list and release them all after we're done iterating the hashtable.
+ ctxEntryListReleaseHolder.m_list.Push(it->m_pCtxEntry);
+
+ DestroyHandle(it->m_ohFactoryObject);
+ m_pWinRTFactoryCache->Remove(it);
+ }
+ }
+ }
+}
+
+OBJECTREF AppDomain::GetMissingObject()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ if (!m_hndMissing)
+ {
+ // Get the field
+ FieldDesc *pValueFD = MscorlibBinder::GetField(FIELD__MISSING__VALUE);
+
+ pValueFD->CheckRunClassInitThrowing();
+
+ // Retrieve the value static field and store it.
+ OBJECTHANDLE hndMissing = CreateHandle(pValueFD->GetStaticOBJECTREF());
+
+ if (FastInterlockCompareExchangePointer(&m_hndMissing, hndMissing, NULL) != NULL)
+ {
+ // Exchanged failed. The m_hndMissing did not equal NULL and was returned.
+ DestroyHandle(hndMissing);
+ }
+ }
+
+ return ObjectFromHandle(m_hndMissing);
+}
+
+#endif // DACCESS_COMPILE
+#endif //CROSSGEN_COMPILE
+#endif // FEATURE_COMINTEROP
+
+#ifndef DACCESS_COMPILE
+
+EEMarshalingData *BaseDomain::GetMarshalingData()
+{
+ CONTRACT (EEMarshalingData*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM());
+ POSTCONDITION(CheckPointer(m_pMarshalingData));
+ }
+ CONTRACT_END;
+
+ if (!m_pMarshalingData)
+ {
+ // Take the lock
+ CrstHolder holder(&m_InteropDataCrst);
+
+ if (!m_pMarshalingData)
+ {
+ LoaderHeap* pHeap = GetLoaderAllocator()->GetLowFrequencyHeap();
+ m_pMarshalingData = new (pHeap) EEMarshalingData(this, pHeap, &m_DomainCrst);
+ }
+ }
+
+ RETURN m_pMarshalingData;
+}
+
+void BaseDomain::DeleteMarshalingData()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // We are in shutdown - no need to take any lock
+ if (m_pMarshalingData)
+ {
+ delete m_pMarshalingData;
+ m_pMarshalingData = NULL;
+ }
+}
+
+#ifndef CROSSGEN_COMPILE
+
+STRINGREF *BaseDomain::IsStringInterned(STRINGREF *pString)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pString));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ return GetLoaderAllocator()->IsStringInterned(pString);
+}
+
+STRINGREF *BaseDomain::GetOrInternString(STRINGREF *pString)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pString));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ return GetLoaderAllocator()->GetOrInternString(pString);
+}
+
+void BaseDomain::InitLargeHeapHandleTable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(m_pLargeHeapHandleTable==NULL);
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ m_pLargeHeapHandleTable = new LargeHeapHandleTable(this, STATIC_OBJECT_TABLE_BUCKET_SIZE);
+
+#ifdef _DEBUG
+ m_pLargeHeapHandleTable->RegisterCrstDebug(&m_LargeHeapHandleTableCrst);
+#endif
+}
+
+#ifdef FEATURE_COMINTEROP
+MethodTable* AppDomain::GetLicenseInteropHelperMethodTable()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ if(m_pLicenseInteropHelperMT == NULL)
+ {
+ // Do this work outside of the lock so we don't have an unbreakable lock condition
+
+ TypeHandle licenseMgrTypeHnd;
+ MethodDescCallSite loadLM(METHOD__MARSHAL__LOAD_LICENSE_MANAGER);
+
+ licenseMgrTypeHnd = (MethodTable*) loadLM.Call_RetLPVOID((ARG_SLOT*)NULL);
+
+ //
+ // Look up this method by name, because the type is actually declared in System.dll. <TODO>@todo: why?</TODO>
+ //
+
+ MethodDesc *pGetLIHMD = MemberLoader::FindMethod(licenseMgrTypeHnd.AsMethodTable(),
+ "GetLicenseInteropHelperType", &gsig_SM_Void_RetIntPtr);
+ _ASSERTE(pGetLIHMD);
+
+ TypeHandle lihTypeHnd;
+
+ MethodDescCallSite getLIH(pGetLIHMD);
+ lihTypeHnd = (MethodTable*) getLIH.Call_RetLPVOID((ARG_SLOT*)NULL);
+
+ BaseDomain::LockHolder lh(this);
+
+ if(m_pLicenseInteropHelperMT == NULL)
+ m_pLicenseInteropHelperMT = lihTypeHnd.AsMethodTable();
+ }
+ return m_pLicenseInteropHelperMT;
+}
+
+COMorRemotingFlag AppDomain::GetComOrRemotingFlag()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // 0. check if the value is already been set
+ if (m_COMorRemotingFlag != COMorRemoting_NotInitialized)
+ return m_COMorRemotingFlag;
+
+ // 1. check whether the process is AppX
+ if (AppX::IsAppXProcess())
+ {
+ // do not use Remoting in AppX
+ m_COMorRemotingFlag = COMorRemoting_COM;
+ return m_COMorRemotingFlag;
+ }
+
+ // 2. check the xml file
+ m_COMorRemotingFlag = GetPreferComInsteadOfManagedRemotingFromConfigFile();
+ if (m_COMorRemotingFlag != COMorRemoting_NotInitialized)
+ {
+ return m_COMorRemotingFlag;
+ }
+
+ // 3. check the global setting
+ if (NULL != g_pConfig && g_pConfig->ComInsteadOfManagedRemoting())
+ {
+ m_COMorRemotingFlag = COMorRemoting_COM;
+ }
+ else
+ {
+ m_COMorRemotingFlag = COMorRemoting_Remoting;
+ }
+
+ return m_COMorRemotingFlag;
+}
+
+BOOL AppDomain::GetPreferComInsteadOfManagedRemoting()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return (GetComOrRemotingFlag() == COMorRemoting_COM);
+}
+
+STDAPI GetXMLObjectEx(IXMLParser **ppv);
+
+COMorRemotingFlag AppDomain::GetPreferComInsteadOfManagedRemotingFromConfigFile()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_REMOTING
+ COMorRemotingFlag res = COMorRemoting_NotInitialized;
+ NonVMComHolder<IXMLParser> pIXMLParser(NULL);
+ NonVMComHolder<IStream> pFile(NULL);
+ NonVMComHolder<AppDomainConfigFactory> factory(NULL);
+
+ EX_TRY
+ {
+ HRESULT hr;
+ CQuickBytes qb;
+
+ // get config file URL which is a combination of app base and config file name
+ IfFailGo(m_pFusionContext->PrefetchAppConfigFile());
+
+ LPWSTR wzConfigFileUrl = (LPWSTR)qb.AllocThrows(MAX_URL_LENGTH * sizeof(WCHAR));
+ DWORD dwSize = static_cast<DWORD>(qb.Size());
+
+ IfFailGo(m_pFusionContext->Get(ACTAG_APP_CFG_LOCAL_FILEPATH, wzConfigFileUrl, &dwSize, 0));
+
+ IfFailGo(CreateConfigStream(wzConfigFileUrl, &pFile));
+
+ IfFailGo(GetXMLObjectEx(&pIXMLParser));
+
+ factory = new (nothrow) AppDomainConfigFactory();
+
+ if (!factory) {
+ goto ErrExit;
+ }
+ factory->AddRef(); // RefCount = 1
+
+
+ IfFailGo(pIXMLParser->SetInput(pFile)); // filestream's RefCount=2
+
+ IfFailGo(pIXMLParser->SetFactory(factory)); // factory's RefCount=2
+
+ IfFailGo(pIXMLParser->Run(-1));
+
+ res = factory->GetCOMorRemotingFlag();
+ErrExit: ;
+
+ }
+ EX_CATCH
+ {
+ ;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return res;
+#else // FEATURE_REMOTING
+ return COMorRemoting_COM;
+#endif // FEATURE_REMOTING
+}
+#endif // FEATURE_COMINTEROP
+
+#endif // CROSSGEN_COMPILE
+
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+
+void *SystemDomain::operator new(size_t size, void *pInPlace)
+{
+ LIMITED_METHOD_CONTRACT;
+ return pInPlace;
+}
+
+
+void SystemDomain::operator delete(void *pMem)
+{
+ LIMITED_METHOD_CONTRACT;
+ // Do nothing - new() was in-place
+}
+
+
+void SystemDomain::SetCompilationOverrides(BOOL fForceDebug,
+ BOOL fForceProfiling,
+ BOOL fForceInstrument)
+{
+ LIMITED_METHOD_CONTRACT;
+ s_fForceDebug = fForceDebug;
+ s_fForceProfiling = fForceProfiling;
+ s_fForceInstrument = fForceInstrument;
+}
+
+#endif //!DACCESS_COMPILE
+
+void SystemDomain::GetCompilationOverrides(BOOL * fForceDebug,
+ BOOL * fForceProfiling,
+ BOOL * fForceInstrument)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ *fForceDebug = s_fForceDebug;
+ *fForceProfiling = s_fForceProfiling;
+ *fForceInstrument = s_fForceInstrument;
+}
+
+#ifndef DACCESS_COMPILE
+
+void SystemDomain::Attach()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(m_pSystemDomain == NULL);
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+#ifndef CROSSGEN_COMPILE
+ // Initialize stub managers
+ PrecodeStubManager::Init();
+ DelegateInvokeStubManager::Init();
+ JumpStubStubManager::Init();
+ RangeSectionStubManager::Init();
+ ILStubManager::Init();
+ InteropDispatchStubManager::Init();
+ StubLinkStubManager::Init();
+
+ ThunkHeapStubManager::Init();
+
+ TailCallStubManager::Init();
+
+ PerAppDomainTPCountList::InitAppDomainIndexList();
+#endif // CROSSGEN_COMPILE
+
+ m_appDomainIndexList.Init();
+ m_appDomainIdList.Init();
+
+ m_SystemDomainCrst.Init(CrstSystemDomain, (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
+ m_DelayedUnloadCrst.Init(CrstSystemDomainDelayedUnloadList, CRST_UNSAFE_COOPGC);
+
+ // Initialize the ID dispenser that is used for domain neutral module IDs
+ g_pModuleIndexDispenser = new IdDispenser();
+
+ // Create the global SystemDomain and initialize it.
+ m_pSystemDomain = new (&g_pSystemDomainMemory[0]) SystemDomain();
+ // No way it can fail since g_pSystemDomainMemory is a static array.
+ CONSISTENCY_CHECK(CheckPointer(m_pSystemDomain));
+
+ LOG((LF_CLASSLOADER,
+ LL_INFO10,
+ "Created system domain at %p\n",
+ m_pSystemDomain));
+
+ // We need to initialize the memory pools etc. for the system domain.
+ m_pSystemDomain->BaseDomain::Init(); // Setup the memory heaps
+
+ // Create the default domain
+ m_pSystemDomain->CreateDefaultDomain();
+ SharedDomain::Attach();
+
+ // Each domain gets its own ReJitManager, and ReJitManager has its own static
+ // initialization to run
+ ReJitManager::InitStatic();
+}
+
+#ifndef CROSSGEN_COMPILE
+
+void SystemDomain::DetachBegin()
+{
+ WRAPPER_NO_CONTRACT;
+ // Shut down the domain and its children (but don't deallocate anything just
+ // yet).
+
+ // TODO: we should really not running managed DLLMain during process detach.
+ if (GetThread() == NULL)
+ {
+ return;
+ }
+
+ if(m_pSystemDomain)
+ m_pSystemDomain->Stop();
+}
+
+void SystemDomain::DetachEnd()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ // Shut down the domain and its children (but don't deallocate anything just
+ // yet).
+ if(m_pSystemDomain)
+ {
+ GCX_PREEMP();
+ m_pSystemDomain->ClearFusionContext();
+ if (m_pSystemDomain->m_pDefaultDomain)
+ m_pSystemDomain->m_pDefaultDomain->ClearFusionContext();
+ }
+}
+
+void SystemDomain::Stop()
+{
+ WRAPPER_NO_CONTRACT;
+ AppDomainIterator i(TRUE);
+
+ while (i.Next())
+ if (i.GetDomain()->m_Stage < AppDomain::STAGE_CLEARED)
+ i.GetDomain()->Stop();
+}
+
+
+void SystemDomain::Terminate() // bNotifyProfiler is ignored
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // This ignores the refences and terminates the appdomains
+ AppDomainIterator i(FALSE);
+
+ while (i.Next())
+ {
+ delete i.GetDomain();
+ // Keep the iterator from Releasing the current domain
+ i.m_pCurrent = NULL;
+ }
+
+ if (m_pSystemFile != NULL) {
+ m_pSystemFile->Release();
+ m_pSystemFile = NULL;
+ }
+
+ m_pSystemAssembly = NULL;
+
+ if(m_pwDevpath) {
+ delete[] m_pwDevpath;
+ m_pwDevpath = NULL;
+ }
+ m_dwDevpath = 0;
+ m_fDevpath = FALSE;
+
+ if (m_pGlobalStringLiteralMap) {
+ delete m_pGlobalStringLiteralMap;
+ m_pGlobalStringLiteralMap = NULL;
+ }
+
+
+ SharedDomain::Detach();
+
+ BaseDomain::Terminate();
+
+#ifdef FEATURE_COMINTEROP
+ if (g_pRCWCleanupList != NULL)
+ delete g_pRCWCleanupList;
+#endif // FEATURE_COMINTEROP
+ m_GlobalAllocator.Terminate();
+}
+
+
+void SystemDomain::PreallocateSpecialObjects()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(g_pPreallocatedSentinelObject == NULL);
+
+ OBJECTREF pPreallocatedSentinalObject = AllocateObject(g_pObjectClass);
+#if CHECK_APP_DOMAIN_LEAKS
+ pPreallocatedSentinalObject->SetSyncBlockAppDomainAgile();
+#endif
+ g_pPreallocatedSentinelObject = CreatePinningHandle( pPreallocatedSentinalObject );
+
+#ifdef FEATURE_PREJIT
+ if (SystemModule()->HasNativeImage())
+ {
+ CORCOMPILE_EE_INFO_TABLE *pEEInfo = SystemModule()->GetNativeImage()->GetNativeEEInfoTable();
+ pEEInfo->emptyString = (CORINFO_Object **)StringObject::GetEmptyStringRefPtr();
+ }
+#endif
+}
+
+void SystemDomain::CreatePreallocatedExceptions()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ EXCEPTIONREF pBaseException = (EXCEPTIONREF)AllocateObject(g_pExceptionClass);
+ pBaseException->SetHResult(COR_E_EXCEPTION);
+ pBaseException->SetXCode(EXCEPTION_COMPLUS);
+ _ASSERTE(g_pPreallocatedBaseException == NULL);
+ g_pPreallocatedBaseException = CreateHandle(pBaseException);
+
+
+ EXCEPTIONREF pOutOfMemory = (EXCEPTIONREF)AllocateObject(g_pOutOfMemoryExceptionClass);
+ pOutOfMemory->SetHResult(COR_E_OUTOFMEMORY);
+ pOutOfMemory->SetXCode(EXCEPTION_COMPLUS);
+ _ASSERTE(g_pPreallocatedOutOfMemoryException == NULL);
+ g_pPreallocatedOutOfMemoryException = CreateHandle(pOutOfMemory);
+
+
+ EXCEPTIONREF pStackOverflow = (EXCEPTIONREF)AllocateObject(g_pStackOverflowExceptionClass);
+ pStackOverflow->SetHResult(COR_E_STACKOVERFLOW);
+ pStackOverflow->SetXCode(EXCEPTION_COMPLUS);
+ _ASSERTE(g_pPreallocatedStackOverflowException == NULL);
+ g_pPreallocatedStackOverflowException = CreateHandle(pStackOverflow);
+
+
+ EXCEPTIONREF pExecutionEngine = (EXCEPTIONREF)AllocateObject(g_pExecutionEngineExceptionClass);
+ pExecutionEngine->SetHResult(COR_E_EXECUTIONENGINE);
+ pExecutionEngine->SetXCode(EXCEPTION_COMPLUS);
+ _ASSERTE(g_pPreallocatedExecutionEngineException == NULL);
+ g_pPreallocatedExecutionEngineException = CreateHandle(pExecutionEngine);
+
+
+ EXCEPTIONREF pRudeAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);
+#if CHECK_APP_DOMAIN_LEAKS
+ pRudeAbortException->SetSyncBlockAppDomainAgile();
+#endif
+ pRudeAbortException->SetHResult(COR_E_THREADABORTED);
+ pRudeAbortException->SetXCode(EXCEPTION_COMPLUS);
+ _ASSERTE(g_pPreallocatedRudeThreadAbortException == NULL);
+ g_pPreallocatedRudeThreadAbortException = CreateHandle(pRudeAbortException);
+
+
+ EXCEPTIONREF pAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);
+#if CHECK_APP_DOMAIN_LEAKS
+ pAbortException->SetSyncBlockAppDomainAgile();
+#endif
+ pAbortException->SetHResult(COR_E_THREADABORTED);
+ pAbortException->SetXCode(EXCEPTION_COMPLUS);
+ _ASSERTE(g_pPreallocatedThreadAbortException == NULL);
+ g_pPreallocatedThreadAbortException = CreateHandle( pAbortException );
+}
+#endif // CROSSGEN_COMPILE
+
+void SystemDomain::Init()
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+#ifdef _DEBUG
+ LOG((
+ LF_EEMEM,
+ LL_INFO10,
+ "sizeof(EEClass) = %d\n"
+ "sizeof(MethodTable) = %d\n"
+ "sizeof(MethodDesc)= %d\n"
+ "sizeof(FieldDesc) = %d\n"
+ "sizeof(Module) = %d\n",
+ sizeof(EEClass),
+ sizeof(MethodTable),
+ sizeof(MethodDesc),
+ sizeof(FieldDesc),
+ sizeof(Module)
+ ));
+#endif // _DEBUG
+
+ // The base domain is initialized in SystemDomain::Attach()
+ // to allow stub caches to use the memory pool. Do not
+ // initialze it here!
+
+#ifndef CROSSGEN_COMPILE
+#ifdef _DEBUG
+ Context *curCtx = GetCurrentContext();
+#endif
+ _ASSERTE(curCtx);
+ _ASSERTE(curCtx->GetDomain() != NULL);
+#endif
+
+#ifdef _DEBUG
+ g_fVerifierOff = g_pConfig->IsVerifierOff();
+#endif
+
+#ifdef FEATURE_PREJIT
+ if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ZapDisable) != 0)
+ g_fAllowNativeImages = false;
+#endif
+
+ m_pSystemFile = NULL;
+ m_pSystemAssembly = NULL;
+
+ DWORD size = 0;
+
+#ifdef FEATURE_VERSIONING
+
+ // Get the install directory so we can find mscorlib
+ hr = GetInternalSystemDirectory(NULL, &size);
+ if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ ThrowHR(hr);
+
+ // GetInternalSystemDirectory returns a size, including the null!
+ WCHAR *buffer = m_SystemDirectory.OpenUnicodeBuffer(size-1);
+ IfFailThrow(GetInternalSystemDirectory(buffer, &size));
+ m_SystemDirectory.CloseBuffer();
+ m_SystemDirectory.Normalize();
+
+ // At this point m_SystemDirectory should already be canonicalized
+
+#else
+
+ m_SystemDirectory = GetInternalSystemDirectory(&size);
+
+#endif // FEATURE_VERSIONING
+
+ m_BaseLibrary.Append(m_SystemDirectory);
+ m_BaseLibrary.Append(g_pwBaseLibrary);
+ m_BaseLibrary.Normalize();
+
+ LoadBaseSystemClasses();
+
+ {
+ // We are about to start allocating objects, so we must be in cooperative mode.
+ // However, many of the entrypoints to the system (DllGetClassObject and all
+ // N/Direct exports) get called multiple times. Sometimes they initialize the EE,
+ // but generally they remain in preemptive mode. So we really want to push/pop
+ // the state here:
+ GCX_COOP();
+
+#ifndef CROSSGEN_COMPILE
+ if (!NingenEnabled())
+ {
+ CreatePreallocatedExceptions();
+
+ PreallocateSpecialObjects();
+ }
+#endif
+
+ // Finish loading mscorlib now.
+ m_pSystemAssembly->GetDomainAssembly()->EnsureActive();
+#ifdef FEATURE_FUSION
+ // disable fusion log for m_pSystemFile, because m_pSystemFile will get reused
+ m_pSystemFile->DisableFusionLogging();
+#endif
+ }
+
+#ifdef _DEBUG
+ BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE);
+
+ while(fPause)
+ {
+ ClrSleepEx(20, TRUE);
+ }
+#endif // _DEBUG
+}
+
+#ifndef CROSSGEN_COMPILE
+void SystemDomain::LazyInitGlobalStringLiteralMap()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Allocate the global string literal map.
+ NewHolder<GlobalStringLiteralMap> pGlobalStringLiteralMap(new GlobalStringLiteralMap());
+
+ // Initialize the global string literal map.
+ pGlobalStringLiteralMap->Init();
+
+ if (InterlockedCompareExchangeT<GlobalStringLiteralMap *>(&m_pGlobalStringLiteralMap, pGlobalStringLiteralMap, NULL) == NULL)
+ {
+ pGlobalStringLiteralMap.SuppressRelease();
+ }
+}
+
+void AppDomain::CreateADUnloadStartEvent()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ g_pUnloadStartEvent = new CLREvent();
+ g_pUnloadStartEvent->CreateAutoEvent(FALSE);
+}
+
+/*static*/ void SystemDomain::EnumAllStaticGCRefs(promote_func* fn, ScanContext* sc)
+{
+ CONTRACT_VOID
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACT_END;
+
+ // We don't do a normal AppDomainIterator because we can't take the SystemDomain lock from
+ // here.
+ // We're only supposed to call this from a Server GC. We're walking here m_appDomainIdList
+ // m_appDomainIdList will have an AppDomain* or will be NULL. So the only danger is if we
+ // Fetch an AppDomain and then in some other thread the AppDomain is deleted.
+ //
+ // If the thread deleting the AppDomain (AppDomain::~AppDomain)was in Preemptive mode
+ // while doing SystemDomain::EnumAllStaticGCRefs we will issue a GCX_COOP(), which will wait
+ // for the GC to finish, so we are safe
+ //
+ // If the thread is in cooperative mode, it must have been suspended for the GC so a delete
+ // can't happen.
+
+ _ASSERTE(GCHeap::IsGCInProgress() &&
+ GCHeap::IsServerHeap() &&
+ IsGCSpecialThread());
+
+ SystemDomain* sysDomain = SystemDomain::System();
+ if (sysDomain)
+ {
+ DWORD i;
+ DWORD count = (DWORD) m_appDomainIdList.GetCount();
+ for (i = 0 ; i < count ; i++)
+ {
+ AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
+ if (pAppDomain && pAppDomain->IsActive() && !pAppDomain->IsUnloading())
+ {
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ if (g_fEnableARM)
+ {
+ sc->pCurrentDomain = pAppDomain;
+ }
+#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ pAppDomain->EnumStaticGCRefs(fn, sc);
+ }
+ }
+ }
+
+ RETURN;
+}
+
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+void SystemDomain::ResetADSurvivedBytes()
+{
+ CONTRACT_VOID
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACT_END;
+
+ _ASSERTE(GCHeap::IsGCInProgress());
+
+ SystemDomain* sysDomain = SystemDomain::System();
+ if (sysDomain)
+ {
+ DWORD i;
+ DWORD count = (DWORD) m_appDomainIdList.GetCount();
+ for (i = 0 ; i < count ; i++)
+ {
+ AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
+ if (pAppDomain && pAppDomain->IsUserActive())
+ {
+ pAppDomain->ResetSurvivedBytes();
+ }
+ }
+ }
+
+ RETURN;
+}
+
+ULONGLONG SystemDomain::GetADSurvivedBytes()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ SystemDomain* sysDomain = SystemDomain::System();
+ ULONGLONG ullTotalADSurvived = 0;
+ if (sysDomain)
+ {
+ DWORD i;
+ DWORD count = (DWORD) m_appDomainIdList.GetCount();
+ for (i = 0 ; i < count ; i++)
+ {
+ AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
+ if (pAppDomain && pAppDomain->IsUserActive())
+ {
+ ULONGLONG ullSurvived = pAppDomain->GetSurvivedBytes();
+ ullTotalADSurvived += ullSurvived;
+ }
+ }
+ }
+
+ return ullTotalADSurvived;
+}
+
+void SystemDomain::RecordTotalSurvivedBytes(size_t totalSurvivedBytes)
+{
+ CONTRACT_VOID
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACT_END;
+
+ m_totalSurvivedBytes = totalSurvivedBytes;
+
+ SystemDomain* sysDomain = SystemDomain::System();
+ if (sysDomain)
+ {
+ DWORD i;
+ DWORD count = (DWORD) m_appDomainIdList.GetCount();
+ for (i = 0 ; i < count ; i++)
+ {
+ AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
+ if (pAppDomain && pAppDomain->IsUserActive())
+ {
+ FireEtwAppDomainMemSurvived((ULONGLONG)pAppDomain, pAppDomain->GetSurvivedBytes(), totalSurvivedBytes, GetClrInstanceId());
+ }
+ }
+ }
+
+ RETURN;
+}
+#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+// Only called when EE is suspended.
+DWORD SystemDomain::GetTotalNumSizedRefHandles()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ SystemDomain* sysDomain = SystemDomain::System();
+ DWORD dwTotalNumSizedRefHandles = 0;
+ if (sysDomain)
+ {
+ DWORD i;
+ DWORD count = (DWORD) m_appDomainIdList.GetCount();
+ for (i = 0 ; i < count ; i++)
+ {
+ AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
+ if (pAppDomain && pAppDomain->IsActive() && !pAppDomain->IsUnloading())
+ {
+ dwTotalNumSizedRefHandles += pAppDomain->GetNumSizedRefHandles();
+ }
+ }
+ }
+
+ return dwTotalNumSizedRefHandles;
+}
+#endif // CROSSGEN_COMPILE
+
+void SystemDomain::LoadBaseSystemClasses()
+{
+ STANDARD_VM_CONTRACT;
+
+ ETWOnStartup(LdSysBases_V1, LdSysBasesEnd_V1);
+
+ {
+#ifdef FEATURE_FUSION
+ ETWOnStartup (FusionAppCtx_V1, FusionAppCtxEnd_V1);
+ // Setup fusion context for the system domain - this is used for binding mscorlib.
+ IfFailThrow(FusionBind::SetupFusionContext(m_SystemDirectory, NULL, &m_pFusionContext));
+
+ m_pSystemFile = PEAssembly::OpenSystem(m_pFusionContext);
+#else
+ m_pSystemFile = PEAssembly::OpenSystem(NULL);
+#endif // FEATURE_FUSION
+ }
+ // Only partially load the system assembly. Other parts of the code will want to access
+ // the globals in this function before finishing the load.
+ m_pSystemAssembly = DefaultDomain()->LoadDomainAssembly(NULL, m_pSystemFile, FILE_LOAD_POST_LOADLIBRARY, NULL)->GetCurrentAssembly();
+
+ // Set up binder for mscorlib
+ MscorlibBinder::AttachModule(m_pSystemAssembly->GetManifestModule());
+
+ // Load Object
+ g_pObjectClass = MscorlibBinder::GetClass(CLASS__OBJECT);
+
+ // get the Object::.ctor method desc so we can special-case it
+ g_pObjectCtorMD = MscorlibBinder::GetMethod(METHOD__OBJECT__CTOR);
+
+ // Now that ObjectClass is loaded, we can set up
+ // the system for finalizers. There is no point in deferring this, since we need
+ // to know this before we allocate our first object.
+ g_pObjectFinalizerMD = MscorlibBinder::GetMethod(METHOD__OBJECT__FINALIZE);
+
+
+ g_pCanonMethodTableClass = MscorlibBinder::GetClass(CLASS____CANON);
+
+ // NOTE: !!!IMPORTANT!!! ValueType and Enum MUST be loaded one immediately after
+ // the other, because we have coded MethodTable::IsChildValueType
+ // in such a way that it depends on this behaviour.
+ // Load the ValueType class
+ g_pValueTypeClass = MscorlibBinder::GetClass(CLASS__VALUE_TYPE);
+
+ // Load the enum class
+ g_pEnumClass = MscorlibBinder::GetClass(CLASS__ENUM);
+ _ASSERTE(!g_pEnumClass->IsValueType());
+
+ // Load System.RuntimeType
+ // We need to load this after ValueType and Enum because RuntimeType now
+ // contains an enum field (m_invocationFlags). Otherwise INVOCATION_FLAGS
+ // would be treated as a reference type and clr!SigPointer::GetTypeHandleThrowing
+ // throws an exception.
+ g_pRuntimeTypeClass = MscorlibBinder::GetClass(CLASS__CLASS);
+ _ASSERTE(g_pRuntimeTypeClass->IsFullyLoaded());
+
+ // Load Array class
+ g_pArrayClass = MscorlibBinder::GetClass(CLASS__ARRAY);
+
+ // Calling a method on IList<T> for an array requires redirection to a method on
+ // the SZArrayHelper class. Retrieving such methods means calling
+ // GetActualImplementationForArrayGenericIListMethod, which calls FetchMethod for
+ // the corresponding method on SZArrayHelper. This basically results in a class
+ // load due to a method call, which the debugger cannot handle, so we pre-load
+ // the SZArrayHelper class here.
+ g_pSZArrayHelperClass = MscorlibBinder::GetClass(CLASS__SZARRAYHELPER);
+
+ // Load Nullable class
+ g_pNullableClass = MscorlibBinder::GetClass(CLASS__NULLABLE);
+
+ // Load the Object array class.
+ g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass)).AsArray();
+
+ // We have delayed allocation of mscorlib's static handles until we load the object class
+ MscorlibBinder::GetModule()->AllocateRegularStaticHandles(DefaultDomain());
+
+ // used by MethodTable::ContainsStackPtr
+ g_TypedReferenceMT = MscorlibBinder::GetClass(CLASS__TYPED_REFERENCE);
+ g_ArgumentHandleMT = MscorlibBinder::GetClass(CLASS__ARGUMENT_HANDLE);
+ g_ArgIteratorMT = MscorlibBinder::GetClass(CLASS__ARG_ITERATOR);
+
+ // Make sure all primitive types are loaded
+ for (int et = ELEMENT_TYPE_VOID; et <= ELEMENT_TYPE_R8; et++)
+ MscorlibBinder::LoadPrimitiveType((CorElementType)et);
+
+ MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_I);
+ MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_U);
+
+ // unfortunately, the following cannot be delay loaded since the jit
+ // uses it to compute method attributes within a function that cannot
+ // handle Complus exception and the following call goes through a path
+ // where a complus exception can be thrown. It is unfortunate, because
+ // we know that the delegate class and multidelegate class are always
+ // guaranteed to be found.
+ g_pDelegateClass = MscorlibBinder::GetClass(CLASS__DELEGATE);
+ g_pMulticastDelegateClass = MscorlibBinder::GetClass(CLASS__MULTICAST_DELEGATE);
+
+ // used by IsImplicitInterfaceOfSZArray
+ MscorlibBinder::GetClass(CLASS__IENUMERABLEGENERIC);
+ MscorlibBinder::GetClass(CLASS__ICOLLECTIONGENERIC);
+ MscorlibBinder::GetClass(CLASS__ILISTGENERIC);
+#if !defined(FEATURE_CORECLR) || defined(FEATURE_COMINTEROP)
+ MscorlibBinder::GetClass(CLASS__IREADONLYCOLLECTIONGENERIC);
+ MscorlibBinder::GetClass(CLASS__IREADONLYLISTGENERIC);
+#endif
+
+ // Load String
+ g_pStringClass = MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING);
+ _ASSERTE(g_pStringClass->GetBaseSize() == ObjSizeOf(StringObject)+sizeof(WCHAR));
+ _ASSERTE(g_pStringClass->GetComponentSize() == 2);
+
+#ifndef CROSSGEN_COMPILE
+ ECall::PopulateManagedStringConstructors();
+
+ if (CLRIoCompletionHosted())
+ {
+ g_pOverlappedDataClass = MscorlibBinder::GetClass(CLASS__OVERLAPPEDDATA);
+ _ASSERTE (g_pOverlappedDataClass);
+ if (CorHost2::GetHostOverlappedExtensionSize() != 0)
+ {
+ // Overlapped may have an extension if a host hosts IO completion subsystem
+ DWORD instanceFieldBytes = g_pOverlappedDataClass->GetNumInstanceFieldBytes() + CorHost2::GetHostOverlappedExtensionSize();
+ _ASSERTE (instanceFieldBytes + ObjSizeOf(Object) >= MIN_OBJECT_SIZE);
+ DWORD baseSize = (DWORD) (instanceFieldBytes + ObjSizeOf(Object));
+ baseSize = (baseSize + ALLOC_ALIGN_CONSTANT) & ~ALLOC_ALIGN_CONSTANT; // m_BaseSize must be aligned
+ DWORD adjustSize = baseSize - g_pOverlappedDataClass->GetBaseSize();
+ CGCDesc* map = CGCDesc::GetCGCDescFromMT(g_pOverlappedDataClass);
+ CGCDescSeries * cur = map->GetHighestSeries();
+ _ASSERTE ((SSIZE_T)map->GetNumSeries() == 1);
+ cur->SetSeriesSize(cur->GetSeriesSize() - adjustSize);
+ g_pOverlappedDataClass->SetBaseSize(baseSize);
+ }
+ }
+#endif // CROSSGEN_COMPILE
+
+ g_pExceptionClass = MscorlibBinder::GetClass(CLASS__EXCEPTION);
+ g_pOutOfMemoryExceptionClass = MscorlibBinder::GetException(kOutOfMemoryException);
+ g_pStackOverflowExceptionClass = MscorlibBinder::GetException(kStackOverflowException);
+ g_pExecutionEngineExceptionClass = MscorlibBinder::GetException(kExecutionEngineException);
+ g_pThreadAbortExceptionClass = MscorlibBinder::GetException(kThreadAbortException);
+
+ // Used for determining whether a class has a critical finalizer
+ // To determine whether a class has a critical finalizer, we
+ // currently will simply see if it's parent class has a critical
+ // finalizer. To introduce a class with a critical finalizer,
+ // we'll explicitly load CriticalFinalizerObject and set the bit
+ // here.
+ g_pCriticalFinalizerObjectClass = MscorlibBinder::GetClass(CLASS__CRITICAL_FINALIZER_OBJECT);
+ _ASSERTE(g_pCriticalFinalizerObjectClass->HasCriticalFinalizer());
+
+ // used by gc to handle predefined agility checking
+ g_pThreadClass = MscorlibBinder::GetClass(CLASS__THREAD);
+
+#ifdef FEATURE_COMINTEROP
+ g_pBaseCOMObject = MscorlibBinder::GetClass(CLASS__COM_OBJECT);
+ g_pBaseRuntimeClass = MscorlibBinder::GetClass(CLASS__RUNTIME_CLASS);
+
+ MscorlibBinder::GetClass(CLASS__IDICTIONARYGENERIC);
+ MscorlibBinder::GetClass(CLASS__IREADONLYDICTIONARYGENERIC);
+ MscorlibBinder::GetClass(CLASS__ATTRIBUTE);
+ MscorlibBinder::GetClass(CLASS__EVENT_HANDLERGENERIC);
+
+ MscorlibBinder::GetClass(CLASS__IENUMERABLE);
+ MscorlibBinder::GetClass(CLASS__ICOLLECTION);
+ MscorlibBinder::GetClass(CLASS__ILIST);
+ MscorlibBinder::GetClass(CLASS__IDISPOSABLE);
+
+#ifdef _DEBUG
+ WinRTInterfaceRedirector::VerifyRedirectedInterfaceStubs();
+#endif // _DEBUG
+#endif
+
+ // Load a special marker method used to detect Constrained Execution Regions
+ // at jit time.
+ g_pPrepareConstrainedRegionsMethod = MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__PREPARE_CONSTRAINED_REGIONS);
+ g_pExecuteBackoutCodeHelperMethod = MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__EXECUTE_BACKOUT_CODE_HELPER);
+
+ // Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper.
+ // For more details, see comment in code:JITutil_MonEnterWorker around "__me = GetEEFuncEntryPointMacro(JIT_MonEnter)".
+ ECall::GetFCallImpl(MscorlibBinder::GetMethod(METHOD__MONITOR__ENTER));
+
+#ifdef PROFILING_SUPPORTED
+ // Note that g_profControlBlock.fBaseSystemClassesLoaded must be set to TRUE only after
+ // all base system classes are loaded. Profilers are not allowed to call any type-loading
+ // APIs until g_profControlBlock.fBaseSystemClassesLoaded is TRUE. It is important that
+ // all base system classes need to be loaded before profilers can trigger the type loading.
+ g_profControlBlock.fBaseSystemClassesLoaded = TRUE;
+#endif // PROFILING_SUPPORTED
+
+#if defined(_DEBUG) && !defined(CROSSGEN_COMPILE)
+ if (!NingenEnabled())
+ {
+ g_Mscorlib.Check();
+ }
+#endif
+
+#if defined(HAVE_GCCOVER) && defined(FEATURE_PREJIT)
+ if (GCStress<cfg_instr_ngen>::IsEnabled())
+ {
+ // Setting up gc coverage requires the base system classes
+ // to be initialized. So we have deferred it until now for mscorlib.
+ Module *pModule = MscorlibBinder::GetModule();
+ _ASSERTE(pModule->IsSystem());
+ if(pModule->HasNativeImage())
+ {
+ SetupGcCoverageForNativeImage(pModule);
+ }
+ }
+#endif // defined(HAVE_GCCOVER) && !defined(FEATURE_PREJIT)
+}
+
+/*static*/
+void SystemDomain::LoadDomain(AppDomain *pDomain)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(System()));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ pDomain->SetCanUnload(); // by default can unload any domain
+ SystemDomain::System()->AddDomain(pDomain);
+}
+
+ADIndex SystemDomain::GetNewAppDomainIndex(AppDomain *pAppDomain)
+{
+ STANDARD_VM_CONTRACT;
+
+ DWORD count = m_appDomainIndexList.GetCount();
+ DWORD i;
+
+#ifdef _DEBUG
+ if (count < 2000)
+ {
+ // So that we can keep AD index inside object header.
+ // We do not want to create syncblock unless needed.
+ i = count;
+ }
+ else
+ {
+#endif // _DEBUG
+ //
+ // Look for an unused index. Note that in a checked build,
+ // we never reuse indexes - this makes it easier to tell
+ // when we are looking at a stale app domain.
+ //
+
+ i = m_appDomainIndexList.FindElement(m_dwLowestFreeIndex, NULL);
+ if (i == (DWORD) ArrayList::NOT_FOUND)
+ i = count;
+ m_dwLowestFreeIndex = i+1;
+#ifdef _DEBUG
+ if (m_dwLowestFreeIndex >= 2000)
+ {
+ m_dwLowestFreeIndex = 0;
+ }
+ }
+#endif // _DEBUG
+
+ if (i == count)
+ IfFailThrow(m_appDomainIndexList.Append(pAppDomain));
+ else
+ m_appDomainIndexList.Set(i, pAppDomain);
+
+ _ASSERTE(i < m_appDomainIndexList.GetCount());
+
+ // Note that index 0 means domain agile.
+ return ADIndex(i+1);
+}
+
+void SystemDomain::ReleaseAppDomainIndex(ADIndex index)
+{
+ WRAPPER_NO_CONTRACT;
+ SystemDomain::LockHolder lh;
+ // Note that index 0 means domain agile.
+ index.m_dwIndex--;
+
+ _ASSERTE(m_appDomainIndexList.Get(index.m_dwIndex) != NULL);
+
+ m_appDomainIndexList.Set(index.m_dwIndex, NULL);
+
+#ifndef _DEBUG
+ if (index.m_dwIndex < m_dwLowestFreeIndex)
+ m_dwLowestFreeIndex = index.m_dwIndex;
+#endif // !_DEBUG
+}
+
+#endif // !DACCESS_COMPILE
+
+PTR_AppDomain SystemDomain::GetAppDomainAtIndex(ADIndex index)
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+ _ASSERTE(index.m_dwIndex != 0);
+
+ PTR_AppDomain pAppDomain = TestGetAppDomainAtIndex(index);
+
+ _ASSERTE(pAppDomain || !"Attempt to access unloaded app domain");
+
+ return pAppDomain;
+}
+
+PTR_AppDomain SystemDomain::TestGetAppDomainAtIndex(ADIndex index)
+{
+ LIMITED_METHOD_CONTRACT;
+ SUPPORTS_DAC;
+ _ASSERTE(index.m_dwIndex != 0);
+ index.m_dwIndex--;
+
+#ifndef DACCESS_COMPILE
+ _ASSERTE(index.m_dwIndex < (DWORD)m_appDomainIndexList.GetCount());
+ AppDomain *pAppDomain = (AppDomain*) m_appDomainIndexList.Get(index.m_dwIndex);
+#else // DACCESS_COMPILE
+ PTR_ArrayListStatic pList = &m_appDomainIndexList;
+ AppDomain *pAppDomain = dac_cast<PTR_AppDomain>(pList->Get(index.m_dwIndex));
+#endif // DACCESS_COMPILE
+ return PTR_AppDomain(pAppDomain);
+}
+
+#ifndef DACCESS_COMPILE
+
+// See also code:SystemDomain::ReleaseAppDomainId
+ADID SystemDomain::GetNewAppDomainId(AppDomain *pAppDomain)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ DWORD i = m_appDomainIdList.GetCount();
+
+ IfFailThrow(m_appDomainIdList.Append(pAppDomain));
+
+ _ASSERTE(i < m_appDomainIdList.GetCount());
+
+ return ADID(i+1);
+}
+
+AppDomain *SystemDomain::GetAppDomainAtId(ADID index)
+{
+ CONTRACTL
+ {
+#ifdef _DEBUG
+ if (!SystemDomain::IsUnderDomainLock() && !IsGCThread()) { MODE_COOPERATIVE;} else { DISABLED(MODE_ANY);}
+#endif
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ if(index.m_dwId == 0)
+ return NULL;
+ DWORD requestedID = index.m_dwId - 1;
+
+ if(requestedID >= (DWORD)m_appDomainIdList.GetCount())
+ return NULL;
+
+ AppDomain * result = (AppDomain *)m_appDomainIdList.Get(requestedID);
+
+#ifndef CROSSGEN_COMPILE
+ if(result==NULL && GetThread() == FinalizerThread::GetFinalizerThread() &&
+ SystemDomain::System()->AppDomainBeingUnloaded()!=NULL &&
+ SystemDomain::System()->AppDomainBeingUnloaded()->GetId()==index)
+ result=SystemDomain::System()->AppDomainBeingUnloaded();
+ // If the current thread can't enter the AppDomain, then don't return it.
+ if (!result || !result->CanThreadEnter(GetThread()))
+ return NULL;
+#endif // CROSSGEN_COMPILE
+
+ return result;
+}
+
+// Releases an appdomain index. Note that today we have code that depends on these
+// indexes not being recycled, so we don't actually shrink m_appDomainIdList, but
+// simply zero out an entry. THus we 'leak' the memory associated the slot in
+// m_appDomainIdList.
+//
+// TODO make this a sparse structure so that we avoid that leak.
+//
+void SystemDomain::ReleaseAppDomainId(ADID index)
+{
+ LIMITED_METHOD_CONTRACT;
+ index.m_dwId--;
+
+ _ASSERTE(index.m_dwId < (DWORD)m_appDomainIdList.GetCount());
+
+ m_appDomainIdList.Set(index.m_dwId, NULL);
+}
+
+#if defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(CROSSGEN_COMPILE)
+
+#ifdef _DEBUG
+int g_fMainThreadApartmentStateSet = 0;
+#endif
+
+Thread::ApartmentState SystemDomain::GetEntryPointThreadAptState(IMDInternalImport* pScope, mdMethodDef mdMethod)
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr;
+ IfFailThrow(hr = pScope->GetCustomAttributeByName(mdMethod,
+ DEFAULTDOMAIN_MTA_TYPE,
+ NULL,
+ NULL));
+ BOOL fIsMTA = FALSE;
+ if(hr == S_OK)
+ fIsMTA = TRUE;
+
+ IfFailThrow(hr = pScope->GetCustomAttributeByName(mdMethod,
+ DEFAULTDOMAIN_STA_TYPE,
+ NULL,
+ NULL));
+ BOOL fIsSTA = FALSE;
+ if (hr == S_OK)
+ fIsSTA = TRUE;
+
+ if (fIsSTA && fIsMTA)
+ COMPlusThrowHR(COR_E_CUSTOMATTRIBUTEFORMAT);
+
+ if (fIsSTA)
+ return Thread::AS_InSTA;
+ else if (fIsMTA)
+ return Thread::AS_InMTA;
+
+ return Thread::AS_Unknown;
+}
+
+void SystemDomain::SetThreadAptState (IMDInternalImport* pScope, Thread::ApartmentState state)
+{
+ STANDARD_VM_CONTRACT;
+
+ BOOL fIsLegacy = FALSE;
+
+ // Check for legacy behavior regarding COM Apartment state of the main thread.
+
+#define METAMODEL_MAJOR_VER_WITH_NEW_BEHAVIOR 2
+#define METAMODEL_MINOR_VER_WITH_NEW_BEHAVIOR 0
+
+ LPCSTR pVer;
+ IfFailThrow(pScope->GetVersionString(&pVer));
+
+ // Does this look like a version?
+ if (pVer != NULL)
+ {
+ // Is it 'vN.' where N is a digit?
+ if ((pVer[0] == 'v' || pVer[0] == 'V') &&
+ IS_DIGIT(pVer[1]) &&
+ (pVer[2] == '.') )
+ {
+ // Looks like a version. Is it lesser than v2.0 major version where we start using new behavior?
+ fIsLegacy = DIGIT_TO_INT(pVer[1]) < METAMODEL_MAJOR_VER_WITH_NEW_BEHAVIOR;
+ }
+ }
+
+ if (!fIsLegacy && g_pConfig != NULL)
+ {
+ fIsLegacy = g_pConfig->LegacyApartmentInitPolicy();
+ }
+
+
+ Thread* pThread = GetThread();
+ _ASSERTE(pThread);
+
+ if(state == Thread::AS_InSTA)
+ {
+ Thread::ApartmentState pState = pThread->SetApartment(Thread::AS_InSTA, TRUE);
+ _ASSERTE(pState == Thread::AS_InSTA);
+ }
+ else if ((state == Thread::AS_InMTA) || (!fIsLegacy))
+ {
+ // If either MTAThreadAttribute is specified or (if no attribute is specified and we are not
+ // running in legacy mode), then
+ // we will set the apartment state to MTA. The reason for this is to ensure the apartment
+ // state is consistent and reliably set. Without this, the apartment state for the main
+ // thread would be undefined and would actually be dependent on if the assembly was
+ // ngen'd, which other type were loaded, etc.
+ Thread::ApartmentState pState = pThread->SetApartment(Thread::AS_InMTA, TRUE);
+ _ASSERTE(pState == Thread::AS_InMTA);
+ }
+
+#ifdef _DEBUG
+ g_fMainThreadApartmentStateSet++;
+#endif
+}
+#endif // defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(CROSSGEN_COMPILE)
+
+// Looks in all the modules for the DefaultDomain attribute
+// The order is assembly and then the modules. It is first
+// come, first serve.
+BOOL SystemDomain::SetGlobalSharePolicyUsingAttribute(IMDInternalImport* pScope, mdMethodDef mdMethod)
+{
+ STANDARD_VM_CONTRACT;
+
+#ifdef FEATURE_FUSION
+ HRESULT hr;
+
+ //
+ // Check to see if the assembly has the LoaderOptimization attribute set.
+ //
+
+ DWORD cbVal;
+ BYTE *pVal;
+ IfFailThrow(hr = pScope->GetCustomAttributeByName(mdMethod,
+ DEFAULTDOMAIN_LOADEROPTIMIZATION_TYPE,
+ (const void**)&pVal, &cbVal));
+
+ if (hr == S_OK) {
+ CustomAttributeParser cap(pVal, cbVal);
+ IfFailThrow(cap.SkipProlog());
+
+ UINT8 u1;
+ IfFailThrow(cap.GetU1(&u1));
+
+ g_dwGlobalSharePolicy = u1 & AppDomain::SHARE_POLICY_MASK;
+
+ return TRUE;
+ }
+#endif
+
+ return FALSE;
+}
+
+void SystemDomain::SetupDefaultDomain()
+{
+ CONTRACT_VOID
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+
+ Thread *pThread = GetThread();
+ _ASSERTE(pThread);
+
+ AppDomain *pDomain;
+ pDomain = pThread->GetDomain();
+ _ASSERTE(pDomain);
+
+ GCX_COOP();
+
+ ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(),ADV_DEFAULTAD)
+ {
+ // Push this frame around loading the main assembly to ensure the
+ // debugger can properly recgonize any managed code that gets run
+ // as "class initializaion" code.
+ FrameWithCookie<DebuggerClassInitMarkFrame> __dcimf;
+
+ {
+ GCX_PREEMP();
+ InitializeDefaultDomain(TRUE);
+ }
+
+ __dcimf.Pop();
+ }
+ END_DOMAIN_TRANSITION;
+
+ RETURN;
+}
+
+HRESULT SystemDomain::SetupDefaultDomainNoThrow()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ SystemDomain::SetupDefaultDomain();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+}
+
+#ifdef _DEBUG
+int g_fInitializingInitialAD = 0;
+#endif
+
+// This routine completes the initialization of the default domaine.
+// After this call mananged code can be executed.
+void SystemDomain::InitializeDefaultDomain(
+ BOOL allowRedirects
+#ifdef FEATURE_HOSTED_BINDER
+ , ICLRPrivBinder * pBinder
+#endif
+ )
+{
+ STANDARD_VM_CONTRACT;
+
+ WCHAR* pwsConfig = NULL;
+ WCHAR* pwsPath = NULL;
+
+ ETWOnStartup (InitDefaultDomain_V1, InitDefaultDomainEnd_V1);
+
+#if defined(FEATURE_FUSION) // SxS
+ // Determine the application base and the configuration file name
+ CQuickWSTR sPathName;
+ CQuickWSTR sConfigName;
+
+ SIZE_T dwSize;
+ HRESULT hr = GetConfigFileFromWin32Manifest(sConfigName.Ptr(),
+ sConfigName.MaxSize(),
+ &dwSize);
+ if(FAILED(hr))
+ {
+ if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ sConfigName.ReSizeThrows(dwSize);
+ hr = GetConfigFileFromWin32Manifest(sConfigName.Ptr(),
+ sConfigName.MaxSize(),
+ &dwSize);
+ }
+ IfFailThrow(hr);
+ }
+ else
+ sConfigName.ReSizeThrows(dwSize);
+
+ hr = GetApplicationPathFromWin32Manifest(sPathName.Ptr(),
+ sPathName.MaxSize(),
+ &dwSize);
+ if(FAILED(hr))
+ {
+ if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ sPathName.ReSizeThrows(dwSize);
+ hr = GetApplicationPathFromWin32Manifest(sPathName.Ptr(),
+ sPathName.MaxSize(),
+ &dwSize);
+ }
+ IfFailThrow(hr);
+ }
+ else
+ sPathName.ReSizeThrows(dwSize);
+
+ pwsConfig = (sConfigName.Size() > 0 ? sConfigName.Ptr() : NULL);
+ pwsPath = (sPathName.Size() > 0 ? sPathName.Ptr() : NULL);
+#endif // defined(FEATURE_FUSION) // SxS
+
+ // Setup the default AppDomain.
+
+#ifdef _DEBUG
+ g_fInitializingInitialAD++;
+#endif
+
+ AppDomain* pDefaultDomain = SystemDomain::System()->DefaultDomain();
+
+#ifdef FEATURE_HOSTED_BINDER
+ if (pBinder != nullptr)
+ {
+ pDefaultDomain->SetLoadContextHostBinder(pBinder);
+ }
+ #ifdef FEATURE_APPX_BINDER
+ else if (AppX::IsAppXProcess())
+ {
+ CLRPrivBinderAppX * pAppXBinder = CLRPrivBinderAppX::GetOrCreateBinder();
+ pDefaultDomain->SetLoadContextHostBinder(pAppXBinder);
+ }
+ #endif
+#endif
+
+ {
+ GCX_COOP();
+
+#ifndef CROSSGEN_COMPILE
+ if (!NingenEnabled())
+ {
+#ifndef FEATURE_CORECLR
+ pDefaultDomain->InitializeHashing(NULL);
+ pDefaultDomain->InitializeSorting(NULL);
+#endif // FEATURE_CORECLR
+ }
+#endif // CROSSGEN_COMPILE
+
+ pDefaultDomain->InitializeDomainContext(allowRedirects, pwsPath, pwsConfig);
+
+#ifndef CROSSGEN_COMPILE
+ if (!NingenEnabled())
+ {
+#ifdef FEATURE_CLICKONCE
+ pDefaultDomain->InitializeDefaultClickOnceDomain();
+#endif // FEATURE_CLICKONCE
+
+ if (!IsSingleAppDomain())
+ {
+ pDefaultDomain->InitializeDefaultDomainManager();
+ pDefaultDomain->InitializeDefaultDomainSecurity();
+ }
+ }
+#endif // CROSSGEN_COMPILE
+ }
+
+ // DefaultDomain Load event
+ ETW::LoaderLog::DomainLoad(pDefaultDomain);
+
+#ifdef _DEBUG
+ g_fInitializingInitialAD--;
+#endif
+
+ TESTHOOKCALL(RuntimeStarted(RTS_DEFAULTADREADY));
+}
+
+
+
+#ifndef CROSSGEN_COMPILE
+
+#ifdef _DEBUG
+Volatile<LONG> g_fInExecuteMainMethod = 0;
+#endif
+
+#ifndef FEATURE_CORECLR
+void SystemDomain::ExecuteMainMethod(HMODULE hMod, __in_opt LPWSTR path /*=NULL*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(hMod, NULL_OK));
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ CounterHolder counter(&g_fInExecuteMainMethod);
+#endif
+
+ Thread *pThread = GetThread();
+ _ASSERTE(pThread);
+
+ GCX_COOP();
+
+ //
+ // There is no EH protecting this transition!
+ // This is generically ok in this method because if we throw out of here, it becomes unhandled anyway.
+ //
+ FrameWithCookie<ContextTransitionFrame> frame;
+ pThread->EnterContextRestricted(SystemDomain::System()->DefaultDomain()->GetDefaultContext(), &frame);
+ _ASSERTE(pThread->GetDomain());
+
+ AppDomain *pDomain = GetAppDomain();
+ _ASSERTE(pDomain);
+
+ // Push this frame around loading the main assembly to ensure the
+ // debugger can properly recognize any managed code that gets run
+ // as "class initializaion" code.
+ FrameWithCookie<DebuggerClassInitMarkFrame> __dcimf;
+ {
+ GCX_PREEMP();
+
+ PEImageHolder pTempImage(PEImage::LoadImage(hMod));
+
+ PEFileHolder pTempFile(PEFile::Open(pTempImage.Extract()));
+
+ // Check for CustomAttributes - Set up the DefaultDomain and the main thread
+ // Note that this has to be done before ExplicitBind() as it
+ // affects the bind
+ mdToken tkEntryPoint = pTempFile->GetEntryPointToken();
+ // <TODO>@TODO: What if the entrypoint is in another file of the assembly?</TODO>
+ ReleaseHolder<IMDInternalImport> scope(pTempFile->GetMDImportWithRef());
+ // In theory, we should have a valid executable image and scope should never be NULL, but we've been
+ // getting Watson failures for AVs here due to ISVs modifying image headers and some new OS loader
+ // checks (see Dev10# 718530 and Windows 7# 615596)
+ if (scope == NULL)
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+#ifdef FEATURE_COMINTEROP
+ Thread::ApartmentState state = Thread::AS_Unknown;
+
+ if((!IsNilToken(tkEntryPoint)) && (TypeFromToken(tkEntryPoint) == mdtMethodDef)) {
+ if (scope->IsValidToken(tkEntryPoint))
+ state = SystemDomain::GetEntryPointThreadAptState(scope, tkEntryPoint);
+ else
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+ // If the entry point has an explicit thread apartment state, set it
+ // before running the AppDomainManager initialization code.
+ if (state == Thread::AS_InSTA || state == Thread::AS_InMTA)
+ SystemDomain::SetThreadAptState(scope, state);
+#endif // FEATURE_COMINTEROP
+
+ BOOL fSetGlobalSharePolicyUsingAttribute = FALSE;
+
+ if((!IsNilToken(tkEntryPoint)) && (TypeFromToken(tkEntryPoint) == mdtMethodDef))
+ {
+ // The global share policy needs to be set before initializing default domain
+ // so that it is in place for loading of appdomain manager.
+ fSetGlobalSharePolicyUsingAttribute = SystemDomain::SetGlobalSharePolicyUsingAttribute(scope, tkEntryPoint);
+ }
+
+ // This can potentially run managed code.
+ InitializeDefaultDomain(FALSE);
+
+#ifdef FEATURE_COMINTEROP
+ // If we haven't set an explicit thread apartment state, set it after the
+ // AppDomainManager has got a chance to go set it in InitializeNewDomain.
+ if (state != Thread::AS_InSTA && state != Thread::AS_InMTA)
+ SystemDomain::SetThreadAptState(scope, state);
+#endif // FEATURE_COMINTEROP
+
+ if (fSetGlobalSharePolicyUsingAttribute)
+ SystemDomain::System()->DefaultDomain()->SetupLoaderOptimization(g_dwGlobalSharePolicy);
+
+ NewHolder<IPEFileSecurityDescriptor> pSecDesc(Security::CreatePEFileSecurityDescriptor(pDomain, pTempFile));
+
+ {
+ GCX_COOP();
+ pSecDesc->Resolve();
+ if (pSecDesc->AllowBindingRedirects())
+ pDomain->TurnOnBindingRedirects();
+ }
+
+ PEAssemblyHolder pFile(pDomain->BindExplicitAssembly(hMod, TRUE));
+
+ pDomain->m_pRootAssembly = GetAppDomain()->LoadAssembly(NULL, pFile, FILE_ACTIVE);
+
+ {
+ GCX_COOP();
+
+ // Reuse the evidence that was generated for the PEFile for the assembly so we don't have to
+ // regenerate evidence of the same type again if it is requested later.
+ pDomain->m_pRootAssembly->GetSecurityDescriptor()->SetEvidenceFromPEFile(pSecDesc);
+ }
+
+ // If the AppDomainManager for the default domain was specified in the application config file then
+ // we require that the assembly be trusted in order to set the manager
+ if (pDomain->HasAppDomainManagerInfo() && pDomain->AppDomainManagerSetFromConfig())
+ {
+ Assembly *pEntryAssembly = pDomain->GetAppDomainManagerEntryAssembly();
+ if (!pEntryAssembly->GetSecurityDescriptor()->AllowApplicationSpecifiedAppDomainManager())
+ {
+ COMPlusThrow(kTypeLoadException, IDS_E_UNTRUSTED_APPDOMAIN_MANAGER);
+ }
+ }
+
+ if (CorCommandLine::m_pwszAppFullName == NULL) {
+ StackSString friendlyName;
+ StackSString assemblyPath = pFile->GetPath();
+ SString::Iterator i = assemblyPath.End();
+
+ if (PEAssembly::FindLastPathSeparator(assemblyPath, i)) {
+ i++;
+ friendlyName.Set(assemblyPath, i, assemblyPath.End());
+ }
+ else
+ friendlyName.Set(assemblyPath);
+
+ pDomain->SetFriendlyName(friendlyName, TRUE);
+ }
+ }
+ __dcimf.Pop();
+
+ {
+ GCX_PREEMP();
+
+ LOG((LF_CLASSLOADER | LF_CORDB,
+ LL_INFO10,
+ "Created domain for an executable at %p\n",
+ (pDomain->m_pRootAssembly ? pDomain->m_pRootAssembly->Parent() : NULL)));
+ TESTHOOKCALL(RuntimeStarted(RTS_CALLINGENTRYPOINT));
+
+#ifdef FEATURE_MULTICOREJIT
+ pDomain->GetMulticoreJitManager().AutoStartProfile(pDomain);
+#endif
+
+ pDomain->m_pRootAssembly->ExecuteMainMethod(NULL);
+ }
+
+ pThread->ReturnToContext(&frame);
+
+#ifdef FEATURE_TESTHOOKS
+ TESTHOOKCALL(LeftAppDomain(DefaultADID));
+#endif
+}
+#endif //!FEATURE_CORECLR
+
+#ifdef FEATURE_CLICKONCE
+void SystemDomain::ActivateApplication(int *pReturnValue)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ struct _gc {
+ OBJECTREF orThis;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCX_COOP();
+ GCPROTECT_BEGIN(gc);
+
+ gc.orThis = SystemDomain::System()->DefaultDomain()->GetExposedObject();
+
+ MethodDescCallSite activateApp(METHOD__APP_DOMAIN__ACTIVATE_APPLICATION, &gc.orThis);
+
+ ARG_SLOT args[] = {
+ ObjToArgSlot(gc.orThis),
+ };
+ int retval = activateApp.Call_RetI4(args);
+ if (pReturnValue)
+ *pReturnValue = retval;
+
+ GCPROTECT_END();
+}
+#endif // FEATURE_CLICKONCE
+
+#ifdef FEATURE_MIXEDMODE
+static HRESULT RunDllMainHelper(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved, Thread* pThread, bool bReenablePreemptive)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ STATIC_CONTRACT_FAULT;
+
+ MethodDesc *pMD;
+ AppDomain *pDomain;
+ Module *pModule;
+ HRESULT hr = S_FALSE; // Assume no entry point.
+
+ // Setup the thread state to cooperative to run managed code.
+
+ // Get the old domain from the thread. Legacy dll entry points must always
+ // be run from the default domain.
+ //
+ // We cannot support legacy dlls getting loaded into all domains!!
+ EX_TRY
+ {
+ ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(),ADV_DEFAULTAD)
+ {
+ pDomain = pThread->GetDomain();
+
+ // The module needs to be in the current list if you are coming here.
+ pModule = pDomain->GetIJWModule(hInst);
+ if (!pModule)
+ goto ErrExit;
+
+ // See if there even is an entry point.
+ pMD = pModule->GetDllEntryPoint();
+ if (!pMD)
+ goto ErrExit;
+
+ // We're actually going to run some managed code. There may be a customer
+ // debug probe enabled, that prevents execution in the loader lock.
+ CanRunManagedCode(hInst);
+
+ {
+ // Enter cooperative mode
+ GCX_COOP_NO_DTOR();
+ }
+
+ // Run through the helper which will do exception handling for us.
+ hr = ::RunDllMain(pMD, hInst, dwReason, lpReserved);
+
+ {
+ // Update thread state for the case where we are returning to unmanaged code.
+ GCX_MAYBE_PREEMP_NO_DTOR(bReenablePreemptive);
+ }
+
+ErrExit: ;
+ // does not throw exception
+ }
+ END_DOMAIN_TRANSITION;
+
+ }
+ EX_CATCH
+ {
+ hr = GetExceptionHResult(GET_THROWABLE());
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+ return (hr);
+}
+
+//*****************************************************************************
+// This guy will set up the proper thread state, look for the module given
+// the hinstance, and then run the entry point if there is one.
+//*****************************************************************************
+HRESULT SystemDomain::RunDllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
+{
+
+ CONTRACTL
+ {
+ NOTHROW;
+ if (GetThread() && !lpReserved) {MODE_PREEMPTIVE;} else {DISABLED(MODE_PREEMPTIVE);};
+ if(GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);};
+ }
+ CONTRACTL_END;
+
+
+ Thread *pThread = NULL;
+ BOOL fEnterCoop = FALSE;
+ HRESULT hr = S_FALSE; // Assume no entry point.
+
+ pThread = GetThread();
+ if ((!pThread && (dwReason == DLL_PROCESS_DETACH || dwReason == DLL_THREAD_DETACH)) ||
+ g_fEEShutDown)
+ return S_OK;
+
+ // ExitProcess is called while a thread is doing GC.
+ if (dwReason == DLL_PROCESS_DETACH && GCHeap::IsGCInProgress())
+ return S_OK;
+
+ // ExitProcess is called on a thread that we don't know about
+ if (dwReason == DLL_PROCESS_DETACH && GetThread() == NULL)
+ return S_OK;
+
+ // Need to setup the thread since this might be the first time the EE has
+ // seen it if the thread was created in unmanaged code and this is a thread
+ // attach event.
+ if (pThread)
+ fEnterCoop = pThread->PreemptiveGCDisabled();
+ else {
+ pThread = SetupThreadNoThrow(&hr);
+ if (pThread == NULL)
+ return hr;
+ }
+
+ return RunDllMainHelper(hInst, dwReason, lpReserved, pThread, !fEnterCoop);
+}
+#endif // FEATURE_MIXEDMODE
+
+#endif // CROSSGEN_COMPILE
+
+
+
+// Helper function to load an assembly. This is called from LoadCOMClass.
+/* static */
+
+Assembly *AppDomain::LoadAssemblyHelper(LPCWSTR wszAssembly,
+ LPCWSTR wszCodeBase)
+{
+ CONTRACT(Assembly *)
+ {
+ THROWS;
+ POSTCONDITION(CheckPointer(RETVAL));
+ PRECONDITION(wszAssembly || wszCodeBase);
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ AssemblySpec spec;
+ if(wszAssembly) {
+ #define MAKE_TRANSLATIONFAILED { ThrowOutOfMemory(); }
+ MAKE_UTF8PTR_FROMWIDE(szAssembly,wszAssembly);
+ #undef MAKE_TRANSLATIONFAILED
+
+ IfFailThrow(spec.Init(szAssembly));
+ }
+
+ if (wszCodeBase) {
+ spec.SetCodeBase(wszCodeBase);
+ }
+ RETURN spec.LoadAssembly(FILE_LOADED);
+}
+
+#if defined(FEATURE_CLASSIC_COMINTEROP) && !defined(CROSSGEN_COMPILE)
+
+#ifdef FEATURE_CORECLR
+MethodTable *AppDomain::LoadCOMClass(GUID clsid,
+ BOOL bLoadRecord/*=FALSE*/,
+ BOOL* pfAssemblyInReg/*=NULL*/)
+{
+ // @CORESYSTODO: what to do here?
+ return NULL;
+}
+#else // FEATURE_CORECLR
+
+static BOOL IsSameRuntimeVersion(ICLRRuntimeInfo *pInfo1, ICLRRuntimeInfo *pInfo2)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ WCHAR wszVersion1[_MAX_PATH];
+ WCHAR wszVersion2[_MAX_PATH];
+ DWORD cchVersion;
+
+ cchVersion = COUNTOF(wszVersion1);
+ IfFailThrow(pInfo1->GetVersionString(wszVersion1, &cchVersion));
+
+ cchVersion = COUNTOF(wszVersion2);
+ IfFailThrow(pInfo2->GetVersionString(wszVersion2, &cchVersion));
+
+ return SString::_wcsicmp(wszVersion1, wszVersion2) == 0;
+}
+
+MethodTable *AppDomain::LoadCOMClass(GUID clsid,
+ BOOL bLoadRecord/*=FALSE*/,
+ BOOL* pfAssemblyInReg/*=NULL*/)
+{
+ CONTRACT (MethodTable*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+
+ MethodTable* pMT = NULL;
+
+ NewArrayHolder<WCHAR> wszClassName = NULL;
+ NewArrayHolder<WCHAR> wszAssemblyString = NULL;
+ NewArrayHolder<WCHAR> wszCodeBaseString = NULL;
+
+ DWORD cbAssembly = 0;
+ DWORD cbCodeBase = 0;
+ Assembly *pAssembly = NULL;
+ BOOL fFromRegistry = FALSE;
+ BOOL fRegFreePIA = FALSE;
+
+ HRESULT hr = S_OK;
+
+ if (pfAssemblyInReg != NULL)
+ *pfAssemblyInReg = FALSE;
+
+ // with sxs.dll help
+ hr = FindShimInfoFromWin32(clsid, bLoadRecord, NULL, NULL, &wszClassName, &wszAssemblyString, &fRegFreePIA);
+
+ if(FAILED(hr))
+ {
+ hr = FindShimInfoFromRegistry(clsid, bLoadRecord, VER_ASSEMBLYMAJORVERSION, VER_ASSEMBLYMINORVERSION,
+ &wszClassName, &wszAssemblyString, &wszCodeBaseString);
+ if (FAILED(hr))
+ RETURN NULL;
+
+ fFromRegistry = TRUE;
+ }
+
+ // Skip the GetRuntimeForManagedCOMObject check for value types since they cannot be activated and are
+ // always used for wrapping existing instances coming from COM.
+ if (!bLoadRecord)
+ {
+ // We will load the assembly only if it is a PIA or if unmanaged activation would load the currently running
+ // runtime. Otherwise we return NULL which will result in using the default System.__ComObject type.
+
+ // the type is a PIA type if mscoree.dll is not its inproc server dll or it was specified as <clrSurrogate> in the manifest
+ BOOL fPIA = (fFromRegistry ? !Clr::Util::Com::CLSIDHasMscoreeAsInprocServer32(clsid) : fRegFreePIA);
+ if (!fPIA)
+ {
+ // this isn't a PIA, so we must determine which runtime it would load
+ ReleaseHolder<ICLRRuntimeHostInternal> pRuntimeHostInternal;
+ IfFailThrow(g_pCLRRuntime->GetInterface(CLSID_CLRRuntimeHostInternal,
+ IID_ICLRRuntimeHostInternal,
+ &pRuntimeHostInternal));
+
+ // we call the shim to see which runtime would this be activated in
+ ReleaseHolder<ICLRRuntimeInfo> pRuntimeInfo;
+ if (FAILED(pRuntimeHostInternal->GetRuntimeForManagedCOMObject(clsid, IID_ICLRRuntimeInfo, &pRuntimeInfo)))
+ {
+ // the requested runtime is not loadable - don't load the assembly
+ RETURN NULL;
+ }
+
+ if (!IsSameRuntimeVersion(g_pCLRRuntime, pRuntimeInfo))
+ {
+ // the requested runtime is different from this runtime - don't load the assembly
+ RETURN NULL;
+ }
+ }
+ }
+
+ if (pfAssemblyInReg != NULL)
+ *pfAssemblyInReg = TRUE;
+
+ if (wszAssemblyString != NULL) {
+ pAssembly = LoadAssemblyHelper(wszAssemblyString, wszCodeBaseString);
+ pMT = TypeName::GetTypeFromAssembly(wszClassName, pAssembly).GetMethodTable();
+ if (!pMT)
+ goto ErrExit;
+ }
+
+ if (pMT == NULL) {
+ ErrExit:
+ // Convert the GUID to its string representation.
+ WCHAR szClsid[64];
+ if (GuidToLPWSTR(clsid, szClsid, NumItems(szClsid)) == 0)
+ szClsid[0] = 0;
+
+ // Throw an exception indicating we failed to load the type with
+ // the requested CLSID.
+ COMPlusThrow(kTypeLoadException, IDS_CLASSLOAD_NOCLSIDREG, szClsid);
+ }
+
+ RETURN pMT;
+}
+
+#endif // FEATURE_CORECLR
+
+#endif // FEATURE_CLASSIC_COMINTEROP && !CROSSGEN_COMPILE
+
+
+/*static*/
+bool SystemDomain::IsReflectionInvocationMethod(MethodDesc* pMeth)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ MethodTable* pCaller = pMeth->GetMethodTable();
+
+ // All Reflection Invocation methods are defined in mscorlib.dll
+ if (!pCaller->GetModule()->IsSystem())
+ return false;
+
+ /* List of types that should be skipped to identify true caller */
+ static const BinderClassID reflectionInvocationTypes[] = {
+ CLASS__METHOD,
+ CLASS__METHOD_BASE,
+ CLASS__METHOD_INFO,
+ CLASS__CONSTRUCTOR,
+ CLASS__CONSTRUCTOR_INFO,
+ CLASS__CLASS,
+ CLASS__TYPE_HANDLE,
+ CLASS__METHOD_HANDLE,
+ CLASS__FIELD_HANDLE,
+ CLASS__TYPE,
+ CLASS__FIELD,
+ CLASS__RT_FIELD_INFO,
+ CLASS__FIELD_INFO,
+ CLASS__EVENT,
+ CLASS__EVENT_INFO,
+ CLASS__PROPERTY,
+ CLASS__PROPERTY_INFO,
+ CLASS__ACTIVATOR,
+ CLASS__ARRAY,
+ CLASS__ASSEMBLYBASE,
+ CLASS__ASSEMBLY,
+ CLASS__TYPE_DELEGATOR,
+ CLASS__RUNTIME_HELPERS,
+#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORECLR)
+ CLASS__ITYPE,
+ CLASS__IASSEMBLY,
+ CLASS__IMETHODBASE,
+ CLASS__IMETHODINFO,
+ CLASS__ICONSTRUCTORINFO,
+ CLASS__IFIELDINFO,
+ CLASS__IPROPERTYINFO,
+ CLASS__IEVENTINFO,
+ CLASS__IAPPDOMAIN,
+#endif // FEATURE_COMINTEROP && !FEATURE_CORECLR
+ CLASS__LAZY_INITIALIZER,
+ CLASS__DYNAMICMETHOD,
+ CLASS__DELEGATE,
+ CLASS__MULTICAST_DELEGATE
+ };
+
+ static const BinderClassID genericReflectionInvocationTypes[] = {
+ CLASS__LAZY_HELPERS,
+ CLASS__LAZY
+ };
+
+ static mdTypeDef genericReflectionInvocationTypeDefs[NumItems(genericReflectionInvocationTypes)];
+
+ static bool fInited = false;
+
+ if (!VolatileLoad(&fInited))
+ {
+ // Make sure all types are loaded so that we can use faster GetExistingClass()
+ for (unsigned i = 0; i < NumItems(reflectionInvocationTypes); i++)
+ {
+ MscorlibBinder::GetClass(reflectionInvocationTypes[i]);
+ }
+
+ // Make sure all types are loaded so that we can use faster GetExistingClass()
+ for (unsigned i = 0; i < NumItems(genericReflectionInvocationTypes); i++)
+ {
+ genericReflectionInvocationTypeDefs[i] = MscorlibBinder::GetClass(genericReflectionInvocationTypes[i])->GetCl();
+ }
+
+ MscorlibBinder::GetClass(CLASS__APP_DOMAIN);
+
+ VolatileStore(&fInited, true);
+ }
+
+ if (pCaller->HasInstantiation())
+ {
+ // For generic types, pCaller will be an instantiated type and never equal to the type definition.
+ // So we compare their TypeDef tokens instead.
+ for (unsigned i = 0; i < NumItems(genericReflectionInvocationTypeDefs); i++)
+ {
+ if (pCaller->GetCl() == genericReflectionInvocationTypeDefs[i])
+ return true;
+ }
+ }
+ else
+ {
+ for (unsigned i = 0; i < NumItems(reflectionInvocationTypes); i++)
+ {
+ if (MscorlibBinder::GetExistingClass(reflectionInvocationTypes[i]) == pCaller)
+ return true;
+ }
+
+ // AppDomain is an example of a type that is both used in the implementation of
+ // reflection, and also a type that contains methods that are clients of reflection
+ // (i.e., they instigate their own CreateInstance). Skip all AppDomain frames that
+ // are NOT known clients of reflection. NOTE: The ever-increasing complexity of this
+ // exclusion list is a sign that we need a better way--this is error-prone and
+ // unmaintainable as more changes are made to BCL types.
+ if ((pCaller == MscorlibBinder::GetExistingClass(CLASS__APP_DOMAIN))
+ && (pMeth != MscorlibBinder::GetMethod(METHOD__APP_DOMAIN__CREATE_APP_DOMAIN_MANAGER)) // This uses reflection to create an AppDomainManager
+ #ifdef FEATURE_CLICKONCE
+ && (pMeth != MscorlibBinder::GetMethod(METHOD__APP_DOMAIN__ACTIVATE_APPLICATION)) // This uses reflection to create an ActivationContext
+ #endif
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifndef CROSSGEN_COMPILE
+struct CallersDataWithStackMark
+{
+ StackCrawlMark* stackMark;
+ BOOL foundMe;
+#ifdef FEATURE_REMOTING
+ BOOL skippingRemoting;
+#endif
+ MethodDesc* pFoundMethod;
+ MethodDesc* pPrevMethod;
+ AppDomain* pAppDomain;
+};
+
+/*static*/
+MethodDesc* SystemDomain::GetCallersMethod(StackCrawlMark* stackMark,
+ AppDomain **ppAppDomain/*=NULL*/)
+
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+
+ CallersDataWithStackMark cdata;
+ ZeroMemory(&cdata, sizeof(CallersDataWithStackMark));
+ cdata.stackMark = stackMark;
+
+ GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND);
+
+ if(cdata.pFoundMethod) {
+ if (ppAppDomain)
+ *ppAppDomain = cdata.pAppDomain;
+ return cdata.pFoundMethod;
+ } else
+ return NULL;
+}
+
+/*static*/
+MethodTable* SystemDomain::GetCallersType(StackCrawlMark* stackMark,
+ AppDomain **ppAppDomain/*=NULL*/)
+
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ CallersDataWithStackMark cdata;
+ ZeroMemory(&cdata, sizeof(CallersDataWithStackMark));
+ cdata.stackMark = stackMark;
+
+ GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND);
+
+ if(cdata.pFoundMethod) {
+ if (ppAppDomain)
+ *ppAppDomain = cdata.pAppDomain;
+ return cdata.pFoundMethod->GetMethodTable();
+ } else
+ return NULL;
+}
+
+/*static*/
+Module* SystemDomain::GetCallersModule(StackCrawlMark* stackMark,
+ AppDomain **ppAppDomain/*=NULL*/)
+
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+
+ CallersDataWithStackMark cdata;
+ ZeroMemory(&cdata, sizeof(CallersDataWithStackMark));
+ cdata.stackMark = stackMark;
+
+ GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND);
+
+ if(cdata.pFoundMethod) {
+ if (ppAppDomain)
+ *ppAppDomain = cdata.pAppDomain;
+ return cdata.pFoundMethod->GetModule();
+ } else
+ return NULL;
+}
+
+struct CallersData
+{
+ int skip;
+ MethodDesc* pMethod;
+};
+
+/*static*/
+Assembly* SystemDomain::GetCallersAssembly(StackCrawlMark *stackMark,
+ AppDomain **ppAppDomain/*=NULL*/)
+{
+ WRAPPER_NO_CONTRACT;
+ Module* mod = GetCallersModule(stackMark, ppAppDomain);
+ if (mod)
+ return mod->GetAssembly();
+ return NULL;
+}
+
+/*static*/
+Module* SystemDomain::GetCallersModule(int skip)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+
+ CallersData cdata;
+ ZeroMemory(&cdata, sizeof(CallersData));
+ cdata.skip = skip;
+
+ StackWalkFunctions(GetThread(), CallersMethodCallback, &cdata);
+
+ if(cdata.pMethod)
+ return cdata.pMethod->GetModule();
+ else
+ return NULL;
+}
+
+/*private static*/
+StackWalkAction SystemDomain::CallersMethodCallbackWithStackMark(CrawlFrame* pCf, VOID* data)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ SO_INTOLERANT;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+
+ MethodDesc *pFunc = pCf->GetFunction();
+
+ /* We asked to be called back only for functions */
+ _ASSERTE(pFunc);
+
+ CallersDataWithStackMark* pCaller = (CallersDataWithStackMark*) data;
+ if (pCaller->stackMark)
+ {
+ if (!pCf->IsInCalleesFrames(pCaller->stackMark))
+ {
+ // save the current in case it is the one we want
+ pCaller->pPrevMethod = pFunc;
+ pCaller->pAppDomain = pCf->GetAppDomain();
+ return SWA_CONTINUE;
+ }
+
+ // LookForMe stack crawl marks needn't worry about reflection or
+ // remoting frames on the stack. Each frame above (newer than) the
+ // target will be captured by the logic above. Once we transition to
+ // finding the stack mark below the AofRA, we know that we hit the
+ // target last time round and immediately exit with the cached result.
+
+ if (*(pCaller->stackMark) == LookForMe)
+ {
+ pCaller->pFoundMethod = pCaller->pPrevMethod;
+ return SWA_ABORT;
+ }
+ }
+
+ // Skip reflection and remoting frames that could lie between a stack marked
+ // method and its true caller (or that caller and its own caller). These
+ // frames are infrastructure and logically transparent to the stack crawling
+ // algorithm.
+
+ // Skipping remoting frames. We always skip entire client to server spans
+ // (though we see them in the order server then client during a stack crawl
+ // obviously).
+
+ // We spot the server dispatcher end because all calls are dispatched
+ // through a single method: StackBuilderSink._PrivateProcessMessage.
+
+ Frame* frame = pCf->GetFrame();
+ _ASSERTE(pCf->IsFrameless() || frame);
+
+#ifdef FEATURE_REMOTING
+ if (pFunc == MscorlibBinder::GetMethod(METHOD__STACK_BUILDER_SINK__PRIVATE_PROCESS_MESSAGE))
+ {
+ _ASSERTE(!pCaller->skippingRemoting);
+ pCaller->skippingRemoting = true;
+ return SWA_CONTINUE;
+ }
+ // And we spot the client end because there's a transparent proxy transition
+ // frame pushed.
+ if (frame && frame->GetFrameType() == Frame::TYPE_TP_METHOD_FRAME)
+ {
+ pCaller->skippingRemoting = false;
+ return SWA_CONTINUE;
+ }
+
+ // Skip any frames into between the server and client remoting endpoints.
+ if (pCaller->skippingRemoting)
+ return SWA_CONTINUE;
+#endif
+
+
+ // Skipping reflection frames. We don't need to be quite as exhaustive here
+ // as the security or reflection stack walking code since we know this logic
+ // is only invoked for selected methods in mscorlib itself. So we're
+ // reasonably sure we won't have any sensitive methods late bound invoked on
+ // constructors, properties or events. This leaves being invoked via
+ // MethodInfo, Type or Delegate (and depending on which invoke overload is
+ // being used, several different reflection classes may be involved).
+
+ g_IBCLogger.LogMethodDescAccess(pFunc);
+
+ if (SystemDomain::IsReflectionInvocationMethod(pFunc))
+ return SWA_CONTINUE;
+
+ if (frame && frame->GetFrameType() == Frame::TYPE_MULTICAST)
+ {
+ // This must be either a secure delegate frame or a true multicast delegate invocation.
+
+ _ASSERTE(pFunc->GetMethodTable()->IsDelegate());
+
+ DELEGATEREF del = (DELEGATEREF)((SecureDelegateFrame*)frame)->GetThis(); // This can throw.
+
+ if (COMDelegate::IsSecureDelegate(del))
+ {
+ if (del->IsWrapperDelegate())
+ {
+ // On ARM, we use secure delegate infrastructure to preserve R4 register.
+ return SWA_CONTINUE;
+ }
+ // For a secure delegate frame, we should return the delegate creator instead
+ // of the delegate method itself.
+ pFunc = (MethodDesc*) del->GetMethodPtrAux();
+ }
+ else
+ {
+ _ASSERTE(COMDelegate::IsTrueMulticastDelegate(del));
+ return SWA_CONTINUE;
+ }
+ }
+
+ // Return the first non-reflection/remoting frame if no stack mark was
+ // supplied.
+ if (!pCaller->stackMark)
+ {
+ pCaller->pFoundMethod = pFunc;
+ pCaller->pAppDomain = pCf->GetAppDomain();
+ return SWA_ABORT;
+ }
+
+ // If we got here, we must already be in the frame containing the stack mark and we are not looking for "me".
+ _ASSERTE(pCaller->stackMark &&
+ pCf->IsInCalleesFrames(pCaller->stackMark) &&
+ *(pCaller->stackMark) != LookForMe);
+
+ // When looking for caller's caller, we delay returning results for another
+ // round (the way this is structured, we will still be able to skip
+ // reflection and remoting frames between the caller and the caller's
+ // caller).
+
+ if ((*(pCaller->stackMark) == LookForMyCallersCaller) &&
+ (pCaller->pFoundMethod == NULL))
+ {
+ pCaller->pFoundMethod = pFunc;
+ return SWA_CONTINUE;
+ }
+
+#ifndef FEATURE_REMOTING
+ // If remoting is not available, we only set the caller if the crawlframe is from the same domain.
+ // Why? Because if the callerdomain is different from current domain,
+ // there have to be interop/native frames in between.
+ // For example, in the CORECLR, if we find the caller to be in a different domain, then the
+ // call into reflection is due to an unmanaged call into mscorlib. For that
+ // case, the caller really is an INTEROP method.
+ // In general, if the caller is INTEROP, we set the caller/callerdomain to be NULL
+ // (To be precise: they are already NULL and we don't change them).
+ if (pCf->GetAppDomain() == GetAppDomain())
+#endif // FEATURE_REMOTING
+ // We must either be looking for the caller, or the caller's caller when
+ // we've already found the caller (we used a non-null value in pFoundMethod
+ // simply as a flag, the correct method to return in both case is the
+ // current method).
+ {
+ pCaller->pFoundMethod = pFunc;
+ pCaller->pAppDomain = pCf->GetAppDomain();
+ }
+
+ return SWA_ABORT;
+}
+
+/*private static*/
+StackWalkAction SystemDomain::CallersMethodCallback(CrawlFrame* pCf, VOID* data)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ MethodDesc *pFunc = pCf->GetFunction();
+
+ /* We asked to be called back only for functions */
+ _ASSERTE(pFunc);
+
+ // Ignore intercepted frames
+ if(pFunc->IsInterceptedForDeclSecurity())
+ return SWA_CONTINUE;
+
+ CallersData* pCaller = (CallersData*) data;
+ if(pCaller->skip == 0) {
+ pCaller->pMethod = pFunc;
+ return SWA_ABORT;
+ }
+ else {
+ pCaller->skip--;
+ return SWA_CONTINUE;
+ }
+
+}
+#endif // CROSSGEN_COMPILE
+
+#ifdef CROSSGEN_COMPILE
+// defined in compile.cpp
+extern CompilationDomain * theDomain;
+#endif
+
+void SystemDomain::CreateDefaultDomain()
+{
+ STANDARD_VM_CONTRACT;
+
+#ifdef CROSSGEN_COMPILE
+ AppDomainRefHolder pDomain(theDomain);
+#else
+ AppDomainRefHolder pDomain(new AppDomain());
+#endif
+
+ SystemDomain::LockHolder lh;
+ pDomain->Init();
+
+ Security::SetDefaultAppDomainProperty(pDomain->GetSecurityDescriptor());
+
+ // need to make this assignment here since we'll be releasing
+ // the lock before calling AddDomain. So any other thread
+ // grabbing this lock after we release it will find that
+ // the COM Domain has already been created
+ m_pDefaultDomain = pDomain;
+ _ASSERTE (pDomain->GetId().m_dwId == DefaultADID);
+
+ // allocate a Virtual Call Stub Manager for the default domain
+ m_pDefaultDomain->InitVSD();
+
+ pDomain->SetStage(AppDomain::STAGE_OPEN);
+ pDomain.SuppressRelease();
+
+ LOG((LF_CLASSLOADER | LF_CORDB,
+ LL_INFO10,
+ "Created default domain at %p\n", m_pDefaultDomain));
+}
+
+#ifdef DEBUGGING_SUPPORTED
+
+void SystemDomain::PublishAppDomainAndInformDebugger (AppDomain *pDomain)
+{
+ CONTRACTL
+ {
+ if(!g_fEEInit) {THROWS;} else {DISABLED(NOTHROW);};
+ if(!g_fEEInit) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);};
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB, LL_INFO100, "SD::PADAID: Adding 0x%x\n", pDomain));
+
+ // Call the publisher API to add this appdomain entry to the list
+ // The publisher will handle failures, so we don't care if this succeeds or fails.
+ if (g_pDebugInterface != NULL)
+ {
+ g_pDebugInterface->AddAppDomainToIPC(pDomain);
+ }
+}
+
+#endif // DEBUGGING_SUPPORTED
+
+void SystemDomain::AddDomain(AppDomain* pDomain)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_ANY;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer((pDomain)));
+ }
+ CONTRACTL_END;
+
+ {
+ LockHolder lh;
+
+ _ASSERTE (pDomain->m_Stage != AppDomain::STAGE_CREATING);
+ if (pDomain->m_Stage == AppDomain::STAGE_READYFORMANAGEDCODE ||
+ pDomain->m_Stage == AppDomain::STAGE_ACTIVE)
+ {
+ pDomain->SetStage(AppDomain::STAGE_OPEN);
+ IncrementNumAppDomains(); // Maintain a count of app domains added to the list.
+ }
+ }
+
+ // Note that if you add another path that can reach here without calling
+ // PublishAppDomainAndInformDebugger, then you should go back & make sure
+ // that PADAID gets called. Right after this call, if not sooner.
+ LOG((LF_CORDB, LL_INFO1000, "SD::AD:Would have added domain here! 0x%x\n",
+ pDomain));
+}
+
+BOOL SystemDomain::RemoveDomain(AppDomain* pDomain)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pDomain));
+ PRECONDITION(!pDomain->IsDefaultDomain());
+ }
+ CONTRACTL_END;
+
+ // You can not remove the default domain.
+
+
+ if (!pDomain->IsActive())
+ return FALSE;
+
+ pDomain->Release();
+
+ return TRUE;
+}
+
+
+#ifdef PROFILING_SUPPORTED
+void SystemDomain::NotifyProfilerStartup()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System());
+ g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) System());
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System());
+ g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) System(), S_OK);
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System()->DefaultDomain());
+ g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) System()->DefaultDomain());
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System()->DefaultDomain());
+ g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) System()->DefaultDomain(), S_OK);
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(SharedDomain::GetDomain());
+ g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) SharedDomain::GetDomain());
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(SharedDomain::GetDomain());
+ g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) SharedDomain::GetDomain(), S_OK);
+ END_PIN_PROFILER();
+ }
+}
+
+HRESULT SystemDomain::NotifyProfilerShutdown()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System());
+ g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) System());
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System());
+ g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) System(), S_OK);
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System()->DefaultDomain());
+ g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) System()->DefaultDomain());
+ END_PIN_PROFILER();
+ }
+
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ _ASSERTE(System()->DefaultDomain());
+ g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) System()->DefaultDomain(), S_OK);
+ END_PIN_PROFILER();
+ }
+ return (S_OK);
+}
+#endif // PROFILING_SUPPORTED
+
+#ifdef FEATURE_FUSION
+static HRESULT GetVersionPath(HKEY root, __in LPWSTR key, __out LPWSTR* pDevpath, DWORD* pdwDevpath)
+{
+ CONTRACTL
+ {
+ MODE_PREEMPTIVE;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ DWORD rtn;
+ RegKeyHolder versionKey;
+ rtn = WszRegOpenKeyEx(root, key, 0, KEY_READ, &versionKey);
+ if(rtn == ERROR_SUCCESS) {
+ DWORD type;
+ DWORD cbDevpath;
+ if(WszRegQueryValueEx(versionKey, W("devpath"), 0, &type, (LPBYTE) NULL, &cbDevpath) == ERROR_SUCCESS && type == REG_SZ) {
+ *pDevpath = (LPWSTR) new (nothrow) BYTE[cbDevpath];
+ if(*pDevpath == NULL)
+ return E_OUTOFMEMORY;
+ else {
+ rtn = WszRegQueryValueEx(versionKey, W("devpath"), 0, &type, (LPBYTE) *pDevpath, &cbDevpath);
+ if ((rtn == ERROR_SUCCESS) && (type == REG_SZ))
+ *pdwDevpath = (DWORD) wcslen(*pDevpath);
+ }
+ }
+ else
+ return REGDB_E_INVALIDVALUE;
+ }
+
+ return HRESULT_FROM_WIN32(rtn);
+}
+
+// Get the developers path from the environment. This can only be set through the environment and
+// cannot be added through configuration files, registry etc. This would make it to easy for
+// developers to deploy apps that are not side by side. The environment variable should only
+// be used on developers machines where exact matching to versions makes build and testing to
+// difficult.
+void SystemDomain::GetDevpathW(__out_ecount_opt(1) LPWSTR* pDevpath, DWORD* pdwDevpath)
+{
+ CONTRACTL
+ {
+ THROWS;
+ MODE_ANY;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GCX_PREEMP();
+
+ if(g_pConfig->DeveloperInstallation() && m_fDevpath == FALSE) {
+
+ LockHolder lh;
+
+ if(m_fDevpath == FALSE) {
+ DWORD dwPath = 0;
+ dwPath = WszGetEnvironmentVariable(APPENV_DEVPATH, 0, 0);
+ if(dwPath) {
+ m_pwDevpath = (WCHAR*) new WCHAR[dwPath];
+ m_dwDevpath = WszGetEnvironmentVariable(APPENV_DEVPATH,
+ m_pwDevpath,
+ dwPath);
+ }
+ else {
+ RegKeyHolder userKey;
+ RegKeyHolder machineKey;
+
+ WCHAR pVersion[MAX_PATH];
+ DWORD dwVersion = MAX_PATH;
+ HRESULT hr = S_OK;
+ hr = FusionBind::GetVersion(pVersion, &dwVersion);
+ if(SUCCEEDED(hr)) {
+ LONG rslt;
+ rslt = WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W,0,KEY_READ, &userKey);
+ hr = HRESULT_FROM_WIN32(rslt);
+ if (SUCCEEDED(hr)) {
+ hr = GetVersionPath(userKey, pVersion, &m_pwDevpath, &m_dwDevpath);
+ }
+
+ if (FAILED(hr) && WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W,0,KEY_READ, &machineKey) == ERROR_SUCCESS) {
+ hr = GetVersionPath(machineKey, pVersion, &m_pwDevpath, &m_dwDevpath);
+ }
+ }
+ if (Assembly::FileNotFound(hr))
+ hr = S_FALSE;
+ else
+ IfFailThrow(hr);
+ }
+
+ m_fDevpath = TRUE;
+ }
+ // lh out of scope here
+ }
+
+ if(pDevpath) *pDevpath = m_pwDevpath;
+ if(pdwDevpath) *pdwDevpath = m_dwDevpath;
+ return;
+}
+#endif // FEATURE_FUSION
+
+#ifdef _DEBUG
+struct AppDomain::ThreadTrackInfo {
+ Thread *pThread;
+ CDynArray<Frame *> frameStack;
+};
+#endif // _DEBUG
+
+AppDomain::AppDomain()
+{
+ // initialize fields so the appdomain can be safely destructed
+ // shouldn't call anything that can fail here - use ::Init instead
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ m_cRef=1;
+ m_pNextInDelayedUnloadList = NULL;
+ m_pSecContext = NULL;
+ m_fRudeUnload = FALSE;
+ m_pUnloadRequestThread = NULL;
+ m_ADUnloadSink=NULL;
+
+#ifndef FEATURE_CORECLR
+ m_bUseOsSorting = RunningOnWin8();
+ m_sortVersion = DEFAULT_SORT_VERSION;
+ m_pCustomSortLibrary = NULL;
+#if _DEBUG
+ m_bSortingInitialized = FALSE;
+#endif // _DEBUG
+ m_pNlsHashProvider = NULL;
+#endif //!FEATURE_CORECLR
+
+ // Initialize Shared state. Assemblies are loaded
+ // into each domain by default.
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ m_SharePolicy = SHARE_POLICY_UNSPECIFIED;
+#endif
+
+ m_pRootAssembly = NULL;
+
+ m_pwDynamicDir = NULL;
+
+ m_dwFlags = 0;
+ m_pSecDesc = NULL;
+ m_pDefaultContext = NULL;
+#ifdef FEATURE_COMINTEROP
+ m_pComCallWrapperCache = NULL;
+ m_pRCWCache = NULL;
+ m_pRCWRefCache = NULL;
+ m_pLicenseInteropHelperMT = NULL;
+ m_COMorRemotingFlag = COMorRemoting_NotInitialized;
+ memset(m_rpCLRTypes, 0, sizeof(m_rpCLRTypes));
+ m_pSystemDll = nullptr;
+ m_pSystemRuntimeWindowsRuntimeDll = nullptr;
+ m_pSystemRuntimeWindowsRuntimeUIXamlDll = nullptr;
+ m_pSystemNumericsVectors = nullptr;
+#endif // FEATURE_COMINTEROP
+
+ m_pUMEntryThunkCache = NULL;
+
+ m_pAsyncPool = NULL;
+ m_hHandleTableBucket = NULL;
+
+ m_ExposedObject = NULL;
+ m_pComIPForExposedObject = NULL;
+
+ #ifdef _DEBUG
+ m_pThreadTrackInfoList = NULL;
+ m_TrackSpinLock = 0;
+ m_Assemblies.Debug_SetAppDomain(this);
+#endif // _DEBUG
+
+ m_dwThreadEnterCount = 0;
+ m_dwThreadsStillInAppDomain = (ULONG)-1;
+
+ m_pSecDesc = NULL;
+ m_hHandleTableBucket=NULL;
+
+ m_ExposedObject = NULL;
+
+#ifdef FEATURE_COMINTEROP
+ m_pRefDispIDCache = NULL;
+ m_hndMissing = NULL;
+#endif
+
+ m_pRefClassFactHash = NULL;
+ m_anonymouslyHostedDynamicMethodsAssembly = NULL;
+
+ m_ReversePInvokeCanEnter=TRUE;
+ m_ForceTrivialWaitOperations = false;
+ m_Stage=STAGE_CREATING;
+
+ m_bForceGCOnUnload=FALSE;
+ m_bUnloadingFromUnloadEvent=FALSE;
+#ifdef _DEBUG
+ m_dwIterHolders=0;
+ m_dwRefTakers=0;
+ m_dwCreationHolders=0;
+#endif
+
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ m_ullTotalProcessorUsage = 0;
+ m_pullAllocBytes = NULL;
+ m_pullSurvivedBytes = NULL;
+#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+#ifdef FEATURE_TYPEEQUIVALENCE
+ m_pTypeEquivalenceTable = NULL;
+#endif // FEATURE_TYPEEQUIVALENCE
+
+#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_REFLECTION_ONLY_LOAD
+ m_pReflectionOnlyWinRtBinder = NULL;
+ m_pReflectionOnlyWinRtTypeCache = NULL;
+#endif // FEATURE_REFLECTION_ONLY_LOAD
+ m_pNameToTypeMap = NULL;
+ m_vNameToTypeMapVersion = 0;
+ m_nEpoch = 0;
+ m_pWinRTFactoryCache = NULL;
+#endif // FEATURE_COMINTEROP
+
+ m_fAppDomainManagerSetInConfig = FALSE;
+ m_dwAppDomainManagerInitializeDomainFlags = eInitializeNewDomainFlags_None;
+
+#ifdef FEATURE_PREJIT
+ m_pDomainFileWithNativeImageList = NULL;
+#endif
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+ m_fIsBindingModelLocked.Store(FALSE);
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
+} // AppDomain::AppDomain
+
+AppDomain::~AppDomain()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifndef CROSSGEN_COMPILE
+
+ _ASSERTE(m_dwCreationHolders == 0);
+
+ // release the TPIndex. note that since TPIndex values are recycled the TPIndex
+ // can only be released once all threads in the AppDomain have exited.
+ if (GetTPIndex().m_dwIndex != 0)
+ PerAppDomainTPCountList::ResetAppDomainIndex(GetTPIndex());
+
+ if (m_dwId.m_dwId!=0)
+ SystemDomain::ReleaseAppDomainId(m_dwId);
+
+ m_AssemblyCache.Clear();
+
+ if (m_ADUnloadSink)
+ m_ADUnloadSink->Release();
+
+ if (m_pSecContext)
+ delete m_pSecContext;
+
+ if(!g_fEEInit)
+ Terminate();
+
+#ifndef FEATURE_CORECLR
+ if (m_pCustomSortLibrary)
+ delete m_pCustomSortLibrary;
+
+ if (m_pNlsHashProvider)
+ delete m_pNlsHashProvider;
+#endif
+
+
+#ifdef FEATURE_REMOTING
+ if (!g_fEEInit)
+ {
+ GCX_COOP(); // See SystemDomain::EnumAllStaticGCRefs if you are removing this
+ CrossDomainTypeMap::FlushStaleEntries();
+ CrossDomainFieldMap::FlushStaleEntries();
+ }
+#endif // FEATURE_REMOTING
+
+#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_REFLECTION_ONLY_LOAD
+ if (m_pReflectionOnlyWinRtBinder != NULL)
+ {
+ m_pReflectionOnlyWinRtBinder->Release();
+ }
+ if (m_pReflectionOnlyWinRtTypeCache != NULL)
+ {
+ m_pReflectionOnlyWinRtTypeCache->Release();
+ }
+#endif // FEATURE_REFLECTION_ONLY_LOAD
+ if (m_pNameToTypeMap != nullptr)
+ {
+ delete m_pNameToTypeMap;
+ m_pNameToTypeMap = nullptr;
+ }
+ if (m_pWinRTFactoryCache != nullptr)
+ {
+ delete m_pWinRTFactoryCache;
+ m_pWinRTFactoryCache = nullptr;
+ }
+#endif //FEATURE_COMINTEROP
+
+#ifdef _DEBUG
+ // If we were tracking thread AD transitions, cleanup the list on shutdown
+ if (m_pThreadTrackInfoList)
+ {
+ while (m_pThreadTrackInfoList->Count() > 0)
+ {
+ // Get the very last element
+ ThreadTrackInfo *pElem = *(m_pThreadTrackInfoList->Get(m_pThreadTrackInfoList->Count() - 1));
+ _ASSERTE(pElem);
+
+ // Free the memory
+ delete pElem;
+
+ // Remove pointer entry from the list
+ m_pThreadTrackInfoList->Delete(m_pThreadTrackInfoList->Count() - 1);
+ }
+
+ // Now delete the list itself
+ delete m_pThreadTrackInfoList;
+ m_pThreadTrackInfoList = NULL;
+ }
+#endif // _DEBUG
+
+#endif // CROSSGEN_COMPILE
+}
+
+//*****************************************************************************
+//*****************************************************************************
+//*****************************************************************************
+#ifdef _DEBUG
+#include "handletablepriv.h"
+#endif
+
+
+
+void AppDomain::Init()
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(SystemDomain::IsUnderDomainLock());
+ }
+ CONTRACTL_END;
+
+ m_pDelayedLoaderAllocatorUnloadList = NULL;
+
+ SetStage( STAGE_CREATING);
+
+
+#ifdef FEATURE_HOSTED_BINDER
+ // The lock is taken also during stack walking (GC or profiler)
+ // - To prevent deadlock with GC thread, we cannot trigger GC while holding the lock
+ // - To prevent deadlock with profiler thread, we cannot allow thread suspension
+ m_crstHostAssemblyMap.Init(
+ CrstHostAssemblyMap,
+ (CrstFlags)(CRST_GC_NOTRIGGER_WHEN_TAKEN
+ | CRST_DEBUGGER_THREAD
+ INDEBUG(| CRST_DEBUG_ONLY_CHECK_FORBID_SUSPEND_THREAD)));
+ m_crstHostAssemblyMapAdd.Init(CrstHostAssemblyMapAdd);
+#endif //FEATURE_HOSTED_BINDER
+
+ m_dwId = SystemDomain::GetNewAppDomainId(this);
+
+ m_LoaderAllocator.Init(this);
+
+#ifndef CROSSGEN_COMPILE
+ //Allocate the threadpool entry before the appdomin id list. Otherwise,
+ //the thread pool list will be out of sync if insertion of id in
+ //the appdomain fails.
+ m_tpIndex = PerAppDomainTPCountList::AddNewTPIndex();
+#endif // CROSSGEN_COMPILE
+
+ m_dwIndex = SystemDomain::GetNewAppDomainIndex(this);
+
+#ifndef CROSSGEN_COMPILE
+ PerAppDomainTPCountList::SetAppDomainId(m_tpIndex, m_dwId);
+
+ m_ADUnloadSink=new ADUnloadSink();
+#endif
+
+ BaseDomain::Init();
+
+ // Set up the IL stub cache
+ m_ILStubCache.Init(GetLoaderAllocator()->GetHighFrequencyHeap());
+
+ m_pSecContext = new SecurityContext (GetLowFrequencyHeap());
+
+// Set up the binding caches
+ m_AssemblyCache.Init(&m_DomainCacheCrst, GetHighFrequencyHeap());
+ m_UnmanagedCache.InitializeTable(this, &m_DomainCacheCrst);
+
+ m_MemoryPressure = 0;
+
+ m_sDomainLocalBlock.Init(this);
+
+#ifndef CROSSGEN_COMPILE
+
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ // NOTE: it's important that we initialize ARM data structures before calling
+ // Ref_CreateHandleTableBucket, this is because AD::Init() can race with GC
+ // and once we add ourselves to the handle table map the GC can start walking
+ // our handles and calling AD::RecordSurvivedBytes() which touches ARM data.
+ if (GCHeap::IsServerHeap())
+ m_dwNumHeaps = CPUGroupInfo::CanEnableGCCPUGroups() ?
+ CPUGroupInfo::GetNumActiveProcessors() :
+ GetCurrentProcessCpuCount();
+ else
+ m_dwNumHeaps = 1;
+ m_pullAllocBytes = new ULONGLONG [m_dwNumHeaps * ARM_CACHE_LINE_SIZE_ULL];
+ m_pullSurvivedBytes = new ULONGLONG [m_dwNumHeaps * ARM_CACHE_LINE_SIZE_ULL];
+ for (DWORD i = 0; i < m_dwNumHeaps; i++)
+ {
+ m_pullAllocBytes[i * ARM_CACHE_LINE_SIZE_ULL] = 0;
+ m_pullSurvivedBytes[i * ARM_CACHE_LINE_SIZE_ULL] = 0;
+ }
+ m_ullLastEtwAllocBytes = 0;
+#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+ // Default domain reuses the handletablemap that was created during EEStartup since
+ // default domain cannot be unloaded.
+ if (GetId().m_dwId == DefaultADID)
+ {
+ m_hHandleTableBucket = g_HandleTableMap.pBuckets[0];
+ }
+ else
+ {
+ m_hHandleTableBucket = Ref_CreateHandleTableBucket(m_dwIndex);
+ }
+
+#ifdef _DEBUG
+ if (((HandleTable *)(m_hHandleTableBucket->pTable[0]))->uADIndex != m_dwIndex)
+ _ASSERTE (!"AD index mismatch");
+#endif // _DEBUG
+
+#endif // CROSSGEN_COMPILE
+
+#ifdef FEATURE_TYPEEQUIVALENCE
+ m_TypeEquivalenceCrst.Init(CrstTypeEquivalenceMap);
+#endif
+
+ m_ReflectionCrst.Init(CrstReflection, CRST_UNSAFE_ANYMODE);
+ m_RefClassFactCrst.Init(CrstClassFactInfoHash);
+
+ {
+ LockOwner lock = {&m_DomainCrst, IsOwnerOfCrst};
+ m_clsidHash.Init(0,&CompareCLSID,true, &lock); // init hash table
+ }
+
+ CreateSecurityDescriptor();
+ SetStage(STAGE_READYFORMANAGEDCODE);
+
+#ifndef CROSSGEN_COMPILE
+ m_pDefaultContext = new Context(this);
+
+ m_ExposedObject = CreateHandle(NULL);
+
+ // Create the Application Security Descriptor
+
+ COUNTER_ONLY(GetPerfCounters().m_Loading.cAppDomains++);
+
+#ifdef FEATURE_COMINTEROP
+ if (!AppX::IsAppXProcess())
+ {
+#ifdef FEATURE_REFLECTION_ONLY_LOAD
+ m_pReflectionOnlyWinRtTypeCache = clr::SafeAddRef(new CLRPrivTypeCacheReflectionOnlyWinRT());
+ m_pReflectionOnlyWinRtBinder = clr::SafeAddRef(new CLRPrivBinderReflectionOnlyWinRT(m_pReflectionOnlyWinRtTypeCache));
+#endif
+ }
+#ifdef FEATURE_APPX_BINDER
+ else if (g_fEEStarted && !IsDefaultDomain())
+ { // Non-default domain in an AppX process. This exists only for designers and we'd better be in dev mode.
+ _ASSERTE(IsCompilationProcess() || AppX::IsAppXDesignMode());
+
+ // Inherit AppX binder from default domain.
+ SetLoadContextHostBinder(SystemDomain::System()->DefaultDomain()->GetLoadContextHostBinder());
+
+ // Note: LoadFrom, LoadFile, Load(byte[], ...), ReflectionOnlyLoad, LoadWithPartialName,
+ /// etc. are not supported and are actively blocked.
+ }
+#endif //FEATURE_APPX_BINDER
+#endif //FEATURE_COMINTEROP
+
+#endif // CROSSGEN_COMPILE
+} // AppDomain::Init
+
+
+/*********************************************************************/
+
+BOOL AppDomain::IsCompilationDomain()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ BOOL isCompilationDomain = (m_dwFlags & COMPILATION_DOMAIN) != 0;
+#ifdef FEATURE_PREJIT
+ _ASSERTE(!isCompilationDomain ||
+ (IsCompilationProcess() && IsPassiveDomain()));
+#endif // FEATURE_PREJIT
+ return isCompilationDomain;
+}
+
+#ifndef CROSSGEN_COMPILE
+
+extern int g_fADUnloadWorkerOK;
+
+// Notes:
+// This helper will send the AppDomain creation notifications for profiler / debugger.
+// If it throws, its backout code will also send a notification.
+// If it succeeds, then we still need to send a AppDomainCreateFinished notification.
+void AppDomain::CreateUnmanagedObject(AppDomainCreationHolder<AppDomain>& pDomain)
+{
+ CONTRACTL
+ {
+ THROWS;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ GCX_PREEMP();
+
+ pDomain.Assign(new AppDomain());
+ if (g_fADUnloadWorkerOK<0)
+ {
+ AppDomain::CreateADUnloadWorker();
+ }
+
+ //@todo: B#25921
+ // We addref Appdomain object here and notify a profiler that appdomain
+ // creation has started, then return to managed code which will call
+ // the function that releases the appdomain and notifies a profiler that we finished
+ // creating the appdomain. If an exception is raised while we're in that managed code
+ // we will leak memory and the profiler will not be notified about the failure
+
+#ifdef PROFILING_SUPPORTED
+ // Signal profile if present.
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) (AppDomain*) pDomain);
+ END_PIN_PROFILER();
+ }
+ EX_TRY
+#endif // PROFILING_SUPPORTED
+ {
+ {
+ SystemDomain::LockHolder lh;
+ pDomain->Init();
+ // allocate a Virtual Call Stub Manager for this domain
+ pDomain->InitVSD();
+ }
+
+ pDomain->SetCanUnload(); // by default can unload any domain
+
+ #ifdef DEBUGGING_SUPPORTED
+ // Notify the debugger here, before the thread transitions into the
+ // AD to finish the setup, and before any assemblies are loaded into it.
+ SystemDomain::PublishAppDomainAndInformDebugger(pDomain);
+ #endif // DEBUGGING_SUPPORTED
+
+ STRESS_LOG2 (LF_APPDOMAIN, LL_INFO100, "Create domain [%d] %p\n", pDomain->GetId().m_dwId, (AppDomain*)pDomain);
+ pDomain->LoadSystemAssemblies();
+ pDomain->SetupSharedStatics();
+
+ pDomain->SetStage(AppDomain::STAGE_ACTIVE);
+ }
+#ifdef PROFILING_SUPPORTED
+ EX_HOOK
+ {
+ // Need the first assembly loaded in to get any data on an app domain.
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID)(AppDomain*) pDomain, GET_EXCEPTION()->GetHR());
+ END_PIN_PROFILER();
+ }
+ }
+ EX_END_HOOK;
+
+ // On success, caller must still send the AppDomainCreationFinished notification.
+#endif // PROFILING_SUPPORTED
+}
+
+void AppDomain::Stop()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_ANY;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_MULTICOREJIT
+ GetMulticoreJitManager().StopProfile(true);
+#endif
+
+ // Set the unloaded flag before notifying the debugger
+ GetLoaderAllocator()->SetIsUnloaded();
+
+#ifdef DEBUGGING_SUPPORTED
+ if (IsDebuggerAttached())
+ NotifyDebuggerUnload();
+#endif // DEBUGGING_SUPPORTED
+
+ m_pRootAssembly = NULL; // This assembly is in the assembly list;
+
+ if (m_pSecDesc != NULL)
+ {
+ delete m_pSecDesc;
+ m_pSecDesc = NULL;
+ }
+
+#ifdef DEBUGGING_SUPPORTED
+ if (NULL != g_pDebugInterface)
+ {
+ // Call the publisher API to delete this appdomain entry from the list
+ CONTRACT_VIOLATION(ThrowsViolation);
+ g_pDebugInterface->RemoveAppDomainFromIPC (this);
+ }
+#endif // DEBUGGING_SUPPORTED
+}
+
+void AppDomain::Terminate()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ GCX_PREEMP();
+
+
+ _ASSERTE(m_dwThreadEnterCount == 0 || IsDefaultDomain());
+
+ if (m_pComIPForExposedObject)
+ {
+ m_pComIPForExposedObject->Release();
+ m_pComIPForExposedObject = NULL;
+ }
+
+ delete m_pDefaultContext;
+ m_pDefaultContext = NULL;
+
+ if (m_pUMEntryThunkCache)
+ {
+ delete m_pUMEntryThunkCache;
+ m_pUMEntryThunkCache = NULL;
+ }
+
+#ifdef FEATURE_COMINTEROP
+ if (m_pRCWCache)
+ {
+ delete m_pRCWCache;
+ m_pRCWCache = NULL;
+ }
+
+ if (m_pRCWRefCache)
+ {
+ delete m_pRCWRefCache;
+ m_pRCWRefCache = NULL;
+ }
+
+ if (m_pComCallWrapperCache)
+ {
+ m_pComCallWrapperCache->Neuter();
+ m_pComCallWrapperCache->Release();
+ }
+
+ // if the above released the wrapper cache, then it will call back and reset our
+ // m_pComCallWrapperCache to null. If not null, then need to set it's domain pointer to
+ // null.
+ if (! m_pComCallWrapperCache)
+ {
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::Terminate ComCallWrapperCache released\n"));
+ }
+#ifdef _DEBUG
+ else
+ {
+ m_pComCallWrapperCache = NULL;
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::Terminate ComCallWrapperCache not released\n"));
+ }
+#endif // _DEBUG
+
+#endif // FEATURE_COMINTEROP
+
+#ifdef FEATURE_FUSION
+ if(m_pAsyncPool != NULL)
+ {
+ delete m_pAsyncPool;
+ m_pAsyncPool = NULL;
+ }
+#endif
+
+ if (!IsAtProcessExit())
+ {
+ // if we're not shutting down everything then clean up the string literals associated
+ // with this appdomain -- note that is no longer needs to happen while suspended
+ // because the appropriate locks are taken in the GlobalStringLiteralMap
+ // this is important as this locks have a higher lock number than does the
+ // thread-store lock which is taken when we suspend.
+ GetLoaderAllocator()->CleanupStringLiteralMap();
+
+ // Suspend the EE to do some clean up that can only occur
+ // while no threads are running.
+ GCX_COOP (); // SuspendEE may require current thread to be in Coop mode
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
+ }
+
+ // Note that this must be performed before restarting the EE. It will clean
+ // the cache and prevent others from using stale cache entries.
+ //@TODO: Would be nice to get this back to BaseDomain, but need larger fix for that.
+ // NOTE: Must have the runtime suspended to unlink managers
+ // NOTE: May be NULL due to OOM during initialization. Can skip in that case.
+ GetLoaderAllocator()->UninitVirtualCallStubManager();
+ MethodTable::ClearMethodDataCache();
+ ClearJitGenericHandleCache(this);
+
+ // @TODO s_TPMethodTableCrst prevents us from from keeping the whole
+ // assembly shutdown logic here. See if we can do better in the next milestone
+#ifdef FEATURE_PREJIT
+ DeleteNativeCodeRanges();
+#endif
+
+ if (!IsAtProcessExit())
+ {
+ // Resume the EE.
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ }
+
+ ShutdownAssemblies();
+#ifdef FEATURE_CORECLR
+ ShutdownNativeDllSearchDirectories();
+#endif
+
+ if (m_pRefClassFactHash)
+ {
+ m_pRefClassFactHash->Destroy();
+ // storage for m_pRefClassFactHash itself is allocated on the loader heap
+ }
+
+#ifdef FEATURE_TYPEEQUIVALENCE
+ m_TypeEquivalenceCrst.Destroy();
+#endif
+
+ m_ReflectionCrst.Destroy();
+ m_RefClassFactCrst.Destroy();
+
+ m_LoaderAllocator.Terminate();
+
+ BaseDomain::Terminate();
+
+#ifdef _DEBUG
+ if (m_hHandleTableBucket &&
+ m_hHandleTableBucket->pTable &&
+ ((HandleTable *)(m_hHandleTableBucket->pTable[0]))->uADIndex != m_dwIndex)
+ _ASSERTE (!"AD index mismatch");
+#endif // _DEBUG
+
+ if (m_hHandleTableBucket) {
+ Ref_DestroyHandleTableBucket(m_hHandleTableBucket);
+ m_hHandleTableBucket = NULL;
+ }
+
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+ if (m_pullAllocBytes)
+ {
+ delete [] m_pullAllocBytes;
+ }
+ if (m_pullSurvivedBytes)
+ {
+ delete [] m_pullSurvivedBytes;
+ }
+#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+ if(m_dwIndex.m_dwIndex != 0)
+ SystemDomain::ReleaseAppDomainIndex(m_dwIndex);
+} // AppDomain::Terminate
+
+void AppDomain::CloseDomain()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+
+ BOOL bADRemoved=FALSE;;
+
+ AddRef(); // Hold a reference
+ AppDomainRefHolder AdHolder(this);
+ {
+ SystemDomain::LockHolder lh;
+
+ SystemDomain::System()->DecrementNumAppDomains(); // Maintain a count of app domains added to the list.
+ bADRemoved = SystemDomain::System()->RemoveDomain(this);
+ }
+
+ if(bADRemoved)
+ Stop();
+}
+
+/*********************************************************************/
+
+struct GetExposedObject_Args
+{
+ AppDomain *pDomain;
+ OBJECTREF *ref;
+};
+
+static void GetExposedObject_Wrapper(LPVOID ptr)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+ GetExposedObject_Args *args = (GetExposedObject_Args *) ptr;
+ *(args->ref) = args->pDomain->GetExposedObject();
+}
+
+
+OBJECTREF AppDomain::GetExposedObject()
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ OBJECTREF ref = GetRawExposedObject();
+ if (ref == NULL)
+ {
+ APPDOMAINREF obj = NULL;
+
+ Thread *pThread = GetThread();
+ if (pThread->GetDomain() != this)
+ {
+ GCPROTECT_BEGIN(ref);
+ GetExposedObject_Args args = {this, &ref};
+ // call through DoCallBack with a domain transition
+ pThread->DoADCallBack(this,GetExposedObject_Wrapper, &args,ADV_CREATING|ADV_RUNNINGIN);
+ GCPROTECT_END();
+ return ref;
+ }
+ MethodTable *pMT = MscorlibBinder::GetClass(CLASS__APP_DOMAIN);
+
+ // Create the module object
+ obj = (APPDOMAINREF) AllocateObject(pMT);
+ obj->SetDomain(this);
+
+ if(StoreFirstObjectInHandle(m_ExposedObject, (OBJECTREF) obj) == FALSE) {
+ obj = (APPDOMAINREF) GetRawExposedObject();
+ _ASSERTE(obj);
+ }
+
+ return (OBJECTREF) obj;
+ }
+
+ return ref;
+}
+
+#ifndef FEATURE_CORECLR
+void AppDomain::InitializeSorting(OBJECTREF* ppAppdomainSetup)
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(ppAppdomainSetup == NULL || IsProtectedByGCFrame(ppAppdomainSetup));
+ }
+ CONTRACTL_END;
+
+ DWORD sortVersionFromConfig = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CompatSortNLSVersion);
+
+ if(sortVersionFromConfig != 0)
+ {
+ m_bUseOsSorting = FALSE;
+ m_sortVersion = sortVersionFromConfig;
+ }
+
+ if(ppAppdomainSetup != NULL)
+ {
+ APPDOMAINSETUPREF adSetup = (APPDOMAINSETUPREF) *ppAppdomainSetup;
+ APPDOMAINSORTINGSETUPINFOREF sortingSetup = adSetup->GetAppDomainSortingSetupInfo();
+
+ if(sortingSetup != NULL)
+ {
+ if(sortingSetup->UseV2LegacySorting() || sortingSetup->UseV4LegacySorting())
+ {
+
+ m_bUseOsSorting = FALSE;
+
+ if(sortingSetup->UseV2LegacySorting())
+ {
+ m_sortVersion = SORT_VERSION_WHIDBEY;
+ }
+
+ if(sortingSetup->UseV4LegacySorting())
+ {
+ m_sortVersion = SORT_VERSION_V4;
+ }
+ }
+ else if(sortingSetup->GetPFNIsNLSDefinedString() != NULL
+ && sortingSetup->GetPFNCompareStringEx() != NULL
+ && sortingSetup->GetPFNLCMapStringEx() != NULL
+ && sortingSetup->GetPFNFindNLSStringEx() != NULL
+ && sortingSetup->GetPFNCompareStringOrdinal() != NULL
+ && sortingSetup->GetPFNGetNLSVersionEx() != NULL
+ && sortingSetup->GetPFNFindStringOrdinal() != NULL)
+ {
+ m_pCustomSortLibrary = new COMNlsCustomSortLibrary;
+ m_pCustomSortLibrary->pIsNLSDefinedString = (PFN_IS_NLS_DEFINED_STRING) sortingSetup->GetPFNIsNLSDefinedString();
+ m_pCustomSortLibrary->pCompareStringEx = (PFN_COMPARE_STRING_EX) sortingSetup->GetPFNCompareStringEx();
+ m_pCustomSortLibrary->pLCMapStringEx = (PFN_LC_MAP_STRING_EX) sortingSetup->GetPFNLCMapStringEx();
+ m_pCustomSortLibrary->pFindNLSStringEx = (PFN_FIND_NLS_STRING_EX) sortingSetup->GetPFNFindNLSStringEx();
+ m_pCustomSortLibrary->pCompareStringOrdinal = (PFN_COMPARE_STRING_ORDINAL) sortingSetup->GetPFNCompareStringOrdinal();
+ m_pCustomSortLibrary->pGetNLSVersionEx = (PFN_GET_NLS_VERSION_EX) sortingSetup->GetPFNGetNLSVersionEx();
+ m_pCustomSortLibrary->pFindStringOrdinal = (PFN_FIND_STRING_ORDINAL) sortingSetup->GetPFNFindStringOrdinal();
+ }
+ }
+ }
+
+ if(m_bUseOsSorting == FALSE && m_sortVersion == DEFAULT_SORT_VERSION)
+ {
+ // If we are using the legacy sorting dlls, the default version for sorting is SORT_VERSION_V4. Note that
+ // we don't expect this to change in the future (even when V5 or V6 of the runtime comes out).
+ m_sortVersion = SORT_VERSION_V4;
+ }
+
+ if(RunningOnWin8() && m_bUseOsSorting == FALSE)
+ {
+ // We need to ensure that the versioned sort DLL could load so we don't crash later. This ensures we have
+ // the same behavior as Windows 7, where even if we couldn't load the correct versioned sort dll, we would
+ // provide the default sorting behavior.
+ INT_PTR sortOrigin;
+ if(COMNlsInfo::InternalInitVersionedSortHandle(W(""), &sortOrigin, m_sortVersion) == NULL)
+ {
+ LOG((LF_APPDOMAIN, LL_WARNING, "AppDomain::InitializeSorting failed to load legacy sort DLL for AppDomain.\n"));
+ // We couldn't load a sort DLL. Fall back to default sorting using the OS.
+ m_bUseOsSorting = TRUE;
+ m_sortVersion = DEFAULT_SORT_VERSION;
+ }
+ }
+
+#if _DEBUG
+ m_bSortingInitialized = TRUE;
+#endif
+}
+#endif
+
+#ifndef FEATURE_CORECLR
+void AppDomain::InitializeHashing(OBJECTREF* ppAppdomainSetup)
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(ppAppdomainSetup == NULL || IsProtectedByGCFrame(ppAppdomainSetup));
+ }
+ CONTRACTL_END;
+
+ m_pNlsHashProvider = new COMNlsHashProvider;
+
+#ifdef FEATURE_RANDOMIZED_STRING_HASHING
+ BOOL fUseRandomizedHashing = (BOOL) CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_UseRandomizedStringHashAlgorithm);
+
+ if(ppAppdomainSetup != NULL)
+ {
+ APPDOMAINSETUPREF adSetup = (APPDOMAINSETUPREF) *ppAppdomainSetup;
+ fUseRandomizedHashing |= adSetup->UseRandomizedStringHashing();
+ }
+
+ m_pNlsHashProvider->SetUseRandomHashing(fUseRandomizedHashing);
+#endif // FEATURE_RANDOMIZED_STRING_HASHING
+}
+#endif // FEATURE_CORECLR
+
+OBJECTREF AppDomain::DoSetup(OBJECTREF* setupInfo)
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ ADID adid=GetAppDomain()->GetId();
+
+ OBJECTREF retval=NULL;
+ GCPROTECT_BEGIN(retval);
+
+ ENTER_DOMAIN_PTR(this,ADV_CREATING);
+
+ MethodDescCallSite setup(METHOD__APP_DOMAIN__SETUP);
+
+ ARG_SLOT args[1];
+
+ args[0]=ObjToArgSlot(*setupInfo);
+
+ OBJECTREF activator;
+ activator=setup.Call_RetOBJECTREF(args);
+#ifdef FEATURE_REMOTING
+ if (activator != NULL)
+ {
+ GCPROTECT_BEGIN(activator);
+ retval=AppDomainHelper::CrossContextCopyTo(adid,&activator);
+ GCPROTECT_END();
+ }
+#else
+ _ASSERTE(activator==NULL);
+#endif
+
+#if defined(FEATURE_MULTICOREJIT)
+ // Disable AutoStartProfile in default domain from this code path.
+ // It's called from SystemDomain::ExecuteMainMethod for normal program, not needed for SL and Asp.Net
+ if (! IsDefaultDomain())
+ {
+ GCX_PREEMP();
+
+ GetMulticoreJitManager().AutoStartProfile(this);
+ }
+#endif
+
+ END_DOMAIN_TRANSITION;
+ GCPROTECT_END();
+ return retval;
+}
+
+#endif // !CROSSGEN_COMPILE
+
+#ifdef FEATURE_COMINTEROP
+#ifndef CROSSGEN_COMPILE
+HRESULT AppDomain::GetComIPForExposedObject(IUnknown **pComIP)
+{
+ // Assumption: This function is called for AppDomain's that the current
+ // thread is in or has entered, or the AppDomain is kept alive.
+ //
+ // Assumption: This function can now throw. The caller is responsible for any
+ // BEGIN_EXTERNAL_ENTRYPOINT, EX_TRY, or other
+ // techniques to convert to a COM HRESULT protocol.
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ Thread *pThread = GetThread();
+ if (m_pComIPForExposedObject)
+ {
+ GCX_PREEMP_THREAD_EXISTS(pThread);
+ m_pComIPForExposedObject->AddRef();
+ *pComIP = m_pComIPForExposedObject;
+ return S_OK;
+ }
+
+ IUnknown* punk = NULL;
+
+ OBJECTREF ref = NULL;
+ GCPROTECT_BEGIN(ref);
+
+ EnsureComStarted();
+
+ ENTER_DOMAIN_PTR(this,ADV_DEFAULTAD)
+ {
+ ref = GetExposedObject();
+ punk = GetComIPFromObjectRef(&ref);
+ if (FastInterlockCompareExchangePointer(&m_pComIPForExposedObject, punk, NULL) == NULL)
+ {
+ GCX_PREEMP_THREAD_EXISTS(pThread);
+ m_pComIPForExposedObject->AddRef();
+ }
+ }
+ END_DOMAIN_TRANSITION;
+
+ GCPROTECT_END();
+
+ if(SUCCEEDED(hr))
+ {
+ *pComIP = m_pComIPForExposedObject;
+ }
+
+ return hr;
+}
+#endif //#ifndef CROSSGEN_COMPILE
+
+MethodTable *AppDomain::GetRedirectedType(WinMDAdapter::RedirectedTypeIndex index)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // If we have the type loaded already, use that
+ if (m_rpCLRTypes[index] != nullptr)
+ {
+ return m_rpCLRTypes[index];
+ }
+
+ WinMDAdapter::FrameworkAssemblyIndex frameworkAssemblyIndex;
+ WinMDAdapter::GetRedirectedTypeInfo(index, nullptr, nullptr, nullptr, &frameworkAssemblyIndex, nullptr, nullptr);
+ MethodTable * pMT = LoadRedirectedType(index, frameworkAssemblyIndex);
+ m_rpCLRTypes[index] = pMT;
+ return pMT;
+}
+
+MethodTable* AppDomain::LoadRedirectedType(WinMDAdapter::RedirectedTypeIndex index, WinMDAdapter::FrameworkAssemblyIndex assembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(index < WinMDAdapter::RedirectedTypeIndex_Count);
+ }
+ CONTRACTL_END;
+
+ LPCSTR szClrNamespace;
+ LPCSTR szClrName;
+ LPCSTR szFullWinRTName;
+ WinMDAdapter::FrameworkAssemblyIndex nFrameworkAssemblyIndex;
+
+ WinMDAdapter::GetRedirectedTypeInfo(index, &szClrNamespace, &szClrName, &szFullWinRTName, &nFrameworkAssemblyIndex, nullptr, nullptr);
+
+ _ASSERTE(nFrameworkAssemblyIndex >= WinMDAdapter::FrameworkAssembly_Mscorlib &&
+ nFrameworkAssemblyIndex < WinMDAdapter::FrameworkAssembly_Count);
+
+ if (assembly != nFrameworkAssemblyIndex)
+ {
+ // The framework type does not live in the assembly we were requested to load redirected types from
+ return nullptr;
+ }
+ else if (nFrameworkAssemblyIndex == WinMDAdapter::FrameworkAssembly_Mscorlib)
+ {
+ return ClassLoader::LoadTypeByNameThrowing(MscorlibBinder::GetModule()->GetAssembly(),
+ szClrNamespace,
+ szClrName,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::LoadTypes,
+ CLASS_LOAD_EXACTPARENTS).GetMethodTable();
+ }
+ else
+ {
+ LPCSTR pSimpleName;
+ AssemblyMetaDataInternal context;
+ const BYTE * pbKeyToken;
+ DWORD cbKeyTokenLength;
+ DWORD dwFlags;
+
+ WinMDAdapter::GetExtraAssemblyRefProps(nFrameworkAssemblyIndex,
+ &pSimpleName,
+ &context,
+ &pbKeyToken,
+ &cbKeyTokenLength,
+ &dwFlags);
+
+ Assembly* pAssembly = AssemblySpec::LoadAssembly(pSimpleName,
+ &context,
+ pbKeyToken,
+ cbKeyTokenLength,
+ dwFlags);
+
+ return ClassLoader::LoadTypeByNameThrowing(
+ pAssembly,
+ szClrNamespace,
+ szClrName,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::LoadTypes,
+ CLASS_LOAD_EXACTPARENTS).GetMethodTable();
+ }
+}
+
+bool AppDomain::FindRedirectedAssembly(Assembly* pAssembly, WinMDAdapter::FrameworkAssemblyIndex* pIndex)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pAssembly));
+ PRECONDITION(CheckPointer(pIndex));
+ }
+ CONTRACTL_END;
+
+ DomainAssembly *pDomainAssembly = pAssembly->GetDomainAssembly();
+
+ if (pDomainAssembly->IsSystem())
+ {
+ *pIndex = WinMDAdapter::FrameworkAssembly_Mscorlib;
+ return true;
+ }
+ else if (pDomainAssembly == m_pSystemDll)
+ {
+ *pIndex = WinMDAdapter::FrameworkAssembly_System;
+ return true;
+ }
+ else if (pDomainAssembly == m_pSystemRuntimeWindowsRuntimeDll)
+ {
+ *pIndex = WinMDAdapter::FrameworkAssembly_SystemRuntimeWindowsRuntime;
+ return true;
+ }
+ else if (pDomainAssembly == m_pSystemRuntimeWindowsRuntimeUIXamlDll)
+ {
+ *pIndex = WinMDAdapter::FrameworkAssembly_SystemRuntimeWindowsRuntimeUIXaml;
+ return true;
+ }
+ else if (pDomainAssembly == m_pSystemNumericsVectors)
+ {
+ *pIndex = WinMDAdapter::FrameworkAssembly_SystemNumericsVectors;
+ return true;
+ }
+
+ return false;
+}
+
+#endif //FEATURE_COMINTEROP
+
+#endif //!DACCESS_COMPILE
+
+
+#ifdef FEATURE_COMINTEROP
+BOOL AppDomain::FindRedirectedAssemblyFromIndexIfLoaded(WinMDAdapter::FrameworkAssemblyIndex index, Assembly ** ppAssembly)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // If new redirected assemblies are added, this function probably needs to be updated
+ C_ASSERT(WinMDAdapter::FrameworkAssembly_Count == 5);
+
+ DomainAssembly * pDomainAssembly = NULL;
+
+ if (index == WinMDAdapter::FrameworkAssembly_Mscorlib)
+ {
+ *ppAssembly = SystemDomain::SystemAssembly();
+ return TRUE;
+ }
+ else if (index == WinMDAdapter::FrameworkAssembly_System)
+ {
+ pDomainAssembly = m_pSystemDll;
+ }
+ else if (index == WinMDAdapter::FrameworkAssembly_SystemRuntimeWindowsRuntime)
+ {
+ pDomainAssembly = m_pSystemRuntimeWindowsRuntimeDll;
+ }
+ else if (index == WinMDAdapter::FrameworkAssembly_SystemRuntimeWindowsRuntimeUIXaml)
+ {
+ pDomainAssembly = m_pSystemRuntimeWindowsRuntimeUIXamlDll;
+ }
+ else if (index == WinMDAdapter::FrameworkAssembly_SystemNumericsVectors)
+ {
+ pDomainAssembly = m_pSystemNumericsVectors;
+ }
+
+ if (pDomainAssembly != NULL)
+ {
+ *ppAssembly = pDomainAssembly->GetAssembly();
+ return TRUE;
+ }
+
+ *ppAssembly = NULL;
+ return FALSE;
+}
+
+#endif // FEATURE_COMINTEROP
+
+#ifndef DACCESS_COMPILE
+
+void AppDomain::CreateSecurityDescriptor()
+{
+ STANDARD_VM_CONTRACT;
+
+ _ASSERTE(m_pSecDesc == NULL);
+
+ m_pSecDesc = Security::CreateApplicationSecurityDescriptor(this);
+}
+
+bool IsPlatformAssembly(LPCSTR szName, DomainAssembly *pDomainAssembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(szName));
+ PRECONDITION(CheckPointer(pDomainAssembly));
+ }
+ CONTRACTL_END;
+
+ PEAssembly *pPEAssembly = pDomainAssembly->GetFile();
+
+ if (strcmp(szName, pPEAssembly->GetSimpleName()) != 0)
+ {
+ return false;
+ }
+
+ DWORD cbPublicKey;
+ const BYTE *pbPublicKey = static_cast<const BYTE *>(pPEAssembly->GetPublicKey(&cbPublicKey));
+ if (pbPublicKey == nullptr)
+ {
+ return false;
+ }
+
+#ifdef FEATURE_CORECLR
+ return StrongNameIsSilverlightPlatformKey(pbPublicKey, cbPublicKey);
+#else
+ return StrongNameIsEcmaKey(pbPublicKey, cbPublicKey);
+#endif
+}
+
+void AppDomain::AddAssembly(DomainAssembly * assem)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ {
+ CrstHolder ch(GetAssemblyListLock());
+
+ // Attempt to find empty space in assemblies list
+ DWORD asmCount = m_Assemblies.GetCount_Unlocked();
+ for (DWORD i = 0; i < asmCount; ++i)
+ {
+ if (m_Assemblies.Get_UnlockedNoReference(i) == NULL)
+ {
+ m_Assemblies.Set_Unlocked(i, assem);
+ return;
+ }
+ }
+
+ // If empty space not found, simply add to end of list
+ IfFailThrow(m_Assemblies.Append_Unlocked(assem));
+ }
+
+#ifdef FEATURE_COMINTEROP
+ // See if this is one of the well-known assemblies that we look for
+ if (m_pSystemDll == nullptr && IsPlatformAssembly("System", assem))
+ {
+ m_pSystemDll = assem;
+ return;
+ }
+
+ if (m_pSystemRuntimeWindowsRuntimeDll == nullptr && IsPlatformAssembly("System.Runtime.WindowsRuntime", assem))
+ {
+ m_pSystemRuntimeWindowsRuntimeDll = assem;
+ return;
+ }
+ if (m_pSystemRuntimeWindowsRuntimeUIXamlDll == nullptr && IsPlatformAssembly("System.Runtime.WindowsRuntime.UI.Xaml", assem))
+ {
+ m_pSystemRuntimeWindowsRuntimeUIXamlDll = assem;
+ return;
+ }
+ if (m_pSystemNumericsVectors == nullptr)
+ {
+ PEAssembly *pPEAssembly = assem->GetFile();
+
+ if (strcmp("System.Numerics.Vectors", pPEAssembly->GetSimpleName()) == 0)
+ {
+ DWORD cbPublicKey;
+ const BYTE *pbPublicKey = static_cast<const BYTE *>(pPEAssembly->GetPublicKey(&cbPublicKey));
+
+ if (cbPublicKey == sizeof(s_pbContractPublicKey) && memcmp(pbPublicKey, s_pbContractPublicKey, cbPublicKey) == 0)
+ {
+ m_pSystemNumericsVectors = assem;
+ }
+ }
+ }
+#endif // FEATURE_COMINTEROP
+}
+
+void AppDomain::RemoveAssembly_Unlocked(DomainAssembly * pAsm)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(GetAssemblyListLock()->OwnedByCurrentThread());
+
+ DWORD asmCount = m_Assemblies.GetCount_Unlocked();
+ for (DWORD i = 0; i < asmCount; ++i)
+ {
+ if (m_Assemblies.Get_UnlockedNoReference(i) == pAsm)
+ {
+ m_Assemblies.Set_Unlocked(i, NULL);
+ return;
+ }
+ }
+
+ _ASSERTE(!"Unreachable");
+}
+
+BOOL AppDomain::ContainsAssembly(Assembly * assem)
+{
+ WRAPPER_NO_CONTRACT;
+ AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded |
+ (assem->IsIntrospectionOnly() ? kIncludeIntrospection : kIncludeExecution)));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (i.Next(pDomainAssembly.This()))
+ {
+ CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
+ if (pAssembly == assem)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL AppDomain::HasSetSecurityPolicy()
+{
+ CONTRACT(BOOL)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ GCX_COOP();
+
+ if (NingenEnabled())
+ {
+ return FALSE;
+ }
+ RETURN ((APPDOMAINREF)GetExposedObject())->HasSetPolicy();
+}
+
+#if defined (FEATURE_LOADER_OPTIMIZATION) && !defined(FEATURE_CORECLR)
+// Returns true if the user has declared the desire to load an
+// assembly domain-neutral. This is either by specifying System.LoaderOptimizationAttribute
+// on the entry routine or the host has set this loader-optimization flag.
+BOOL AppDomain::ApplySharePolicy(DomainAssembly *pFile)
+{
+ CONTRACT(BOOL)
+ {
+ PRECONDITION(CheckPointer(pFile));
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ if (!pFile->GetFile()->IsShareable())
+ RETURN FALSE;
+
+ if (ApplySharePolicyFlag(pFile))
+ RETURN TRUE;
+
+ RETURN FALSE;
+}
+
+BOOL AppDomain::ApplySharePolicyFlag(DomainAssembly *pFile)
+{
+ CONTRACT(BOOL)
+ {
+ PRECONDITION(CheckPointer(pFile));
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ switch(GetSharePolicy()) {
+ case SHARE_POLICY_ALWAYS:
+ RETURN (!pFile->MayHaveUnknownDependencies());
+
+ case SHARE_POLICY_GAC:
+ RETURN (pFile->IsClosedInGAC());
+
+ case SHARE_POLICY_NEVER:
+ RETURN pFile->IsSystem();
+
+ default:
+ UNREACHABLE_MSG("Unknown share policy");
+ }
+}
+#endif // FEATURE_LOADER_OPTIMIZATION
+
+EEClassFactoryInfoHashTable* AppDomain::SetupClassFactHash()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ CrstHolder ch(&m_ReflectionCrst);
+
+ if (m_pRefClassFactHash == NULL)
+ {
+ AllocMemHolder<void> pCache(GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof (EEClassFactoryInfoHashTable))));
+ EEClassFactoryInfoHashTable *tmp = new (pCache) EEClassFactoryInfoHashTable;
+ LockOwner lock = {&m_RefClassFactCrst,IsOwnerOfCrst};
+ if (!tmp->Init(20, &lock))
+ COMPlusThrowOM();
+ pCache.SuppressRelease();
+ m_pRefClassFactHash = tmp;
+ }
+
+ return m_pRefClassFactHash;
+}
+
+#ifdef FEATURE_COMINTEROP
+DispIDCache* AppDomain::SetupRefDispIDCache()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ CrstHolder ch(&m_ReflectionCrst);
+
+ if (m_pRefDispIDCache == NULL)
+ {
+ AllocMemHolder<void> pCache = GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof (DispIDCache)));
+
+ DispIDCache *tmp = new (pCache) DispIDCache;
+ tmp->Init();
+
+ pCache.SuppressRelease();
+ m_pRefDispIDCache = tmp;
+ }
+
+ return m_pRefDispIDCache;
+}
+
+#endif // FEATURE_COMINTEROP
+
+FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pLock->HasLock());
+ PRECONDITION(pLock->FindFileLock(pFile) == NULL);
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ NewHolder<FileLoadLock> result(new FileLoadLock(pLock, pFile, pDomainFile));
+
+ pLock->AddElement(result);
+ result->AddRef(); // Add one ref on behalf of the ListLock's reference. The corresponding Release() happens in FileLoadLock::CompleteLoadLevel.
+ return result.Extract();
+}
+
+FileLoadLock::~FileLoadLock()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ ((PEFile *) m_pData)->Release();
+}
+
+DomainFile *FileLoadLock::GetDomainFile()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pDomainFile;
+}
+
+FileLoadLevel FileLoadLock::GetLoadLevel()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_level;
+}
+
+ADID FileLoadLock::GetAppDomainId()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_AppDomainId;
+}
+
+// Acquire will return FALSE and not take the lock if the file
+// has already been loaded to the target level. Otherwise,
+// it will return TRUE and take the lock.
+//
+// Note that the taker must release the lock via IncrementLoadLevel.
+
+BOOL FileLoadLock::Acquire(FileLoadLevel targetLevel)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If we are already loaded to the desired level, the lock is "free".
+ if (m_level >= targetLevel)
+ return FALSE;
+
+ if (!DeadlockAwareEnter())
+ {
+ // We failed to get the lock due to a deadlock.
+ return FALSE;
+ }
+
+ if (m_level >= targetLevel)
+ {
+ Leave();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL FileLoadLock::CanAcquire(FileLoadLevel targetLevel)
+{
+ // If we are already loaded to the desired level, the lock is "free".
+ if (m_level >= targetLevel)
+ return FALSE;
+
+ return CanDeadlockAwareEnter();
+}
+
+#if !defined(DACCESS_COMPILE) && (defined(LOGGING) || defined(STRESS_LOG))
+static const char *fileLoadLevelName[] =
+{
+ "CREATE", // FILE_LOAD_CREATE
+ "BEGIN", // FILE_LOAD_BEGIN
+ "FIND_NATIVE_IMAGE", // FILE_LOAD_FIND_NATIVE_IMAGE
+ "VERIFY_NATIVE_IMAGE_DEPENDENCIES", // FILE_LOAD_VERIFY_NATIVE_IMAGE_DEPENDENCIES
+ "ALLOCATE", // FILE_LOAD_ALLOCATE
+ "ADD_DEPENDENCIES", // FILE_LOAD_ADD_DEPENDENCIES
+ "PRE_LOADLIBRARY", // FILE_LOAD_PRE_LOADLIBRARY
+ "LOADLIBRARY", // FILE_LOAD_LOADLIBRARY
+ "POST_LOADLIBRARY", // FILE_LOAD_POST_LOADLIBRARY
+ "EAGER_FIXUPS", // FILE_LOAD_EAGER_FIXUPS
+ "VTABLE FIXUPS", // FILE_LOAD_VTABLE_FIXUPS
+ "DELIVER_EVENTS", // FILE_LOAD_DELIVER_EVENTS
+ "LOADED", // FILE_LOADED
+ "VERIFY_EXECUTION", // FILE_LOAD_VERIFY_EXECUTION
+ "ACTIVE", // FILE_ACTIVE
+};
+#endif // !DACCESS_COMPILE && (LOGGING || STRESS_LOG)
+
+BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success)
+{
+ CONTRACTL
+ {
+ MODE_ANY;
+ GC_TRIGGERS;
+ THROWS;
+ PRECONDITION(HasLock());
+ }
+ CONTRACTL_END;
+
+ // Increment may happen more than once if reentrancy occurs (e.g. LoadLibrary)
+ if (level > m_level)
+ {
+ // Must complete each level in turn, unless we have an error
+ CONSISTENCY_CHECK(m_pDomainFile->IsError() || (level == (m_level+1)));
+ // Remove the lock from the list if the load is completed
+ if (level >= FILE_ACTIVE)
+ {
+ {
+ GCX_COOP();
+ PEFileListLockHolder lock((PEFileListLock*)m_pList);
+
+#if _DEBUG
+ BOOL fDbgOnly_SuccessfulUnlink =
+#endif
+ m_pList->Unlink(this);
+ _ASSERTE(fDbgOnly_SuccessfulUnlink);
+
+ m_pDomainFile->ClearLoading();
+
+ CONSISTENCY_CHECK(m_dwRefCount >= 2); // Caller (LoadDomainFile) should have 1 refcount and m_pList should have another which was acquired in FileLoadLock::Create.
+
+ m_level = (FileLoadLevel)level;
+
+ // Dev11 bug 236344
+ // In AppDomain::IsLoading, if the lock is taken on m_pList and then FindFileLock returns NULL,
+ // we depend on the DomainFile's load level being up to date. Hence we must update the load
+ // level while the m_pList lock is held.
+ if (success)
+ m_pDomainFile->SetLoadLevel(level);
+ }
+
+
+ Release(); // Release m_pList's refcount on this lock, which was acquired in FileLoadLock::Create
+
+ }
+ else
+ {
+ m_level = (FileLoadLevel)level;
+
+ if (success)
+ m_pDomainFile->SetLoadLevel(level);
+ }
+
+#ifndef DACCESS_COMPILE
+ switch(level)
+ {
+ case FILE_LOAD_ALLOCATE:
+ case FILE_LOAD_ADD_DEPENDENCIES:
+ case FILE_LOAD_DELIVER_EVENTS:
+ case FILE_LOADED:
+ case FILE_ACTIVE: // The timing of stress logs is not critical, so even for the FILE_ACTIVE stage we need not do it while the m_pList lock is held.
+ STRESS_LOG4(LF_CLASSLOADER, LL_INFO100, "Completed Load Level %s for DomainFile %p in AD %i - success = %i\n", fileLoadLevelName[level], m_pDomainFile, m_AppDomainId.m_dwId, success);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+void FileLoadLock::SetError(Exception *ex)
+{
+ CONTRACTL
+ {
+ MODE_ANY;
+ GC_TRIGGERS;
+ THROWS;
+ PRECONDITION(CheckPointer(ex));
+ PRECONDITION(HasLock());
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ m_cachedHR = ex->GetHR();
+
+ LOG((LF_LOADER, LL_WARNING, "LOADER: %x:***%s*\t!!!Non-transient error 0x%x\n",
+ m_pDomainFile->GetAppDomain(), m_pDomainFile->GetSimpleName(), m_cachedHR));
+
+ m_pDomainFile->SetError(ex);
+
+ CompleteLoadLevel(FILE_ACTIVE, FALSE);
+}
+
+void FileLoadLock::AddRef()
+{
+ LIMITED_METHOD_CONTRACT;
+ FastInterlockIncrement((LONG *) &m_dwRefCount);
+}
+
+UINT32 FileLoadLock::Release()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ LONG count = FastInterlockDecrement((LONG *) &m_dwRefCount);
+ if (count == 0)
+ delete this;
+
+ return count;
+}
+
+FileLoadLock::FileLoadLock(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile)
+ : ListLockEntry(pLock, pFile, "File load lock"),
+ m_level((FileLoadLevel) (FILE_LOAD_CREATE)),
+ m_pDomainFile(pDomainFile),
+ m_cachedHR(S_OK),
+ m_AppDomainId(pDomainFile->GetAppDomain()->GetId())
+{
+ WRAPPER_NO_CONTRACT;
+ pFile->AddRef();
+}
+
+void FileLoadLock::HolderLeave(FileLoadLock *pThis)
+{
+ LIMITED_METHOD_CONTRACT;
+ pThis->Leave();
+}
+
+
+
+
+
+
+//
+// Assembly loading:
+//
+// Assembly loading is carefully layered to avoid deadlocks in the
+// presence of circular loading dependencies.
+// A LoadLevel is associated with each assembly as it is being loaded. During the
+// act of loading (abstractly, increasing its load level), its lock is
+// held, and the current load level is stored on the thread. Any
+// recursive loads during that period are automatically restricted to
+// only partially load the dependent assembly to the same level as the
+// caller (or to one short of that level in the presence of a deadlock
+// loop.)
+//
+// Each loading stage must be carfully constructed so that
+// this constraint is expected and can be dealt with.
+//
+// Note that there is one case where this still doesn't handle recursion, and that is the
+// security subsytem. The security system runs managed code, and thus must typically fully
+// initialize assemblies of permission sets it is trying to use. (And of course, these may be used
+// while those assemblies are initializing.) This is dealt with in the historical manner - namely
+// the security system passes in a special flag which says that it will deal with null return values
+// in the case where a load cannot be safely completed due to such issues.
+//
+
+void AppDomain::LoadSystemAssemblies()
+{
+ STANDARD_VM_CONTRACT;
+
+ // The only reason to make an assembly a "system assembly" is if the EE is caching
+ // pointers to stuff in the assembly. Because this is going on, we need to preserve
+ // the invariant that the assembly is loaded into every app domain.
+ //
+ // Right now we have only one system assembly. We shouldn't need to add any more.
+
+ LoadAssembly(NULL, SystemDomain::System()->SystemFile(), FILE_ACTIVE);
+}
+
+FileLoadLevel AppDomain::GetDomainFileLoadLevel(DomainFile *pFile)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ LoadLockHolder lock(this);
+
+ FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetFile());
+
+ if (pLockEntry == NULL)
+ return pFile->GetLoadLevel();
+ else
+ return pLockEntry->GetLoadLevel();
+}
+
+// This checks if the thread has initiated (or completed) loading at the given level. A false guarantees that
+// (a) The current thread (or a thread blocking on the current thread) has not started loading the file
+// at the given level, and
+// (b) No other thread had started loading the file at this level at the start of this function call.
+
+// Note that another thread may start loading the file at that level in a race with the completion of
+// this function. However, the caller still has the guarantee that such a load started after this
+// function was called (and e.g. any state in place before the function call will be seen by the other thread.)
+//
+// Conversely, a true guarantees that either the current thread has started the load step, or another
+// thread has completed the load step.
+//
+
+BOOL AppDomain::IsLoading(DomainFile *pFile, FileLoadLevel level)
+{
+ // Cheap out
+ if (pFile->GetLoadLevel() < level)
+ {
+ FileLoadLock *pLock = NULL;
+ {
+ LoadLockHolder lock(this);
+
+ pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetFile());
+
+ if (pLock == NULL)
+ {
+ // No thread involved with loading
+ return pFile->GetLoadLevel() >= level;
+ }
+
+ pLock->AddRef();
+ }
+
+ FileLoadLockRefHolder lockRef(pLock);
+
+ if (pLock->Acquire(level))
+ {
+ // We got the lock - therefore no other thread has started this loading step yet.
+ pLock->Leave();
+ return FALSE;
+ }
+
+ // We didn't get the lock - either this thread is already doing the load,
+ // or else the load has already finished.
+ }
+ return TRUE;
+}
+
+// CheckLoading is a weaker form of IsLoading, which will not block on
+// other threads waiting for their status. This is appropriate for asserts.
+CHECK AppDomain::CheckLoading(DomainFile *pFile, FileLoadLevel level)
+{
+ // Cheap out
+ if (pFile->GetLoadLevel() < level)
+ {
+ FileLoadLock *pLock = NULL;
+
+ LoadLockHolder lock(this);
+
+ pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetFile());
+
+ if (pLock != NULL
+ && pLock->CanAcquire(level))
+ {
+ // We can get the lock - therefore no other thread has started this loading step yet.
+ CHECK_FAILF(("Loading step %d has not been initiated yet", level));
+ }
+
+ // We didn't get the lock - either this thread is already doing the load,
+ // or else the load has already finished.
+ }
+
+ CHECK_OK;
+}
+
+CHECK AppDomain::CheckCanLoadTypes(Assembly *pAssembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ CHECK_MSG(CheckValidModule(pAssembly->GetManifestModule()),
+ "Type loading can occur only when executing in the assembly's app domain");
+ CHECK_OK;
+}
+
+CHECK AppDomain::CheckCanExecuteManagedCode(MethodDesc* pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ Module* pModule=pMD->GetModule();
+
+ CHECK_MSG(CheckValidModule(pModule),
+ "Managed code can only run when executing in the module's app domain");
+
+ if (!pMD->IsInterface() || pMD->IsStatic()) //interfaces require no activation for instance methods
+ {
+ //cctor could have been interupted by ADU
+ CHECK_MSG(HasUnloadStarted() || pModule->CheckActivated(),
+ "Managed code can only run when its module has been activated in the current app domain");
+ }
+
+ CHECK_MSG(!IsPassiveDomain() || pModule->CanExecuteCode(),
+ "Executing managed code from an unsafe assembly in a Passive AppDomain");
+
+ CHECK_OK;
+}
+
+#endif // !DACCESS_COMPILE
+
+void AppDomain::LoadDomainFile(DomainFile *pFile,
+ FileLoadLevel targetLevel)
+{
+ CONTRACTL
+ {
+ if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
+ if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
+ if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM();); }
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Quick exit if finished
+ if (pFile->GetLoadLevel() >= targetLevel)
+ return;
+
+ // Handle the error case
+ pFile->ThrowIfError(targetLevel);
+
+
+#ifndef DACCESS_COMPILE
+
+ if (pFile->IsLoading())
+ {
+ GCX_PREEMP();
+
+ // Load some more if appropriate
+ LoadLockHolder lock(this);
+
+ FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetFile());
+ if (pLockEntry == NULL)
+ {
+ _ASSERTE (!pFile->IsLoading());
+ return;
+ }
+
+ pLockEntry->AddRef();
+
+ lock.Release();
+
+ LoadDomainFile(pLockEntry, targetLevel);
+ }
+
+#else // DACCESS_COMPILE
+ DacNotImpl();
+#endif // DACCESS_COMPILE
+}
+
+#ifndef DACCESS_COMPILE
+
+FileLoadLevel AppDomain::GetThreadFileLoadLevel()
+{
+ WRAPPER_NO_CONTRACT;
+ if (GetThread()->GetLoadLevelLimiter() == NULL)
+ return FILE_ACTIVE;
+ else
+ return (FileLoadLevel)(GetThread()->GetLoadLevelLimiter()->GetLoadLevel()-1);
+}
+
+
+Assembly *AppDomain::LoadAssembly(AssemblySpec* pIdentity,
+ PEAssembly *pFile,
+ FileLoadLevel targetLevel,
+ AssemblyLoadSecurity *pLoadSecurity /* = NULL */)
+{
+ CONTRACT(Assembly *)
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pFile));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // May be NULL in recursive load case
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ DomainAssembly *pAssembly = LoadDomainAssembly(pIdentity, pFile, targetLevel, pLoadSecurity);
+ PREFIX_ASSUME(pAssembly != NULL);
+
+ RETURN pAssembly->GetAssembly();
+}
+
+#ifndef CROSSGEN_COMPILE
+// Thread stress
+class LoadDomainAssemblyStress : APIThreadStress
+{
+public:
+ AppDomain *pThis;
+ AssemblySpec* pSpec;
+ PEAssembly *pFile;
+ AssemblyLoadSecurity *pLoadSecurity;
+ FileLoadLevel targetLevel;
+
+ LoadDomainAssemblyStress(AppDomain *pThis, AssemblySpec* pSpec, PEAssembly *pFile, FileLoadLevel targetLevel, AssemblyLoadSecurity *pLoadSecurity)
+ : pThis(pThis), pSpec(pSpec), pFile(pFile), pLoadSecurity(pLoadSecurity), targetLevel(targetLevel) {LIMITED_METHOD_CONTRACT;}
+
+ void Invoke()
+ {
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_SO_INTOLERANT;
+ SetupThread();
+ pThis->LoadDomainAssembly(pSpec, pFile, targetLevel, pLoadSecurity);
+ }
+};
+#endif // CROSSGEN_COMPILE
+
+extern BOOL AreSameBinderInstance(ICLRPrivBinder *pBinderA, ICLRPrivBinder *pBinderB);
+
+DomainAssembly* AppDomain::LoadDomainAssembly( AssemblySpec* pSpec,
+ PEAssembly *pFile,
+ FileLoadLevel targetLevel,
+ AssemblyLoadSecurity *pLoadSecurity /* = NULL */)
+{
+ STATIC_CONTRACT_THROWS;
+
+ if (pSpec == nullptr)
+ {
+ // skip caching, since we don't have anything to base it on
+ return LoadDomainAssemblyInternal(pSpec, pFile, targetLevel, pLoadSecurity);
+ }
+
+ DomainAssembly* pRetVal = NULL;
+ EX_TRY
+ {
+ pRetVal = LoadDomainAssemblyInternal(pSpec, pFile, targetLevel, pLoadSecurity);
+ }
+ EX_HOOK
+ {
+ Exception* pEx=GET_EXCEPTION();
+ if (!pEx->IsTransient())
+ {
+#if defined(FEATURE_CORECLR)
+ // Setup the binder reference in AssemblySpec from the PEAssembly if one is not already set.
+ ICLRPrivBinder* pCurrentBindingContext = pSpec->GetBindingContext();
+ ICLRPrivBinder* pBindingContextFromPEAssembly = pFile->GetBindingContext();
+
+ if (pCurrentBindingContext == NULL)
+ {
+ // Set the binding context we got from the PEAssembly if AssemblySpec does not
+ // have that information
+ _ASSERTE(pBindingContextFromPEAssembly != NULL);
+ pSpec->SetBindingContext(pBindingContextFromPEAssembly);
+ }
+#if defined(_DEBUG)
+ else
+ {
+ // Binding context in the spec should be the same as the binding context in the PEAssembly
+ _ASSERTE(AreSameBinderInstance(pCurrentBindingContext, pBindingContextFromPEAssembly));
+ }
+#endif // _DEBUG
+#endif // defined(FEATURE_CORECLR)
+
+ if (!EEFileLoadException::CheckType(pEx))
+ {
+ StackSString name;
+ pSpec->GetFileOrDisplayName(0, name);
+ pEx=new EEFileLoadException(name, pEx->GetHR(), NULL, pEx);
+ AddExceptionToCache(pSpec, pEx);
+ PAL_CPP_THROW(Exception *, pEx);
+ }
+ else
+ AddExceptionToCache(pSpec, pEx);
+ }
+ }
+ EX_END_HOOK;
+
+ return pRetVal;
+}
+
+
+DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity,
+ PEAssembly *pFile,
+ FileLoadLevel targetLevel,
+ AssemblyLoadSecurity *pLoadSecurity /* = NULL */)
+{
+ CONTRACT(DomainAssembly *)
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pFile));
+ PRECONDITION(CheckPointer(pLoadSecurity, NULL_OK));
+ PRECONDITION(pFile->IsSystem() || ::GetAppDomain()==this);
+ POSTCONDITION(CheckPointer(RETVAL));
+ POSTCONDITION(RETVAL->GetLoadLevel() >= GetThreadFileLoadLevel()
+ || RETVAL->GetLoadLevel() >= targetLevel);
+ POSTCONDITION(RETVAL->CheckNoError(targetLevel));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+
+ DomainAssembly * result;
+
+#ifndef CROSSGEN_COMPILE
+ LoadDomainAssemblyStress ts (this, pIdentity, pFile, targetLevel, pLoadSecurity);
+#endif
+
+ // Go into preemptive mode since this may take a while.
+ GCX_PREEMP();
+
+ // Check for existing fully loaded assembly, or for an assembly which has failed during the loading process.
+ result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad);
+
+ if (result == NULL)
+ {
+ // Allocate the DomainAssembly a bit early to avoid GC mode problems. We could potentially avoid
+ // a rare redundant allocation by moving this closer to FileLoadLock::Create, but it's not worth it.
+
+ NewHolder<DomainAssembly> pDomainAssembly;
+ pDomainAssembly = new DomainAssembly(this, pFile, pLoadSecurity, this->GetLoaderAllocator());
+
+ LoadLockHolder lock(this);
+
+ // Find the list lock entry
+ FileLoadLock * fileLock = (FileLoadLock *)lock->FindFileLock(pFile);
+ if (fileLock == NULL)
+ {
+ // Check again in case we were racing
+ result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad);
+ if (result == NULL)
+ {
+ // We are the first one in - create the DomainAssembly
+ fileLock = FileLoadLock::Create(lock, pFile, pDomainAssembly);
+ pDomainAssembly.SuppressRelease();
+ }
+ }
+ else
+ {
+ fileLock->AddRef();
+ }
+
+ lock.Release();
+
+ if (result == NULL)
+ {
+ // We pass our ref on fileLock to LoadDomainFile to release.
+
+ // Note that if we throw here, we will poison fileLock with an error condition,
+ // so it will not be removed until app domain unload. So there is no need
+ // to release our ref count.
+ result = (DomainAssembly *)LoadDomainFile(fileLock, targetLevel);
+ }
+ else
+ {
+ result->EnsureLoadLevel(targetLevel);
+ }
+ }
+ else
+ result->EnsureLoadLevel(targetLevel);
+
+ // Malformed metadata may contain a Module reference to what is actually
+ // an Assembly. In this case we need to throw an exception, since returning
+ // a DomainModule as a DomainAssembly is a type safety violation.
+ if (!result->IsAssembly())
+ {
+ ThrowHR(COR_E_ASSEMBLYEXPECTED);
+ }
+
+ // Cache result in all cases, since found pFile could be from a different AssemblyRef than pIdentity
+ // Do not cache WindowsRuntime assemblies, they are cached in code:CLRPrivTypeCacheWinRT
+ if ((pIdentity != NULL) && (pIdentity->CanUseWithBindingCache()) && (result->CanUseWithBindingCache()))
+ GetAppDomain()->AddAssemblyToCache(pIdentity, result);
+
+ RETURN result;
+} // AppDomain::LoadDomainAssembly
+
+#ifdef FEATURE_MULTIMODULE_ASSEMBLIES
+
+#ifndef CROSSGEN_COMPILE
+// Thread stress
+class LoadDomainModuleStress : APIThreadStress
+{
+public:
+ AppDomain *pThis;
+ DomainAssembly *pAssembly;
+ PEModule *pFile;
+ FileLoadLevel targetLevel;
+
+ LoadDomainModuleStress(AppDomain *pThis, DomainAssembly *pAssembly, PEModule *pFile, FileLoadLevel targetLevel)
+ : pThis(pThis), pAssembly(pAssembly), pFile(pFile), targetLevel(targetLevel) {LIMITED_METHOD_CONTRACT;}
+
+ void Invoke()
+ {
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_SO_INTOLERANT;
+ SetupThread();
+ pThis->LoadDomainModule(pAssembly, pFile, targetLevel);
+ }
+};
+#endif // CROSSGEN_COMPILE
+
+DomainModule *AppDomain::LoadDomainModule(DomainAssembly *pAssembly, PEModule *pFile,
+ FileLoadLevel targetLevel)
+{
+ CONTRACT(DomainModule *)
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pAssembly));
+ PRECONDITION(CheckPointer(pFile));
+ POSTCONDITION(CheckPointer(RETVAL));
+ POSTCONDITION(RETVAL->GetLoadLevel() >= GetThreadFileLoadLevel()
+ || RETVAL->GetLoadLevel() >= targetLevel);
+ POSTCONDITION(RETVAL->CheckNoError(targetLevel));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ GCX_PREEMP();
+
+#ifndef CROSSGEN_COMPILE
+ // Thread stress
+ LoadDomainModuleStress ts (this, pAssembly, pFile, targetLevel);
+#endif
+
+ // Check for existing fully loaded assembly
+ DomainModule *result = pAssembly->FindModule(pFile);
+ if (result == NULL)
+ {
+ LoadLockHolder lock(this);
+
+ // Check again in case we were racing
+ result = pAssembly->FindModule(pFile);
+ if (result == NULL)
+ {
+ // Find the list lock entry
+ FileLoadLock *fileLock = (FileLoadLock *) lock->FindFileLock(pFile);
+ if (fileLock == NULL)
+ {
+ // We are the first one in - create the DomainModule
+ NewHolder<DomainModule> pDomainModule(new DomainModule(this, pAssembly, pFile));
+ fileLock = FileLoadLock::Create(lock, pFile, pDomainModule);
+ pDomainModule.SuppressRelease();
+ }
+ else
+ fileLock->AddRef();
+
+ lock.Release();
+
+ // We pass our ref on fileLock to LoadDomainFile to release.
+
+ // Note that if we throw here, we will poison fileLock with an error condition,
+ // so it will not be removed until app domain unload. So there is no need
+ // to release our ref count.
+
+ result = (DomainModule *) LoadDomainFile(fileLock, targetLevel);
+ }
+ else
+ {
+ lock.Release();
+ result->EnsureLoadLevel(targetLevel);
+ }
+
+ }
+ else
+ result->EnsureLoadLevel(targetLevel);
+
+ // Malformed metadata may contain an Assembly reference to what is actually
+ // a Module. In this case we need to throw an exception, since returning a
+ // DomainAssembly as a DomainModule is a type safety violation.
+ if (result->IsAssembly())
+ {
+ ThrowHR(COR_E_ASSEMBLY_NOT_EXPECTED);
+ }
+
+ RETURN result;
+}
+#endif // FEATURE_MULTIMODULE_ASSEMBLIES
+
+struct LoadFileArgs
+{
+ FileLoadLock *pLock;
+ FileLoadLevel targetLevel;
+ DomainFile *result;
+};
+
+static void LoadDomainFile_Wrapper(void *ptr)
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_SO_INTOLERANT;
+ GCX_PREEMP();
+ LoadFileArgs *args = (LoadFileArgs *) ptr;
+ args->result = GetAppDomain()->LoadDomainFile(args->pLock, args->targetLevel);
+}
+
+DomainFile *AppDomain::LoadDomainFile(FileLoadLock *pLock, FileLoadLevel targetLevel)
+{
+ CONTRACT(DomainFile *)
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pLock));
+ PRECONDITION(pLock->GetDomainFile()->GetAppDomain() == this);
+ POSTCONDITION(RETVAL->GetLoadLevel() >= GetThreadFileLoadLevel()
+ || RETVAL->GetLoadLevel() >= targetLevel);
+ POSTCONDITION(RETVAL->CheckNoError(targetLevel));
+ }
+ CONTRACT_END;
+
+
+ if(!CanLoadCode())
+ COMPlusThrow(kAppDomainUnloadedException);
+
+ // Thread stress
+ APIThreadStress::SyncThreadStress();
+
+ DomainFile *pFile = pLock->GetDomainFile();
+
+ // Make sure we release the lock on exit
+ FileLoadLockRefHolder lockRef(pLock);
+
+ // We need to perform the early steps of loading mscorlib without a domain transition. This is
+ // important for bootstrapping purposes - we need to get mscorlib at least partially loaded
+ // into a domain before we can run serialization code to do the transition.
+ //
+ // Note that we cannot do this in general for all assemblies, because some of the security computations
+ // require the managed exposed object, which must be created in the correct app domain.
+
+ if (this != GetAppDomain()
+ && pFile->GetFile()->IsSystem()
+ && targetLevel > FILE_LOAD_ALLOCATE)
+ {
+ // Re-call the routine with a limited load level. This will cause the first part of the load to
+ // get performed in the current app domain.
+
+ pLock->AddRef();
+ LoadDomainFile(pLock, targetLevel > FILE_LOAD_ALLOCATE ? FILE_LOAD_ALLOCATE : targetLevel);
+
+ // Now continue on to complete the rest of the load, if any.
+ }
+
+ // Do a quick out check for the already loaded case.
+ if (pLock->GetLoadLevel() >= targetLevel)
+ {
+ pFile->ThrowIfError(targetLevel);
+
+ RETURN pFile;
+ }
+
+#ifndef CROSSGEN_COMPILE
+ // Make sure we are in the right domain. Many of the load operations require the target domain
+ // to be the current app domain, most notably anything involving managed code or managed object
+ // creation.
+ if (this != GetAppDomain()
+ && (!pFile->GetFile()->IsSystem() || targetLevel > FILE_LOAD_ALLOCATE))
+ {
+ // Transition to the correct app domain and perform the load there.
+ GCX_COOP();
+
+ // we will release the lock in the other app domain
+ lockRef.SuppressRelease();
+
+ if(!CanLoadCode() || GetDefaultContext() ==NULL)
+ COMPlusThrow(kAppDomainUnloadedException);
+ LoadFileArgs args = {pLock, targetLevel, NULL};
+ GetThread()->DoADCallBack(this, LoadDomainFile_Wrapper, (void *) &args, ADV_CREATING);
+
+ RETURN args.result;
+ }
+#endif // CROSSGEN_COMPILE
+
+ // Initialize a loading queue. This will hold any loads which are triggered recursively but
+ // which cannot be immediately satisfied due to anti-deadlock constraints.
+
+ // PendingLoadQueues are allocated on the stack during a load, and
+ // shared with all nested loads on the same thread. (Note that we won't use
+ // "candidate" if we are in a recursive load; that's OK since they are cheap to
+ // construct.)
+ FileLoadLevel immediateTargetLevel = targetLevel;
+ {
+ LoadLevelLimiter limit;
+ limit.Activate();
+
+ // We cannot set a target level higher than that allowed by the limiter currently.
+ // This is because of anti-deadlock constraints.
+ if (immediateTargetLevel > limit.GetLoadLevel())
+ immediateTargetLevel = limit.GetLoadLevel();
+
+ LOG((LF_LOADER, LL_INFO100, "LOADER: %x:***%s*\t>>>Load initiated, %s/%s\n",
+ pFile->GetAppDomain(), pFile->GetSimpleName(),
+ fileLoadLevelName[immediateTargetLevel], fileLoadLevelName[targetLevel]));
+
+ // Now loop and do the load incrementally to the target level.
+ if (pLock->GetLoadLevel() < immediateTargetLevel)
+ {
+ // Thread stress
+ APIThreadStress::SyncThreadStress();
+
+ while (pLock->Acquire(immediateTargetLevel))
+ {
+ FileLoadLevel workLevel;
+ {
+ FileLoadLockHolder fileLock(pLock);
+
+ // Work level is next step to do
+ workLevel = (FileLoadLevel)(fileLock->GetLoadLevel()+1);
+
+ // Set up the anti-deadlock constraint: we cannot safely recursively load any assemblies
+ // on this thread to a higher level than this assembly is being loaded now.
+ // Note that we do allow work at a parallel level; any deadlocks caused here will
+ // be resolved by the deadlock detection in the FileLoadLocks.
+ limit.SetLoadLevel(workLevel);
+
+ LOG((LF_LOADER,
+ (workLevel == FILE_LOAD_BEGIN
+ || workLevel == FILE_LOADED
+ || workLevel == FILE_ACTIVE)
+ ? LL_INFO10 : LL_INFO1000,
+ "LOADER: %p:***%s*\t loading at level %s\n",
+ this, pFile->GetSimpleName(), fileLoadLevelName[workLevel]));
+
+ TryIncrementalLoad(pFile, workLevel, fileLock);
+ }
+ TESTHOOKCALL(CompletedFileLoadLevel(GetId().m_dwId,pFile,workLevel));
+ }
+
+ if (pLock->GetLoadLevel() == immediateTargetLevel-1)
+ {
+ LOG((LF_LOADER, LL_INFO100, "LOADER: %x:***%s*\t<<<Load limited due to detected deadlock, %s\n",
+ pFile->GetAppDomain(), pFile->GetSimpleName(),
+ fileLoadLevelName[immediateTargetLevel-1]));
+ }
+ }
+
+ LOG((LF_LOADER, LL_INFO100, "LOADER: %x:***%s*\t<<<Load completed, %s\n",
+ pFile->GetAppDomain(), pFile->GetSimpleName(),
+ fileLoadLevelName[pLock->GetLoadLevel()]));
+
+ }
+
+ // There may have been an error stored on the domain file by another thread, or from a previous load
+ pFile->ThrowIfError(targetLevel);
+
+ // There are two normal results from the above loop.
+ //
+ // 1. We succeeded in loading the file to the current thread's load level.
+ // 2. We succeeded in loading the file to the current thread's load level - 1, due
+ // to deadlock condition with another thread loading the same assembly.
+ //
+ // Either of these are considered satisfactory results, as code inside a load must expect
+ // a parial load result.
+ //
+ // However, if load level elevation has occurred, then it is possible for a deadlock to
+ // prevent us from loading an assembly which was loading before the elevation at a radically
+ // lower level. In such a case, we throw an exception which transiently fails the current
+ // load, since it is likely we have not satisfied the caller.
+ // (An alternate, and possibly preferable, strategy here would be for all callers to explicitly
+ // identify the minimum load level acceptable via CheckLoadDomainFile and throw from there.)
+
+ pFile->RequireLoadLevel((FileLoadLevel)(immediateTargetLevel-1));
+
+
+ RETURN pFile;
+}
+
+void AppDomain::TryIncrementalLoad(DomainFile *pFile, FileLoadLevel workLevel, FileLoadLockHolder &lockHolder)
+{
+ STANDARD_VM_CONTRACT;
+
+ // This is factored out so we don't call EX_TRY in a loop (EX_TRY can _alloca)
+
+ BOOL released = FALSE;
+ FileLoadLock* pLoadLock = lockHolder.GetValue();
+
+ EX_TRY
+ {
+#ifndef FEATURE_CORECLR
+ // Event Tracing for Windows is used to log data for performance and functional testing purposes.
+ // The events below are used to measure the performance of two steps in the assembly loader, namely assembly initialization and delivering events.
+ StackSString ETWAssemblySimpleName;
+ if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_PRIVATEBINDING_KEYWORD))
+ {
+ LPCUTF8 simpleName = pFile->GetSimpleName();
+ ETWAssemblySimpleName.AppendUTF8(simpleName ? simpleName : "NULL"); // Gather data used by ETW events later in this function.
+ }
+#endif // FEATURE_CORECLR
+
+ // Special case: for LoadLibrary, we cannot hold the lock during the
+ // actual LoadLibrary call, because we might get a callback from _CorDllMain on any
+ // other thread. (Note that this requires DomainFile's LoadLibrary to be independently threadsafe.)
+
+ if (workLevel == FILE_LOAD_LOADLIBRARY)
+ {
+ lockHolder.Release();
+ released = TRUE;
+ }
+#ifndef FEATURE_CORECLR
+ else if (workLevel == FILE_LOAD_DELIVER_EVENTS)
+ {
+ FireEtwLoaderDeliverEventsPhaseStart(GetId().m_dwId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable, NULL, ETWAssemblySimpleName, GetClrInstanceId());
+ }
+#endif // FEATURE_CORECLR
+
+ // Do the work
+ TESTHOOKCALL(NextFileLoadLevel(GetId().m_dwId,pFile,workLevel));
+#ifndef FEATURE_CORECLR
+ if (workLevel == FILE_LOAD_ALLOCATE)
+ {
+ FireEtwLoaderAssemblyInitPhaseStart(GetId().m_dwId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable, NULL, ETWAssemblySimpleName, GetClrInstanceId());
+ }
+#endif // FEATURE_CORECLR
+ BOOL success = pFile->DoIncrementalLoad(workLevel);
+#ifndef FEATURE_CORECLR
+ if (workLevel == FILE_LOAD_ALLOCATE)
+ {
+ FireEtwLoaderAssemblyInitPhaseEnd(GetId().m_dwId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable, NULL, ETWAssemblySimpleName, GetClrInstanceId());
+ }
+#endif // FEATURE_CORECLR
+ TESTHOOKCALL(CompletingFileLoadLevel(GetId().m_dwId,pFile,workLevel));
+ if (released)
+ {
+ // Reobtain lock to increment level. (Note that another thread may
+ // have already done it which is OK.
+ if (pLoadLock->Acquire(workLevel))
+ {
+ // note lockHolder.Acquire isn't wired up to actually take the lock
+ lockHolder = pLoadLock;
+ released = FALSE;
+ }
+ }
+
+ if (!released)
+ {
+ // Complete the level.
+ if (pLoadLock->CompleteLoadLevel(workLevel, success) &&
+ pLoadLock->GetLoadLevel()==FILE_LOAD_DELIVER_EVENTS)
+ {
+ lockHolder.Release();
+ released = TRUE;
+ pFile->DeliverAsyncEvents();
+#ifndef FEATURE_CORECLR
+ FireEtwLoaderDeliverEventsPhaseEnd(GetId().m_dwId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable, NULL, ETWAssemblySimpleName, GetClrInstanceId());
+#endif // FEATURE_CORECLR
+ };
+ }
+ }
+ EX_HOOK
+ {
+ Exception *pEx = GET_EXCEPTION();
+
+
+ //We will cache this error and wire this load to forever fail,
+ // unless the exception is transient or the file is loaded OK but just cannot execute
+ if (!pEx->IsTransient() && !pFile->IsLoaded())
+ {
+
+ if (released)
+ {
+ // Reobtain lock to increment level. (Note that another thread may
+ // have already done it which is OK.
+ if (pLoadLock->Acquire(workLevel)) // note pLockHolder->Acquire isn't wired up to actually take the lock
+ {
+ // note lockHolder.Acquire isn't wired up to actually take the lock
+ lockHolder = pLoadLock;
+ released = FALSE;
+ }
+ }
+
+ if (!released)
+ {
+ // Report the error in the lock
+ pLoadLock->SetError(pEx);
+ }
+
+ if (!EEFileLoadException::CheckType(pEx))
+ EEFileLoadException::Throw(pFile->GetFile(), pEx->GetHR(), pEx);
+ }
+
+ // Otherwise, we simply abort this load, and can retry later on.
+ // @todo cleanup: make sure that each level is restartable after an exception, and
+ // leaves no bad side effects
+ }
+ EX_END_HOOK;
+}
+
+// Checks whether the module is valid to be in the given app domain (need not be yet loaded)
+CHECK AppDomain::CheckValidModule(Module * pModule)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (pModule->FindDomainFile(this) != NULL)
+ CHECK_OK;
+
+ CCHECK_START
+ {
+ Assembly * pAssembly = pModule->GetAssembly();
+
+ CCHECK(pAssembly->IsDomainNeutral());
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ Assembly * pSharedAssembly = NULL;
+ _ASSERTE(this == ::GetAppDomain());
+ {
+ SharedAssemblyLocator locator(pAssembly->GetManifestFile());
+ pSharedAssembly = SharedDomain::GetDomain()->FindShareableAssembly(&locator);
+ }
+
+ CCHECK(pAssembly == pSharedAssembly);
+#endif
+ }
+ CCHECK_END;
+
+ CHECK_OK;
+}
+
+#ifdef FEATURE_LOADER_OPTIMIZATION
+// Loads an existing Module into an AppDomain
+// WARNING: this can only be done in a very limited scenario - the Module must be an unloaded domain neutral
+// dependency in the app domain in question. Normal code should not call this!
+DomainFile *AppDomain::LoadDomainNeutralModuleDependency(Module *pModule, FileLoadLevel targetLevel)
+{
+ CONTRACT(DomainFile *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(::GetAppDomain()==this);
+ PRECONDITION(CheckPointer(pModule));
+ POSTCONDITION(CheckValidModule(pModule));
+ POSTCONDITION(CheckPointer(RETVAL));
+ POSTCONDITION(RETVAL->GetModule() == pModule);
+ }
+ CONTRACT_END;
+
+ DomainFile *pDomainFile = pModule->FindDomainFile(this);
+
+ STRESS_LOG3(LF_CLASSLOADER, LL_INFO100,"LDNMD: DomainFile %p for module %p in AppDomain %i\n",pDomainFile,pModule,GetId().m_dwId);
+
+ if (pDomainFile == NULL)
+ {
+ GCX_PREEMP();
+
+ Assembly *pAssembly = pModule->GetAssembly();
+
+ DomainAssembly *pDomainAssembly = pAssembly->FindDomainAssembly(this);
+ if (pDomainAssembly == NULL)
+ {
+ AssemblySpec spec(this);
+ spec.InitializeSpec(pAssembly->GetManifestFile());
+
+ pDomainAssembly = spec.LoadDomainAssembly(targetLevel);
+ }
+ else
+ {
+ //if the domain assembly already exists, we need to load it to the target level
+ pDomainAssembly->EnsureLoadLevel (targetLevel);
+ }
+
+ if(pAssembly != pDomainAssembly->GetAssembly())
+ {
+ ThrowHR(SECURITY_E_INCOMPATIBLE_SHARE);
+ }
+
+#ifdef FEATURE_MULTIMODULE_ASSEMBLIES
+ if (pModule == pAssembly->GetManifestModule())
+ pDomainFile = pDomainAssembly;
+ else
+ {
+ pDomainFile = LoadDomainModule(pDomainAssembly, (PEModule*) pModule->GetFile(), targetLevel);
+ STRESS_LOG4(LF_CLASSLOADER, LL_INFO100,"LDNMD: DF: for %p[%p/%p] is %p",
+ pModule,pDomainAssembly,pModule->GetFile(),pDomainFile);
+ }
+#else
+ _ASSERTE (pModule == pAssembly->GetManifestModule());
+ pDomainFile = pDomainAssembly;
+#endif
+ }
+ else
+ {
+ // If the DomainFile already exists, we need to load it to the target level.
+ pDomainFile->EnsureLoadLevel (targetLevel);
+ }
+
+ RETURN pDomainFile;
+}
+
+void AppDomain::SetSharePolicy(SharePolicy policy)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if ((int)policy > SHARE_POLICY_COUNT)
+ COMPlusThrow(kArgumentException,W("Argument_InvalidValue"));
+
+ // We cannot make all code domain neutral and still provide complete compatibility with regard
+ // to using custom security policy and assembly evidence.
+ //
+ // In particular, if you try to do either of the above AFTER loading a domain neutral assembly
+ // out of the GAC, we will now throw an exception. The remedy would be to either not use SHARE_POLICY_ALWAYS
+ // (change LoaderOptimizationMultiDomain to LoaderOptimizationMultiDomainHost), or change the loading order
+ // in the app domain to do the policy set or evidence load earlier (which BTW will have the effect of
+ // automatically using MDH rather than MD, for the same result.)
+ //
+ // We include a compatibility flag here to preserve old functionality if necessary - this has the effect
+ // of never using SHARE_POLICY_ALWAYS.
+ if (policy == SHARE_POLICY_ALWAYS &&
+ (HasSetSecurityPolicy()
+ || GetCompatibilityFlag(compatOnlyGACDomainNeutral)))
+ {
+ // Never share assemblies not in the GAC
+ policy = SHARE_POLICY_GAC;
+ }
+
+ if (policy != m_SharePolicy)
+ {
+
+#ifdef FEATURE_PREJIT
+
+#ifdef FEATURE_FUSION
+ GCX_PREEMP();
+
+ // Update the native image config flags
+ FusionBind::SetApplicationContextDWORDProperty(m_pFusionContext, ACTAG_ZAP_CONFIG_FLAGS,
+ PEFile::GetNativeImageConfigFlags());
+#endif //FEATURE_FUSION
+
+#endif // FEATURE_PREJIT
+
+ m_SharePolicy = policy;
+ }
+
+ return;
+}
+
+#ifdef FEATURE_FUSION
+BOOL AppDomain::ReduceSharePolicyFromAlways()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // We may have already committed to always sharing - this is the case if
+ // we have already loaded non-GAC-bound assemblies as domain neutral.
+
+ if (GetSharePolicy() == SHARE_POLICY_ALWAYS)
+ {
+ AppDomain::AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ // If we have loaded any non-GAC assemblies, we cannot set app domain policy as we have
+ // already committed to the process-wide policy.
+
+ while (i.Next(pDomainAssembly.This()))
+ {
+ if (pDomainAssembly->GetAssembly() &&
+ pDomainAssembly->GetAssembly()->IsDomainNeutral() &&
+ !pDomainAssembly->IsClosedInGAC())
+ {
+ // This assembly has been loaded domain neutral because of SHARE_POLICY_ALWAYS. We
+ // can't reverse that decision now, so we have to fail the sharing policy change.
+ return FALSE;
+ }
+ }
+
+ // We haven't loaded any non-GAC assemblies yet - scale back to SHARE_POLICY_GAC so
+ // future non-GAC assemblies won't be loaded as domain neutral.
+ SetSharePolicy(SHARE_POLICY_GAC);
+ }
+
+ return TRUE;
+}
+#endif // FEATURE_FUSION
+
+AppDomain::SharePolicy AppDomain::GetSharePolicy()
+{
+ LIMITED_METHOD_CONTRACT;
+ // If the policy has been explicitly set for
+ // the domain, use that.
+ SharePolicy policy = m_SharePolicy;
+
+ // Pick up the a specified config policy
+ if (policy == SHARE_POLICY_UNSPECIFIED)
+ policy = (SharePolicy) g_pConfig->DefaultSharePolicy();
+
+ // Next, honor a host's request for global policy.
+ if (policy == SHARE_POLICY_UNSPECIFIED)
+ policy = (SharePolicy) g_dwGlobalSharePolicy;
+
+ // If all else fails, use the hardwired default policy.
+ if (policy == SHARE_POLICY_UNSPECIFIED)
+ policy = SHARE_POLICY_DEFAULT;
+
+ return policy;
+}
+#endif // FEATURE_LOADER_OPTIMIZATION
+
+
+#ifdef FEATURE_CORECLR
+void AppDomain::CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID * pGuid)
+{
+ STANDARD_VM_CONTRACT;
+
+ //
+ // The native images are ever used only for trusted images in CoreCLR.
+ // We don't wish to open the IL file at runtime so we just forgo any
+ // eager consistency checking. But we still want to prevent mistmatched
+ // NGen images from being used. We record all mappings between assembly
+ // names and MVID, and fail once we detect mismatch.
+ //
+
+ if (pSpec->IsStrongNamed() && pSpec->HasPublicKey())
+ {
+ pSpec->ConvertPublicKeyToToken();
+ }
+
+ //
+ // CoreCLR binder unifies assembly versions. Ignore assembly version here to
+ // detect more types of potential mismatches.
+ //
+ AssemblyMetaDataInternal * pContext = pSpec->GetContext();
+ pContext->usMajorVersion = (USHORT)-1;
+ pContext->usMinorVersion = (USHORT)-1;
+ pContext->usBuildNumber = (USHORT)-1;
+ pContext->usRevisionNumber = (USHORT)-1;
+
+ // Ignore the WinRT type while considering if two assemblies have the same identity.
+ pSpec->SetWindowsRuntimeType(NULL, NULL);
+
+ CrstHolder ch(&m_DomainCrst);
+
+ const NativeImageDependenciesEntry * pEntry = m_NativeImageDependencies.Lookup(pSpec);
+
+ if (pEntry != NULL)
+ {
+ if (*pGuid != pEntry->m_guidMVID)
+ {
+ SString msg;
+ msg.Printf("ERROR: Native images generated against multiple versions of assembly %s. ", pSpec->GetName());
+ WszOutputDebugString(msg.GetUnicode());
+ COMPlusThrowNonLocalized(kFileLoadException, msg.GetUnicode());
+ }
+ }
+ else
+ {
+ //
+ // No entry yet - create one
+ //
+ AllocMemTracker amTracker;
+ AllocMemTracker *pamTracker = &amTracker;
+
+ NativeImageDependenciesEntry * pNewEntry =
+ new (pamTracker->Track(GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(NativeImageDependenciesEntry)))))
+ NativeImageDependenciesEntry();
+
+ pNewEntry->m_AssemblySpec.CopyFrom(pSpec);
+ pNewEntry->m_AssemblySpec.CloneFieldsToLoaderHeap(AssemblySpec::ALL_OWNED, GetLowFrequencyHeap(), pamTracker);
+
+ pNewEntry->m_guidMVID = *pGuid;
+
+ m_NativeImageDependencies.Add(pNewEntry);
+ amTracker.SuppressRelease();
+ }
+}
+#endif // FEATURE_CORECLR
+
+
+void AppDomain::SetupSharedStatics()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+#ifndef CROSSGEN_COMPILE
+ if (NingenEnabled())
+ return;
+
+ LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: SetupSharedStatics()"));
+
+ // don't do any work in init stage. If not init only do work in non-shared case if are default domain
+ _ASSERTE(!g_fEEInit);
+
+ // Because we are allocating/referencing objects, need to be in cooperative mode
+ GCX_COOP();
+
+ static OBJECTHANDLE hSharedStaticsHandle = NULL;
+
+ if (hSharedStaticsHandle == NULL) {
+ // Note that there is no race here since the default domain is always set up first
+ _ASSERTE(IsDefaultDomain());
+
+ MethodTable *pMT = MscorlibBinder::GetClass(CLASS__SHARED_STATICS);
+ _ASSERTE(pMT->IsClassPreInited());
+
+ hSharedStaticsHandle = CreateGlobalHandle(AllocateObject(pMT));
+ }
+
+ DomainLocalModule *pLocalModule;
+
+ if (IsSingleAppDomain())
+ {
+ pLocalModule = MscorlibBinder::GetModule()->GetDomainLocalModule();
+ }
+ else
+ {
+ pLocalModule = GetDomainLocalBlock()->GetModuleSlot(
+ MscorlibBinder::GetModule()->GetModuleIndex());
+ }
+
+ FieldDesc *pFD = MscorlibBinder::GetField(FIELD__SHARED_STATICS__SHARED_STATICS);
+
+ OBJECTREF* pHandle = (OBJECTREF*)
+ ((TADDR)pLocalModule->GetPrecomputedGCStaticsBasePointer()+pFD->GetOffset());
+ SetObjectReference( pHandle, ObjectFromHandle(hSharedStaticsHandle), this );
+
+ // This is a convenient place to initialize String.Empty.
+ // It is treated as intrinsic by the JIT as so the static constructor would never run.
+ // Leaving it uninitialized would confuse debuggers.
+
+ // String should not have any static constructors.
+ _ASSERTE(g_pStringClass->IsClassPreInited());
+
+ FieldDesc * pEmptyStringFD = MscorlibBinder::GetField(FIELD__STRING__EMPTY);
+ OBJECTREF* pEmptyStringHandle = (OBJECTREF*)
+ ((TADDR)pLocalModule->GetPrecomputedGCStaticsBasePointer()+pEmptyStringFD->GetOffset());
+ SetObjectReference( pEmptyStringHandle, StringObject::GetEmptyString(), this );
+#endif // CROSSGEN_COMPILE
+}
+
+DomainAssembly * AppDomain::FindAssembly(PEAssembly * pFile, FindAssemblyOptions options/* = FindAssemblyOptions_None*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ const bool includeFailedToLoad = (options & FindAssemblyOptions_IncludeFailedToLoad) != 0;
+
+#ifdef FEATURE_HOSTED_BINDER
+ if (pFile->HasHostAssembly())
+ {
+ DomainAssembly * pDA = FindAssembly(pFile->GetHostAssembly());
+ if (pDA != nullptr && (pDA->IsLoaded() || (includeFailedToLoad && pDA->IsError())))
+ {
+ return pDA;
+ }
+ return nullptr;
+ }
+#endif
+
+ AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded |
+ (includeFailedToLoad ? kIncludeFailedToLoad : 0) |
+ (pFile->IsIntrospectionOnly() ? kIncludeIntrospection : kIncludeExecution)));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (i.Next(pDomainAssembly.This()))
+ {
+ PEFile * pManifestFile = pDomainAssembly->GetFile();
+ if (pManifestFile &&
+ !pManifestFile->IsResource() &&
+ pManifestFile->Equals(pFile))
+ {
+ // Caller already has PEAssembly, so we can give DomainAssembly away freely without AddRef
+ return pDomainAssembly.Extract();
+ }
+ }
+ return NULL;
+}
+
+static const AssemblyIterationFlags STANDARD_IJW_ITERATOR_FLAGS =
+ (AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution | kExcludeCollectible);
+
+#ifdef FEATURE_MIXEDMODE
+Module * AppDomain::GetIJWModule(HMODULE hMod)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ AssemblyIterator i = IterateAssembliesEx(STANDARD_IJW_ITERATOR_FLAGS);
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (i.Next(pDomainAssembly.This()))
+ {
+ _ASSERTE(!pDomainAssembly->IsCollectible());
+ DomainFile * result = pDomainAssembly->FindIJWModule(hMod);
+
+ if (result == NULL)
+ continue;
+ result->EnsureAllocated();
+ return result->GetLoadedModule();
+ }
+
+ return NULL;
+}
+
+DomainFile * AppDomain::FindIJWDomainFile(HMODULE hMod, const SString & path)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ AssemblyIterator i = IterateAssembliesEx(STANDARD_IJW_ITERATOR_FLAGS);
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (i.Next(pDomainAssembly.This()))
+ {
+ _ASSERTE(!pDomainAssembly->IsCollectible());
+ if (pDomainAssembly->GetCurrentAssembly() == NULL)
+ continue;
+
+ DomainFile * result = pDomainAssembly->GetCurrentAssembly()->FindIJWDomainFile(hMod, path);
+
+ if (result != NULL)
+ return result;
+ }
+
+ return NULL;
+}
+#endif // FEATURE_MIXEDMODE
+
+void AppDomain::SetFriendlyName(LPCWSTR pwzFriendlyName, BOOL fDebuggerCares/*=TRUE*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Do all computations into a temporary until we're ensured of success
+ SString tmpFriendlyName;
+
+
+ if (pwzFriendlyName)
+ tmpFriendlyName.Set(pwzFriendlyName);
+ else
+ {
+ // If there is an assembly, try to get the name from it.
+ // If no assembly, but if it's the DefaultDomain, then give it a name
+
+ if (m_pRootAssembly)
+ {
+ tmpFriendlyName.SetUTF8(m_pRootAssembly->GetSimpleName());
+
+ SString::Iterator i = tmpFriendlyName.End();
+ if (tmpFriendlyName.FindBack(i, '.'))
+ tmpFriendlyName.Truncate(i);
+ }
+ else
+ {
+ if (IsDefaultDomain())
+ tmpFriendlyName.Set(DEFAULT_DOMAIN_FRIENDLY_NAME);
+
+ // This is for the profiler - if they call GetFriendlyName on an AppdomainCreateStarted
+ // event, then we want to give them a temporary name they can use.
+ else if (GetId().m_dwId != 0)
+ {
+ tmpFriendlyName.Clear();
+ tmpFriendlyName.Printf(W("%s %d"), OTHER_DOMAIN_FRIENDLY_NAME_PREFIX, GetId().m_dwId);
+ }
+ }
+
+ }
+
+ tmpFriendlyName.Normalize();
+
+
+ m_friendlyName = tmpFriendlyName;
+ m_friendlyName.Normalize();
+
+ if(g_pDebugInterface)
+ {
+ // update the name in the IPC publishing block
+ if (SUCCEEDED(g_pDebugInterface->UpdateAppDomainEntryInIPC(this)))
+ {
+ // inform the attached debugger that the name of this appdomain has changed.
+ if (IsDebuggerAttached() && fDebuggerCares)
+ g_pDebugInterface->NameChangeEvent(this, NULL);
+ }
+ }
+}
+
+void AppDomain::ResetFriendlyName(BOOL fDebuggerCares/*=TRUE*/)
+{
+ WRAPPER_NO_CONTRACT;
+ SetFriendlyName(NULL, fDebuggerCares);
+}
+
+LPCWSTR AppDomain::GetFriendlyName(BOOL fDebuggerCares/*=TRUE*/)
+{
+ CONTRACT (LPCWSTR)
+ {
+ THROWS;
+ if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+#if _DEBUG
+ // Handle NULL this pointer - this happens sometimes when printing log messages
+ // but in general shouldn't occur in real code
+ if (this == NULL)
+ RETURN NULL;
+#endif // _DEBUG
+
+ if (m_friendlyName.IsEmpty())
+ SetFriendlyName(NULL, fDebuggerCares);
+
+ RETURN m_friendlyName;
+}
+
+LPCWSTR AppDomain::GetFriendlyNameForLogging()
+{
+ CONTRACT(LPWSTR)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL,NULL_OK));
+ }
+ CONTRACT_END;
+#if _DEBUG
+ // Handle NULL this pointer - this happens sometimes when printing log messages
+ // but in general shouldn't occur in real code
+ if (this == NULL)
+ RETURN NULL;
+#endif // _DEBUG
+ RETURN (m_friendlyName.IsEmpty() ?W(""):(LPCWSTR)m_friendlyName);
+}
+
+LPCWSTR AppDomain::GetFriendlyNameForDebugger()
+{
+ CONTRACT (LPCWSTR)
+ {
+ NOTHROW;
+ if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+
+ if (m_friendlyName.IsEmpty())
+ {
+ BOOL fSuccess = FALSE;
+
+ EX_TRY
+ {
+ SetFriendlyName(NULL);
+
+ fSuccess = TRUE;
+ }
+ EX_CATCH
+ {
+ // Gobble all exceptions.
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!fSuccess)
+ {
+ RETURN W("");
+ }
+ }
+
+ RETURN m_friendlyName;
+}
+
+
+#endif // !DACCESS_COMPILE
+
+#ifdef DACCESS_COMPILE
+
+PVOID AppDomain::GetFriendlyNameNoSet(bool* isUtf8)
+{
+ SUPPORTS_DAC;
+
+ if (!m_friendlyName.IsEmpty())
+ {
+ *isUtf8 = false;
+ return m_friendlyName.DacGetRawContent();
+ }
+ else if (m_pRootAssembly)
+ {
+ *isUtf8 = true;
+ return (PVOID)m_pRootAssembly->GetSimpleName();
+ }
+ else if (dac_cast<TADDR>(this) ==
+ dac_cast<TADDR>(SystemDomain::System()->DefaultDomain()))
+ {
+ *isUtf8 = false;
+ return (PVOID)DEFAULT_DOMAIN_FRIENDLY_NAME;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+#endif // DACCESS_COMPILE
+
+void AppDomain::CacheStringsForDAC()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ //
+ // If the application base, private bin paths, and configuration file are
+ // available, cache them so DAC can read them out of memory
+ //
+#ifdef FEATURE_FUSION
+ if (m_pFusionContext)
+ {
+ CQuickBytes qb;
+ LPWSTR ssz = (LPWSTR) qb.AllocThrows(MAX_URL_LENGTH * sizeof(WCHAR));
+
+ DWORD dwSize;
+
+ // application base
+ ssz[0] = '\0';
+ dwSize = MAX_URL_LENGTH * sizeof(WCHAR);
+ m_pFusionContext->Get(ACTAG_APP_BASE_URL, ssz, &dwSize, 0);
+ m_applicationBase.Set(ssz);
+
+ // private bin paths
+ ssz[0] = '\0';
+ dwSize = MAX_URL_LENGTH * sizeof(WCHAR);
+ m_pFusionContext->Get(ACTAG_APP_PRIVATE_BINPATH, ssz, &dwSize, 0);
+ m_privateBinPaths.Set(ssz);
+
+ // configuration file
+ ssz[0] = '\0';
+ dwSize = MAX_URL_LENGTH * sizeof(WCHAR);
+ m_pFusionContext->Get(ACTAG_APP_CONFIG_FILE, ssz, &dwSize, 0);
+ m_configFile.Set(ssz);
+ }
+#endif // FEATURE_FUSION
+}
+
+#ifndef DACCESS_COMPILE
+
+BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly *pFile, BOOL fAllowFailure)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pSpec));
+ // Hosted fusion binder makes an exception here, so we cannot assert.
+ //PRECONDITION(pSpec->CanUseWithBindingCache());
+ //PRECONDITION(pFile->CanUseWithBindingCache());
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ CrstHolder holder(&m_DomainCacheCrst);
+ // !!! suppress exceptions
+ if(!m_AssemblyCache.StoreFile(pSpec, pFile) && !fAllowFailure)
+ {
+ // TODO: Disabling the below assertion as currently we experience
+ // inconsistency on resolving the Microsoft.Office.Interop.MSProject.dll
+ // This causes below assertion to fire and crashes the VS. This issue
+ // is being tracked with Dev10 Bug 658555. Brought back it when this bug
+ // is fixed.
+ // _ASSERTE(FALSE);
+
+ EEFileLoadException::Throw(pSpec, FUSION_E_CACHEFILE_FAILED, NULL);
+ }
+
+ return TRUE;
+}
+
+BOOL AppDomain::AddAssemblyToCache(AssemblySpec* pSpec, DomainAssembly *pAssembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pSpec));
+ PRECONDITION(CheckPointer(pAssembly));
+ PRECONDITION(pSpec->CanUseWithBindingCache());
+ PRECONDITION(pAssembly->CanUseWithBindingCache());
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ CrstHolder holder(&m_DomainCacheCrst);
+ // !!! suppress exceptions
+ BOOL bRetVal = m_AssemblyCache.StoreAssembly(pSpec, pAssembly);
+#ifdef FEATURE_FUSION
+ // check for context propagation
+ if (bRetVal && pSpec->GetParentLoadContext() == LOADCTX_TYPE_LOADFROM && pAssembly->GetFile()->GetLoadContext() == LOADCTX_TYPE_DEFAULT)
+ {
+ // LoadFrom propagation occured, store it in a way reachable by Load() (the "post-policy" one)
+ AssemblySpec loadSpec;
+ loadSpec.CopyFrom(pSpec);
+ loadSpec.SetParentAssembly(NULL);
+ bRetVal = m_AssemblyCache.StoreAssembly(&loadSpec, pAssembly);
+ }
+#endif
+ return bRetVal;
+}
+
+BOOL AppDomain::AddExceptionToCache(AssemblySpec* pSpec, Exception *ex)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pSpec));
+ PRECONDITION(pSpec->CanUseWithBindingCache());
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (ex->IsTransient())
+ return TRUE;
+
+ CrstHolder holder(&m_DomainCacheCrst);
+ // !!! suppress exceptions
+ return m_AssemblyCache.StoreException(pSpec, ex);
+}
+
+void AppDomain::AddUnmanagedImageToCache(LPCWSTR libraryName, HMODULE hMod)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(libraryName));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+ if (libraryName)
+ {
+ AssemblySpec spec;
+ spec.SetCodeBase(libraryName);
+ m_UnmanagedCache.InsertEntry(&spec, hMod);
+ }
+ return ;
+}
+
+
+HMODULE AppDomain::FindUnmanagedImageInCache(LPCWSTR libraryName)
+{
+ CONTRACT(HMODULE)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(libraryName,NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL,NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+ if(libraryName == NULL) RETURN NULL;
+
+ AssemblySpec spec;
+ spec.SetCodeBase(libraryName);
+ RETURN (HMODULE) m_UnmanagedCache.LookupEntry(&spec, 0);
+}
+
+
+BOOL AppDomain::IsCached(AssemblySpec *pSpec)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Check to see if this fits our rather loose idea of a reference to mscorlib.
+ // If so, don't use fusion to bind it - do it ourselves.
+ if (pSpec->IsMscorlib())
+ return TRUE;
+
+ return m_AssemblyCache.Contains(pSpec);
+}
+
+
+PEAssembly* AppDomain::FindCachedFile(AssemblySpec* pSpec, BOOL fThrow /*=TRUE*/)
+{
+ CONTRACTL
+ {
+ if (fThrow) {
+ GC_TRIGGERS;
+ THROWS;
+ }
+ else {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Check to see if this fits our rather loose idea of a reference to mscorlib.
+ // If so, don't use fusion to bind it - do it ourselves.
+ if (fThrow && pSpec->IsMscorlib())
+ {
+ CONSISTENCY_CHECK(SystemDomain::System()->SystemAssembly() != NULL);
+ PEAssembly *pFile = SystemDomain::System()->SystemFile();
+ pFile->AddRef();
+ return pFile;
+ }
+
+ return m_AssemblyCache.LookupFile(pSpec, fThrow);
+}
+
+
+BOOL AppDomain::PostBindResolveAssembly(AssemblySpec *pPrePolicySpec,
+ AssemblySpec *pPostPolicySpec,
+ HRESULT hrBindResult,
+ AssemblySpec **ppFailedSpec)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pPrePolicySpec));
+ PRECONDITION(CheckPointer(pPostPolicySpec));
+ PRECONDITION(CheckPointer(ppFailedSpec));
+
+ BOOL fFailure = TRUE;
+ *ppFailedSpec = pPrePolicySpec;
+
+#ifdef FEATURE_FUSION
+ // Fusion policy could have been applied,
+ // so failed assembly could be not exactly what we ordered
+
+ IAssemblyName *pIPostPolicyName = pPrePolicySpec->GetNameAfterPolicy();
+
+ // Get post-policy assembly name
+ if (pIPostPolicyName != NULL)
+ {
+ pPostPolicySpec->InitializeSpec(pIPostPolicyName,
+ NULL,
+ pPrePolicySpec->IsIntrospectionOnly());
+ pPrePolicySpec->ReleaseNameAfterPolicy();
+
+ if (!pPostPolicySpec->CompareEx(pPrePolicySpec))
+ {
+ *ppFailedSpec = pPostPolicySpec;
+ }
+ }
+#endif //FEATURE_FUSION
+
+ PEAssemblyHolder result;
+
+ if ((EEFileLoadException::GetFileLoadKind(hrBindResult) == kFileNotFoundException) ||
+ (hrBindResult == FUSION_E_REF_DEF_MISMATCH) ||
+ (hrBindResult == FUSION_E_INVALID_NAME))
+ {
+ result = TryResolveAssembly(*ppFailedSpec, FALSE /* fPreBind */);
+
+ if (result != NULL && pPrePolicySpec->CanUseWithBindingCache() && result->CanUseWithBindingCache())
+ {
+ fFailure = FALSE;
+
+ // Given the post-policy resolve event construction of the CLR binder,
+ // chained managed resolve events can race with each other, therefore we do allow
+ // the adding of the result to fail. Checking for already chached specs
+ // is not an option as it would introduce another race window.
+ // The binder does a re-fetch of the
+ // orignal binding spec and therefore will not cause inconsistency here.
+ // For the purposes of the resolve event, failure to add to the cache still is a success.
+ AddFileToCache(pPrePolicySpec, result, TRUE /* fAllowFailure */);
+ if (*ppFailedSpec != pPrePolicySpec && pPostPolicySpec->CanUseWithBindingCache())
+ {
+ AddFileToCache(pPostPolicySpec, result, TRUE /* fAllowFailure */ );
+ }
+ }
+ }
+
+ return fFailure;
+}
+
+#ifdef FEATURE_HOSTED_BINDER
+//----------------------------------------------------------------------------------------
+// Helper class for hosted binder
+
+class PEAssemblyAsPrivAssemblyInfo : public IUnknownCommon<ICLRPrivAssemblyInfo>
+{
+public:
+ //------------------------------------------------------------------------------------
+ // Ctor
+
+ PEAssemblyAsPrivAssemblyInfo(PEAssembly *pPEAssembly)
+ {
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_THROWS;
+
+ if (pPEAssembly == nullptr)
+ ThrowHR(E_UNEXPECTED);
+
+ pPEAssembly->AddRef();
+ m_pPEAssembly = pPEAssembly;
+ }
+
+ //------------------------------------------------------------------------------------
+ // ICLRPrivAssemblyInfo methods
+
+ //------------------------------------------------------------------------------------
+ STDMETHOD(GetAssemblyName)(
+ __in DWORD cchBuffer,
+ __out_opt LPDWORD pcchBuffer,
+ __out_ecount_part_opt(cchBuffer, *pcchBuffer) LPWSTR wzBuffer)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ if ((cchBuffer == 0) != (wzBuffer == nullptr))
+ {
+ return E_INVALIDARG;
+ }
+
+ LPCUTF8 szName = m_pPEAssembly->GetSimpleName();
+
+ bool bIsAscii;
+ DWORD cchName;
+ IfFailRet(FString::Utf8_Unicode_Length(szName, &bIsAscii, &cchName));
+
+ if (cchBuffer < cchName + 1)
+ {
+ if (pcchBuffer != nullptr)
+ {
+ *pcchBuffer = cchName + 1;
+ }
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+ else
+ {
+ IfFailRet(FString::Utf8_Unicode(szName, bIsAscii, wzBuffer, *pcchBuffer));
+ *pcchBuffer = cchName;
+ return S_OK;
+ }
+ }
+
+ //------------------------------------------------------------------------------------
+ STDMETHOD(GetAssemblyVersion)(
+ USHORT *pMajor,
+ USHORT *pMinor,
+ USHORT *pBuild,
+ USHORT *pRevision)
+ {
+ WRAPPER_NO_CONTRACT;
+ return m_pPEAssembly->GetVersion(pMajor, pMinor, pBuild, pRevision);
+ }
+
+ //------------------------------------------------------------------------------------
+ STDMETHOD(GetAssemblyPublicKey)(
+ DWORD cbBuffer,
+ LPDWORD pcbBuffer,
+ BYTE *pbBuffer)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ STATIC_CONTRACT_CAN_TAKE_LOCK;
+
+ VALIDATE_PTR_RET(pcbBuffer);
+ VALIDATE_CONDITION((pbBuffer == nullptr) == (cbBuffer == 0), return E_INVALIDARG);
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ // Note: PEAssembly::GetPublicKey will return bogus data pointer when *pcbBuffer == 0
+ LPCVOID pbKey = m_pPEAssembly->GetPublicKey(pcbBuffer);
+
+ if (*pcbBuffer != 0)
+ {
+ if (pbBuffer != nullptr && cbBuffer >= *pcbBuffer)
+ {
+ memcpy(pbBuffer, pbKey, *pcbBuffer);
+ hr = S_OK;
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+ }
+ else
+ {
+ hr = S_FALSE; // ==> No public key
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+ }
+
+private:
+ ReleaseHolder<PEAssembly> m_pPEAssembly;
+};
+
+//-----------------------------------------------------------------------------------------------------------------
+static HRESULT VerifyBindHelper(
+ ICLRPrivAssembly *pPrivAssembly,
+ IAssemblyName *pAssemblyName,
+ PEAssembly *pPEAssembly)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ HRESULT hr = S_OK;
+ // Create an ICLRPrivAssemblyInfo to call to ICLRPrivAssembly::VerifyBind
+ NewHolder<PEAssemblyAsPrivAssemblyInfo> pPrivAssemblyInfoImpl = new PEAssemblyAsPrivAssemblyInfo(pPEAssembly);
+ ReleaseHolder<ICLRPrivAssemblyInfo> pPrivAssemblyInfo;
+ IfFailRet(pPrivAssemblyInfoImpl->QueryInterface(__uuidof(ICLRPrivAssemblyInfo), (LPVOID *)&pPrivAssemblyInfo));
+ pPrivAssemblyInfoImpl.SuppressRelease();
+
+ // Call VerifyBind to give the host a chance to reject the bind based on assembly image contents.
+ IfFailRet(pPrivAssembly->VerifyBind(pAssemblyName, pPrivAssembly, pPrivAssemblyInfo));
+
+ return hr;
+}
+
+//-----------------------------------------------------------------------------------------------------------------
+HRESULT AppDomain::BindAssemblySpecForHostedBinder(
+ AssemblySpec * pSpec,
+ IAssemblyName * pAssemblyName,
+ ICLRPrivBinder * pBinder,
+ PEAssembly ** ppAssembly)
+{
+ STANDARD_VM_CONTRACT;
+
+ PRECONDITION(CheckPointer(pSpec));
+ PRECONDITION(pSpec->GetAppDomain() == this);
+ PRECONDITION(CheckPointer(ppAssembly));
+ PRECONDITION(pSpec->GetCodeBase() == nullptr);
+
+ HRESULT hr = S_OK;
+
+#ifdef FEATURE_FUSION
+ StackSString wszAssemblyName;
+
+ if (fusion::logging::LoggingEnabled())
+ { // Don't perform computation if logging is not enabled.
+ FusionBind::GetAssemblyNameDisplayName(pAssemblyName, wszAssemblyName, ASM_DISPLAYF_FULL);
+ }
+
+ // Fire ETW Start event.
+ FireEtwBindingPhaseStart(
+ GetId().m_dwId, LOADCTX_TYPE_HOSTED, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable,
+ pSpec->m_wszCodeBase, wszAssemblyName.GetUnicode(), GetClrInstanceId());
+#endif
+
+ // The Fusion binder can throw (to preserve compat, since it will actually perform an assembly
+ // load as part of it's bind), so we need to be careful here to catch any FileNotFoundException
+ // objects if fThrowIfNotFound is false.
+ ReleaseHolder<ICLRPrivAssembly> pPrivAssembly;
+
+ // We return HRESULTs here on failure instead of throwing as failures here are not necessarily indicative
+ // of an actual application problem. Returning an error code is substantially faster than throwing, and
+ // should be used when possible.
+ IfFailRet(pBinder->BindAssemblyByName(pAssemblyName, &pPrivAssembly));
+
+ IfFailRet(BindHostedPrivAssembly(nullptr, pPrivAssembly, pAssemblyName, ppAssembly));
+
+#ifdef FEATURE_FUSION
+ // Fire ETW End event.
+ FireEtwBindingPhaseEnd(
+ GetId().m_dwId, LOADCTX_TYPE_HOSTED, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable,
+ pSpec->m_wszCodeBase, wszAssemblyName.GetUnicode(), GetClrInstanceId());
+
+ #endif
+
+ return S_OK;
+}
+
+//-----------------------------------------------------------------------------------------------------------------
+HRESULT
+AppDomain::BindHostedPrivAssembly(
+ PEAssembly * pParentAssembly,
+ ICLRPrivAssembly * pPrivAssembly,
+ IAssemblyName * pAssemblyName,
+ PEAssembly ** ppAssembly,
+ BOOL fIsIntrospectionOnly) // = FALSE
+{
+ STANDARD_VM_CONTRACT;
+
+ PRECONDITION(CheckPointer(pPrivAssembly));
+ PRECONDITION(CheckPointer(ppAssembly));
+
+ HRESULT hr = S_OK;
+
+ *ppAssembly = nullptr;
+
+ // See if result has been previously loaded.
+ {
+ DomainAssembly* pDomainAssembly = FindAssembly(pPrivAssembly);
+ if (pDomainAssembly != nullptr)
+ {
+ *ppAssembly = clr::SafeAddRef(pDomainAssembly->GetFile());
+ }
+ }
+
+ if (*ppAssembly != nullptr)
+ { // Already exists: ask the binder to verify and return the assembly.
+ return VerifyBindHelper(pPrivAssembly, pAssemblyName, *ppAssembly);
+ }
+
+ // Get the IL PEFile.
+ PEImageHolder pPEImageIL;
+ {
+ // Does not already exist, so get the resource for the assembly and load it.
+ DWORD dwImageType;
+ ReleaseHolder<ICLRPrivResource> pIResourceIL;
+
+ IfFailRet(pPrivAssembly->GetImageResource(ASSEMBLY_IMAGE_TYPE_IL, &dwImageType, &pIResourceIL));
+ _ASSERTE(dwImageType == ASSEMBLY_IMAGE_TYPE_IL);
+
+ pPEImageIL = PEImage::OpenImage(pIResourceIL, MDInternalImport_Default);
+ }
+
+ // See if an NI is available.
+ DWORD dwAvailableImages;
+ IfFailRet(pPrivAssembly->GetAvailableImageTypes(&dwAvailableImages));
+ _ASSERTE(dwAvailableImages & ASSEMBLY_IMAGE_TYPE_IL); // Just double checking that IL bit is always set.
+
+ // Get the NI PEFile if available.
+ PEImageHolder pPEImageNI;
+ if (dwAvailableImages & ASSEMBLY_IMAGE_TYPE_NATIVE)
+ {
+ DWORD dwImageType;
+ ReleaseHolder<ICLRPrivResource> pIResourceNI;
+
+ IfFailRet(pPrivAssembly->GetImageResource(ASSEMBLY_IMAGE_TYPE_NATIVE, &dwImageType, &pIResourceNI));
+ _ASSERTE(dwImageType == ASSEMBLY_IMAGE_TYPE_NATIVE || FAILED(hr));
+
+ pPEImageNI = PEImage::OpenImage(pIResourceNI, MDInternalImport_TrustedNativeImage);
+ }
+ _ASSERTE(pPEImageIL != nullptr);
+
+ // Create a PEAssembly using the IL and NI images.
+ PEAssemblyHolder pPEAssembly = PEAssembly::Open(pParentAssembly, pPEImageIL, pPEImageNI, pPrivAssembly, fIsIntrospectionOnly);
+
+#ifdef FEATURE_FUSION
+ // Ensure that the assembly found can be loaded for execution in the process.
+ if (!fIsIntrospectionOnly)
+ IfFailRet(RuntimeIsValidAssemblyOnThisPlatform_CheckProcessorArchitecture(pPEAssembly->GetFusionProcessorArchitecture(), FALSE));
+#endif
+
+ // Ask the binder to verify.
+ IfFailRet(VerifyBindHelper(pPrivAssembly, pAssemblyName, pPEAssembly));
+
+ // The result.
+ *ppAssembly = pPEAssembly.Extract();
+
+ return S_OK;
+} // AppDomain::BindHostedPrivAssembly
+#endif // FEATURE_HOSTED_BINDER
+
+//---------------------------------------------------------------------------------------------------------------------
+PEAssembly * AppDomain::BindAssemblySpec(
+ AssemblySpec * pSpec,
+ BOOL fThrowOnFileNotFound,
+ BOOL fRaisePrebindEvents,
+ StackCrawlMark * pCallerStackMark,
+ AssemblyLoadSecurity * pLoadSecurity,
+ BOOL fUseHostBinderIfAvailable)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ PRECONDITION(CheckPointer(pSpec));
+ PRECONDITION(pSpec->GetAppDomain() == this);
+ PRECONDITION(this==::GetAppDomain());
+
+ GCX_PREEMP();
+
+ BOOL fForceReThrow = FALSE;
+
+#if defined(FEATURE_HOSTED_BINDER) && defined(FEATURE_APPX_BINDER)
+ //
+ // If there is a host binder available and this is an unparented bind within the
+ // default load context, then the bind will be delegated to the domain-wide host
+ // binder. If there is a parent assembly, then a bind will occur only if it has
+ // an associated ICLRPrivAssembly to serve as the binder.
+ //
+ // fUseHostBinderIfAvailable can be false if this method is called by
+ // CLRPrivBinderFusion::BindAssemblyByName, which explicitly indicates that it
+ // wants to use the fusion binder.
+ //
+
+ if (AppX::IsAppXProcess() &&
+ fUseHostBinderIfAvailable &&
+ (
+ ( pSpec->HasParentAssembly()
+ ? // Parent assembly is hosted
+ pSpec->GetParentAssembly()->GetFile()->HasHostAssembly()
+ : // Non-parented default context bind
+ ( HasLoadContextHostBinder() &&
+ !pSpec->IsIntrospectionOnly()
+ )
+ ) ||
+ (pSpec->GetHostBinder() != nullptr)
+ )
+ )
+ {
+ HRESULT hr = S_OK;
+
+ if (pSpec->GetCodeBase() != nullptr)
+ { // LoadFrom is not supported in AppX (we should never even get here)
+ IfFailThrow(E_INVALIDARG);
+ }
+
+ // Get the assembly display name.
+ ReleaseHolder<IAssemblyName> pAssemblyName;
+ IfFailThrow(pSpec->CreateFusionName(&pAssemblyName, TRUE, TRUE));
+
+ // Create new binding scope for fusion logging.
+ fusion::logging::BindingScope defaultScope(pAssemblyName, FUSION_BIND_LOG_CATEGORY_DEFAULT);
+
+ PEAssemblyHolder pAssembly;
+ EX_TRY
+ {
+ // If there is a specified binder, then it is used.
+ // Otherwise if there exist a parent assembly, then it provides the binding context
+ // Otherwise the domain's root-level binder is used.
+ ICLRPrivBinder * pBinder = nullptr;
+
+ if (pSpec->GetHostBinder() != nullptr)
+ {
+ pBinder = pSpec->GetHostBinder();
+ }
+ else
+ {
+ PEAssembly * pParentAssembly =
+ (pSpec->GetParentAssembly() == nullptr) ? nullptr : pSpec->GetParentAssembly()->GetFile();
+
+ if ((pParentAssembly != nullptr) && (pParentAssembly->HasHostAssembly()))
+ {
+ BOOL fMustUseOriginalLoadContextBinder = FALSE;
+ if (pSpec->IsContentType_WindowsRuntime())
+ {
+ // Ugly, but we need to handle Framework assemblies that contain WinRT type references,
+ // and the Fusion binder won't resolve these in AppX processes. The shareable flag is currently
+ // a reasonable proxy for these cases. (It also catches first party WinMD files, but depedencies
+ // from those can also be resolved by the original load context binder).
+ // TODO! Update the fusion binder to resolve WinMD references correctly.
+ IfFailThrow(pParentAssembly->GetHostAssembly()->IsShareable(&fMustUseOriginalLoadContextBinder));
+ }
+
+ if (fMustUseOriginalLoadContextBinder)
+ {
+ pBinder = GetLoadContextHostBinder();
+ }
+ else
+ {
+ pBinder = pParentAssembly->GetHostAssembly();
+ }
+ }
+ else
+ {
+ pBinder = GetCurrentLoadContextHostBinder();
+ }
+ }
+ _ASSERTE(pBinder != nullptr);
+
+ hr = BindAssemblySpecForHostedBinder(pSpec, pAssemblyName, pBinder, &pAssembly);
+ if (FAILED(hr))
+ {
+ goto EndTry1;
+ }
+EndTry1:;
+ }
+ // The combination of this conditional catch/ the following if statement which will throw reduces the count of exceptions
+ // thrown in scenarios where the exception does not escape the method. We cannot get rid of the try/catch block, as
+ // there are cases within some of the clrpriv binder's which throw.
+ // Note: In theory, FileNotFound should always come here as HRESULT, never as exception.
+ EX_CATCH_HRESULT_IF(hr,
+ !fThrowOnFileNotFound && Assembly::FileNotFound(hr))
+
+ if (FAILED(hr) && (fThrowOnFileNotFound || !Assembly::FileNotFound(hr)))
+ {
+ if (Assembly::FileNotFound(hr))
+ {
+ _ASSERTE(fThrowOnFileNotFound);
+ // Uses defaultScope
+ EEFileLoadException::Throw(pSpec, fusion::logging::GetCurrentFusionBindLog(), hr);
+ }
+ if ((hr == CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT) && pSpec->IsContentType_WindowsRuntime())
+ { // Error returned e.g. for WinRT type name without namespace
+ if (fThrowOnFileNotFound)
+ { // Throw ArgumentException (with the HRESULT) wrapped by TypeLoadException to give user type name for diagnostics
+ // Note: TypeLoadException is equivalent of FileNotFound in WinRT world
+ EEMessageException ex(hr);
+ EX_THROW_WITH_INNER(EETypeLoadException, (pSpec->GetWinRtTypeNamespace(), pSpec->GetWinRtTypeClassName(), nullptr, nullptr, IDS_EE_WINRT_LOADFAILURE), &ex);
+ }
+ }
+ else
+ {
+ IfFailThrow(hr);
+ }
+ }
+
+ _ASSERTE((pAssembly != nullptr) || (FAILED(hr) && !fThrowOnFileNotFound));
+ return pAssembly.Extract();
+ }
+ else
+#endif //FEATURE_HOSTED_BINDER && FEATURE_APPX_BINDER
+#if defined(FEATURE_HOSTED_BINDER) && defined(FEATURE_COMINTEROP)
+ // Handle WinRT assemblies in the classic/hybrid scenario. If this is an AppX process,
+ // then this case will be handled by the previous block as part of the full set of
+ // available binding hosts.
+#ifndef FEATURE_APPX_BINDER
+ if (pSpec->IsContentType_WindowsRuntime())
+#else
+ if (!AppX::IsAppXProcess() && pSpec->IsContentType_WindowsRuntime())
+#endif
+ {
+ HRESULT hr = S_OK;
+
+ // Get the assembly display name.
+ ReleaseHolder<IAssemblyName> pAssemblyName;
+
+ IfFailThrow(pSpec->CreateFusionName(&pAssemblyName, TRUE, TRUE));
+
+#ifdef FEATURE_FUSION
+ // Create new binding scope for fusion logging.
+ fusion::logging::BindingScope defaultScope(pAssemblyName, FUSION_BIND_LOG_CATEGORY_DEFAULT);
+#endif
+
+ PEAssemblyHolder pAssembly;
+
+ EX_TRY
+ {
+ hr = BindAssemblySpecForHostedBinder(pSpec, pAssemblyName, m_pWinRtBinder, &pAssembly);
+ if (FAILED(hr))
+ goto EndTry2; // Goto end of try block.
+EndTry2:;
+ }
+ // The combination of this conditional catch/ the following if statement which will throw reduces the count of exceptions
+ // thrown in scenarios where the exception does not escape the method. We cannot get rid of the try/catch block, as
+ // there are cases within some of the clrpriv binder's which throw.
+ // Note: In theory, FileNotFound should always come here as HRESULT, never as exception.
+ EX_CATCH_HRESULT_IF(hr,
+ !fThrowOnFileNotFound && Assembly::FileNotFound(hr))
+
+ if (FAILED(hr) && (fThrowOnFileNotFound || !Assembly::FileNotFound(hr)))
+ {
+ if (Assembly::FileNotFound(hr))
+ {
+ _ASSERTE(fThrowOnFileNotFound);
+ // Uses defaultScope
+#ifdef FEATURE_FUSION
+ EEFileLoadException::Throw(pSpec, fusion::logging::GetCurrentFusionBindLog(), hr);
+#else
+ EEFileLoadException::Throw(pSpec, hr);
+#endif // FEATURE_FUSION
+ }
+
+ // WinRT type bind failures
+ _ASSERTE(pSpec->IsContentType_WindowsRuntime());
+ if (hr == HRESULT_FROM_WIN32(APPMODEL_ERROR_NO_PACKAGE)) // Returned by RoResolveNamespace when using 3rd party WinRT types in classic process
+ {
+ if (fThrowOnFileNotFound)
+ { // Throw NotSupportedException (with custom message) wrapped by TypeLoadException to give user type name for diagnostics
+ // Note: TypeLoadException is equivalent of FileNotFound in WinRT world
+ EEMessageException ex(kNotSupportedException, IDS_EE_WINRT_THIRDPARTY_NOTSUPPORTED);
+ EX_THROW_WITH_INNER(EETypeLoadException, (pSpec->GetWinRtTypeNamespace(), pSpec->GetWinRtTypeClassName(), nullptr, nullptr, IDS_EE_WINRT_LOADFAILURE), &ex);
+ }
+ }
+ else if ((hr == CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT) || // Returned e.g. for WinRT type name without namespace
+ (hr == COR_E_PLATFORMNOTSUPPORTED)) // Using WinRT on pre-Win8 OS
+ {
+ if (fThrowOnFileNotFound)
+ { // Throw ArgumentException/PlatformNotSupportedException wrapped by TypeLoadException to give user type name for diagnostics
+ // Note: TypeLoadException is equivalent of FileNotFound in WinRT world
+ EEMessageException ex(hr);
+ EX_THROW_WITH_INNER(EETypeLoadException, (pSpec->GetWinRtTypeNamespace(), pSpec->GetWinRtTypeClassName(), nullptr, nullptr, IDS_EE_WINRT_LOADFAILURE), &ex);
+ }
+ }
+ else
+ {
+ IfFailThrow(hr);
+ }
+ }
+ _ASSERTE((FAILED(hr) && !fThrowOnFileNotFound) || pAssembly != nullptr);
+
+ return pAssembly.Extract();
+ }
+ else
+#endif // FEATURE_HOSTED_BINDER && FEATURE_COMINTEROP
+ if (pSpec->HasUniqueIdentity())
+ {
+ HRESULT hrBindResult = S_OK;
+ PEAssemblyHolder result;
+
+#if defined(FEATURE_COMINTEROP) && defined(FEATURE_REFLECTION_ONLY_LOAD)
+ // We want to keep this holder around to avoid closing and remapping the file again - calls to Fusion further down will open the file again
+ ReleaseHolder<IMetaDataAssemblyImport> pMetaDataAssemblyImport;
+
+ // Special case ReflectionOnlyLoadFrom on .winmd (WinRT) assemblies
+ if (pSpec->IsIntrospectionOnly() && (pSpec->m_wszCodeBase != NULL))
+ { // This is a LoadFrom request - we need to find out if it is .winmd file or classic managed assembly
+ HRESULT hr = S_OK;
+
+ StackSString sPath(pSpec->GetCodeBase());
+ PEAssembly::UrlToPath(sPath);
+
+ // Open MetaData of the file
+ hr = GetAssemblyMDInternalImportEx(
+ sPath,
+ IID_IMetaDataAssemblyImport,
+ MDInternalImport_Default,
+ (IUnknown **)&pMetaDataAssemblyImport);
+ if (SUCCEEDED(hr))
+ {
+ DWORD dwAssemblyFlags = 0;
+ hr = pMetaDataAssemblyImport->GetAssemblyProps(
+ TokenFromRid(1, mdtAssembly),
+ nullptr, // ppbPublicKey
+ nullptr, // pcbPublicKey
+ nullptr, // pulHashAlgId
+ nullptr, // szName
+ 0, // cchName
+ nullptr, // pchName
+ nullptr, // pMetaData
+ &dwAssemblyFlags);
+ if (SUCCEEDED(hr) && IsAfContentType_WindowsRuntime(dwAssemblyFlags))
+ { // It is .winmd file
+ _ASSERTE(!AppX::IsAppXProcess());
+
+ ReleaseHolder<ICLRPrivAssembly> pPrivAssembly;
+ ReleaseHolder<PEAssembly> pAssembly;
+
+ hr = m_pReflectionOnlyWinRtBinder->BindAssemblyExplicit(sPath, &pPrivAssembly);
+ if (SUCCEEDED(hr))
+ {
+ hr = BindHostedPrivAssembly(nullptr, pPrivAssembly, nullptr, &pAssembly, TRUE);
+ _ASSERTE(FAILED(hr) || (pAssembly != nullptr));
+ }
+ if (FAILED(hr))
+ {
+ if (fThrowOnFileNotFound)
+ {
+ ThrowHR(hr);
+ }
+ return nullptr;
+ }
+ return pAssembly.Extract();
+ }
+ }
+ }
+#endif //FEATURE_COMINTEROP && FEATURE_REFLECTION_ONLY_LOAD
+
+ EX_TRY
+ {
+ if (!IsCached(pSpec))
+ {
+
+#ifdef FEATURE_FUSION
+ if (fRaisePrebindEvents
+ && (result = TryResolveAssembly(pSpec, TRUE /*fPreBind*/)) != NULL
+ && result->CanUseWithBindingCache())
+ {
+ // Failure to add simply means someone else beat us to it. In that case
+ // the FindCachedFile call below (after catch block) will update result
+ // to the cached value.
+ AddFileToCache(pSpec, result, TRUE /*fAllowFailure*/);
+ }
+ else
+#endif
+ {
+ bool fAddFileToCache = false;
+
+ BOOL fIsWellKnown = FALSE;
+
+#ifdef FEATURE_FUSION
+ SafeComHolderPreemp<IAssembly> pIAssembly;
+ SafeComHolderPreemp<IBindResult> pNativeFusionAssembly;
+ SafeComHolderPreemp<IHostAssembly> pIHostAssembly;
+ SafeComHolderPreemp<IFusionBindLog> pFusionLog;
+
+ // Event Tracing for Windows is used to log data for performance and functional testing purposes.
+ // The events below are used to measure the performance of assembly binding as a whole.
+ FireEtwBindingPhaseStart(GetId().m_dwId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable, pSpec->m_wszCodeBase, NULL, GetClrInstanceId());
+ fIsWellKnown = pSpec->FindAssemblyFile(this,
+ fThrowOnFileNotFound,
+ &pIAssembly,
+ &pIHostAssembly,
+ &pNativeFusionAssembly,
+ &pFusionLog,
+ &hrBindResult,
+ pCallerStackMark,
+ pLoadSecurity);
+ FireEtwBindingPhaseEnd(GetId().m_dwId, ETWLoadContextNotAvailable, ETWFieldUnused, ETWLoaderLoadTypeNotAvailable, pSpec->m_wszCodeBase, NULL, GetClrInstanceId());
+ if (pIAssembly || pIHostAssembly)
+ {
+
+ if (fIsWellKnown &&
+ m_pRootAssembly &&
+ pIAssembly == m_pRootAssembly->GetFusionAssembly())
+ {
+ // This is a shortcut to avoid opening another copy of the process exe.
+ // In fact, we have other similar cases where we've called
+ // ExplicitBind() rather than normal binding, which aren't covered here.
+
+ // <TODO>@todo: It would be nice to populate the cache with those assemblies
+ // to avoid getting in this situation.</TODO>
+
+ result = m_pRootAssembly->GetManifestFile();
+ result.SuppressRelease(); // Didn't get a refcount
+ }
+ else
+ {
+ BOOL isSystemAssembly = pSpec->IsMscorlib(); // can use SystemDomain::m_pSystemAssembly
+ BOOL isIntrospectionOnly = pSpec->IsIntrospectionOnly();
+ if (pIAssembly)
+ result = PEAssembly::Open(pIAssembly, pNativeFusionAssembly, pFusionLog,
+ isSystemAssembly, isIntrospectionOnly);
+ else
+ result = PEAssembly::Open(pIHostAssembly, isSystemAssembly,
+ isIntrospectionOnly);
+ }
+ fAddFileToCache = true;
+ }
+ else if (!fIsWellKnown)
+ {
+ // Trigger the resolve event also for non-throw situation.
+ // However, this code path will behave as if the resolve handler has thrown,
+ // that is, not trigger an MDA.
+ _ASSERTE(fThrowOnFileNotFound == FALSE);
+
+ AssemblySpec NewSpec(this);
+ AssemblySpec *pFailedSpec = NULL;
+
+ fForceReThrow = TRUE; // Managed resolve event handler can throw
+
+ // Purposly ignore return value
+ PostBindResolveAssembly(pSpec, &NewSpec, hrBindResult, &pFailedSpec);
+ }
+#else //!FEATURE_FUSION
+ // Use CoreClr's fusion alternative
+ CoreBindResult bindResult;
+
+ pSpec->Bind(this, fThrowOnFileNotFound, &bindResult, FALSE /* fNgenExplicitBind */, FALSE /* fExplicitBindToNativeImage */, pCallerStackMark);
+ hrBindResult = bindResult.GetHRBindResult();
+
+ if (bindResult.Found())
+ {
+ if (SystemDomain::SystemFile() && bindResult.IsMscorlib())
+ {
+ // Avoid rebinding to another copy of mscorlib
+ result = SystemDomain::SystemFile();
+ result.SuppressRelease(); // Didn't get a refcount
+ }
+ else
+ {
+ // IsSystem on the PEFile should be false, even for mscorlib satellites
+ result = PEAssembly::Open(&bindResult,
+ FALSE, pSpec->IsIntrospectionOnly());
+ }
+ fAddFileToCache = true;
+
+#if defined(FEATURE_CORECLR)
+ // Setup the reference to the binder, which performed the bind, into the AssemblySpec
+ ICLRPrivBinder* pBinder = result->GetBindingContext();
+ _ASSERTE(pBinder != NULL);
+ pSpec->SetBindingContext(pBinder);
+#endif // defined(FEATURE_CORECLR)
+ }
+
+#endif //!FEATURE_FUSION
+
+ if (fAddFileToCache)
+ {
+
+#ifdef FEATURE_REFLECTION_ONLY_LOAD
+ // <TODO> PERF: This doesn't scale... </TODO>
+ if (pSpec->IsIntrospectionOnly() && (pSpec->GetCodeBase() != NULL))
+ {
+ IAssemblyName * pIAssemblyName = result->GetFusionAssemblyName();
+
+ AppDomain::AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded | kIncludeIntrospection));
+ CollectibleAssemblyHolder<DomainAssembly *> pCachedDomainAssembly;
+ while (i.Next(pCachedDomainAssembly.This()))
+ {
+ IAssemblyName * pCachedAssemblyName = pCachedDomainAssembly->GetAssembly()->GetFusionAssemblyName();
+ if (pCachedAssemblyName->IsEqual(pIAssemblyName, ASM_CMPF_IL_ALL) == S_OK)
+ {
+ if (!pCachedDomainAssembly->GetAssembly()->GetManifestModule()->GetFile()->Equals(result))
+ {
+ COMPlusThrow(kFileLoadException, IDS_EE_REFLECTIONONLY_LOADFROM, pSpec->GetCodeBase());
+ }
+ }
+ }
+ }
+#endif //FEATURE_REFLECTION_ONLY_LOAD
+
+ if (pSpec->CanUseWithBindingCache() && result->CanUseWithBindingCache())
+ {
+ // Failure to add simply means someone else beat us to it. In that case
+ // the FindCachedFile call below (after catch block) will update result
+ // to the cached value.
+ AddFileToCache(pSpec, result, TRUE /*fAllowFailure*/);
+ }
+ }
+ else if (!fIsWellKnown)
+ {
+ // Trigger the resolve event also for non-throw situation.
+ // However, this code path will behave as if the resolve handler has thrown,
+ // that is, not trigger an MDA.
+ _ASSERTE(fThrowOnFileNotFound == FALSE);
+
+ AssemblySpec NewSpec(this);
+ AssemblySpec *pFailedSpec = NULL;
+
+ fForceReThrow = TRUE; // Managed resolve event handler can throw
+
+ // Purposly ignore return value
+ PostBindResolveAssembly(pSpec, &NewSpec, hrBindResult, &pFailedSpec);
+ }
+ }
+ }
+ }
+ EX_CATCH
+ {
+ Exception *ex = GET_EXCEPTION();
+
+ AssemblySpec NewSpec(this);
+ AssemblySpec *pFailedSpec = NULL;
+
+ // Let transient exceptions or managed resolve event handler exceptions propagate
+ if (ex->IsTransient() || fForceReThrow)
+ {
+ EX_RETHROW;
+ }
+
+ {
+ // This is not executed for SO exceptions so we need to disable the backout
+ // stack validation to prevent false violations from being reported.
+ DISABLE_BACKOUT_STACK_VALIDATION;
+
+ BOOL fFailure = PostBindResolveAssembly(pSpec, &NewSpec, ex->GetHR(), &pFailedSpec);
+ if (fFailure)
+ {
+ BOOL bFileNotFoundException =
+ (EEFileLoadException::GetFileLoadKind(ex->GetHR()) == kFileNotFoundException);
+
+ if (!bFileNotFoundException)
+ {
+ fFailure = AddExceptionToCache(pFailedSpec, ex);
+ } // else, fFailure stays TRUE
+ // Effectively, fFailure == bFileNotFoundException || AddExceptionToCache(pFailedSpec, ex)
+
+ // Only throw this exception if we are the first in the cache
+ if (fFailure)
+ {
+ //
+ // If the BindingFailure MDA is enabled, trigger one for this failure
+ // Note: TryResolveAssembly() can also throw if an AssemblyResolve event subscriber throws
+ // and the MDA isn't sent in this case (or for transient failure cases)
+ //
+#ifdef MDA_SUPPORTED
+ MdaBindingFailure* pProbe = MDA_GET_ASSISTANT(BindingFailure);
+ if (pProbe)
+ {
+ // Transition to cooperative GC mode before using any OBJECTREFs.
+ GCX_COOP();
+
+ OBJECTREF exceptionObj = GET_THROWABLE();
+ GCPROTECT_BEGIN(exceptionObj)
+ {
+ pProbe->BindFailed(pFailedSpec, &exceptionObj);
+ }
+ GCPROTECT_END();
+ }
+#endif
+
+ // In the same cases as for the MDA, store the failure information for DAC to read
+ if (IsDebuggerAttached()) {
+ FailedAssembly *pFailed = new FailedAssembly();
+ pFailed->Initialize(pFailedSpec, ex);
+ IfFailThrow(m_failedAssemblies.Append(pFailed));
+ }
+
+ if (!bFileNotFoundException || fThrowOnFileNotFound)
+ {
+
+ // V1.1 App-compatibility workaround. See VSW530166 if you want to whine about it.
+ //
+ // In Everett, if we failed to download an assembly because of a broken network cable,
+ // we returned a FileNotFoundException with a COR_E_FILENOTFOUND hr embedded inside
+ // (which would be exposed when marshaled to native.)
+ //
+ // In Whidbey, we now set the more appropriate INET_E_RESOURCE_NOT_FOUND hr. But
+ // the online/offline switch code in VSTO for Everett hardcoded a check for
+ // COR_E_FILENOTFOUND.
+ //
+ // So now, to keep that code from breaking, we have to remap INET_E_RESOURCE_NOT_FOUND
+ // back to COR_E_FILENOTFOUND. We're doing it here rather down in Fusion so as to affect
+ // the least number of callers.
+
+ if (ex->GetHR() == INET_E_RESOURCE_NOT_FOUND)
+ {
+ EEFileLoadException::Throw(pFailedSpec, COR_E_FILENOTFOUND, ex);
+ }
+
+ if (EEFileLoadException::CheckType(ex))
+ {
+ if (pFailedSpec == pSpec)
+ {
+ EX_RETHROW; //preserve the information
+ }
+ else
+ {
+ StackSString exceptionDisplayName, failedSpecDisplayName;
+
+ ((EEFileLoadException*)ex)->GetName(exceptionDisplayName);
+ pFailedSpec->GetFileOrDisplayName(0, failedSpecDisplayName);
+
+ if (exceptionDisplayName.CompareCaseInsensitive(failedSpecDisplayName) == 0)
+ {
+ EX_RETHROW; // Throw the original exception. Otherwise, we'd throw an exception that contains the same message twice.
+ }
+ }
+ }
+
+ EEFileLoadException::Throw(pFailedSpec, ex->GetHR(), ex);
+ }
+
+ }
+ }
+ }
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+ // Now, if it's a cacheable bind we need to re-fetch the result from the cache, as we may have been racing with another
+ // thread to store our result. Note that we may throw from here, if there is a cached exception.
+ // This will release the refcount of the current result holder (if any), and will replace
+ // it with a non-addref'ed result
+ if (pSpec->CanUseWithBindingCache() && (result== NULL || result->CanUseWithBindingCache()))
+ {
+ result = FindCachedFile(pSpec);
+
+ if (result != NULL)
+ result->AddRef();
+ }
+
+ return result.Extract();
+ }
+ else
+ {
+ // Unsupported content type
+ if (fThrowOnFileNotFound)
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+ return nullptr;
+ }
+} // AppDomain::BindAssemblySpec
+
+
+#ifdef FEATURE_REFLECTION_ONLY_LOAD
+DomainAssembly *
+AppDomain::BindAssemblySpecForIntrospectionDependencies(
+ AssemblySpec * pSpec)
+{
+ STANDARD_VM_CONTRACT;
+
+ PRECONDITION(CheckPointer(pSpec));
+ PRECONDITION(pSpec->GetAppDomain() == this);
+ PRECONDITION(pSpec->IsIntrospectionOnly());
+ PRECONDITION(this == ::GetAppDomain());
+
+ PEAssemblyHolder result;
+ HRESULT hr;
+
+ if (!pSpec->HasUniqueIdentity())
+ {
+ if (!pSpec->HasBindableIdentity())
+ {
+ COMPlusThrowHR(E_UNEXPECTED);
+ }
+
+ // In classic (non-AppX), this is initilized by AppDomain constructor
+ _ASSERTE(m_pReflectionOnlyWinRtBinder != NULL);
+
+ ReleaseHolder<ICLRPrivAssembly> pPrivAssembly;
+ hr = m_pReflectionOnlyWinRtBinder->BindWinRtType(
+ pSpec->GetWinRtTypeNamespace(),
+ pSpec->GetWinRtTypeClassName(),
+ pSpec->GetParentAssembly(),
+ &pPrivAssembly);
+ if (FAILED(hr))
+ {
+ if (hr == CLR_E_BIND_TYPE_NOT_FOUND)
+ { // We could not find the type - throw TypeLoadException to give user type name for diagnostics
+ EX_THROW(EETypeLoadException, (pSpec->GetWinRtTypeNamespace(), pSpec->GetWinRtTypeClassName(), nullptr, nullptr, IDS_EE_REFLECTIONONLY_WINRT_LOADFAILURE));
+ }
+ if (!Exception::IsTransient(hr))
+ { // Throw the HRESULT as exception wrapped by TypeLoadException to give user type name for diagnostics
+ EEMessageException ex(hr);
+ EX_THROW_WITH_INNER(EETypeLoadException, (pSpec->GetWinRtTypeNamespace(), pSpec->GetWinRtTypeClassName(), nullptr, nullptr, IDS_EE_REFLECTIONONLY_WINRT_LOADFAILURE), &ex);
+ }
+ IfFailThrow(hr);
+ }
+
+ IfFailThrow(BindHostedPrivAssembly(nullptr, pPrivAssembly, nullptr, &result, TRUE));
+ _ASSERTE(result != nullptr);
+ return LoadDomainAssembly(pSpec, result, FILE_LOADED);
+ }
+
+ EX_TRY
+ {
+ if (!IsCached(pSpec))
+ {
+ result = TryResolveAssembly(pSpec, TRUE /*fPreBind*/);
+ if (result != NULL && result->CanUseWithBindingCache())
+ {
+ // Failure to add simply means someone else beat us to it. In that case
+ // the FindCachedFile call below (after catch block) will update result
+ // to the cached value.
+ AddFileToCache(pSpec, result, TRUE /*fAllowFailure*/);
+ }
+ }
+ }
+ EX_CATCH
+ {
+ Exception *ex = GET_EXCEPTION();
+ AssemblySpec NewSpec(this);
+ AssemblySpec *pFailedSpec = NULL;
+
+ // Let transient exceptions propagate
+ if (ex->IsTransient())
+ {
+ EX_RETHROW;
+ }
+
+ // Non-"file not found" exception also propagate
+ BOOL fFailure = PostBindResolveAssembly(pSpec, &NewSpec, ex->GetHR(), &pFailedSpec);
+ if(fFailure)
+ {
+ if (AddExceptionToCache(pFailedSpec, ex))
+ {
+ if ((pFailedSpec == pSpec) && EEFileLoadException::CheckType(ex))
+ {
+ EX_RETHROW; //preserve the information
+ }
+ else
+ EEFileLoadException::Throw(pFailedSpec, ex->GetHR(), ex);
+ }
+ }
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+ result = FindCachedFile(pSpec);
+ result.SuppressRelease();
+
+
+ if (result)
+ {
+ // It was either already in the spec cache or the prebind event returned a result.
+ return LoadDomainAssembly(pSpec, result, FILE_LOADED);
+ }
+
+
+ // Otherwise, look in the list of assemblies already loaded for reflectiononly.
+ IAssemblyName * ptmp = NULL;
+ hr = pSpec->CreateFusionName(&ptmp);
+ if (FAILED(hr))
+ {
+ COMPlusThrowHR(hr);
+ }
+ SafeComHolder<IAssemblyName> pIAssemblyName(ptmp);
+
+ // Note: We do not support introspection-only collectible assemblies (yet)
+ AppDomain::AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded | kIncludeIntrospection | kExcludeCollectible));
+ CollectibleAssemblyHolder<DomainAssembly *> pCachedDomainAssembly;
+
+ while (i.Next(pCachedDomainAssembly.This()))
+ {
+ _ASSERTE(!pCachedDomainAssembly->IsCollectible());
+ IAssemblyName * pCachedAssemblyName = pCachedDomainAssembly->GetAssembly()->GetFusionAssemblyName();
+ if (pCachedAssemblyName->IsEqual(pIAssemblyName, ASM_CMPF_IL_ALL) == S_OK)
+ {
+ return pCachedDomainAssembly;
+ }
+ }
+ // If not found in that list, it is an ERROR. Yes, this is by design.
+ StackSString name;
+ pSpec->GetFileOrDisplayName(0, name);
+ COMPlusThrow(kFileLoadException, IDS_EE_REFLECTIONONLY_LOADFAILURE,name);
+} // AppDomain::BindAssemblySpecForIntrospectionDependencies
+#endif // FEATURE_REFLECTION_ONLY_LOAD
+
+PEAssembly *AppDomain::TryResolveAssembly(AssemblySpec *pSpec, BOOL fPreBind)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_ANY;
+
+ PEAssembly *result = NULL;
+
+ EX_TRY
+ {
+ result = pSpec->ResolveAssemblyFile(this, fPreBind);
+ }
+ EX_HOOK
+ {
+ Exception *pEx = GET_EXCEPTION();
+
+ if (!pEx->IsTransient())
+ {
+ AddExceptionToCache(pSpec, pEx);
+ if (!EEFileLoadException::CheckType(pEx))
+ EEFileLoadException::Throw(pSpec, pEx->GetHR(), pEx);
+ }
+ }
+ EX_END_HOOK;
+
+ return result;
+}
+
+#ifdef FEATURE_FUSION
+void AppDomain::GetFileFromFusion(IAssembly *pIAssembly, LPCWSTR wszModuleName,
+ SString &path)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END;
+
+ SafeComHolder<IAssemblyModuleImport> pImport;
+ IfFailThrow(pIAssembly->GetModuleByName(wszModuleName, &pImport));
+
+ if (!pImport->IsAvailable()) {
+ AssemblySink* pSink = AllocateAssemblySink(NULL);
+ SafeComHolder<IAssemblyBindSink> sinkholder(pSink);
+ SafeComHolder<IAssemblyModuleImport> pResult;
+
+ IfFailThrow(FusionBind::RemoteLoadModule(GetFusionContext(),
+ pImport,
+ pSink,
+ &pResult));
+ pResult->AddRef();
+ pImport.Assign(pResult);
+ }
+
+ DWORD dwPath = 0;
+ pImport->GetModulePath(NULL, &dwPath);
+
+ LPWSTR buffer = path.OpenUnicodeBuffer(dwPath-1);
+ IfFailThrow(pImport->GetModulePath(buffer, &dwPath));
+ path.CloseBuffer();
+}
+
+PEAssembly *AppDomain::BindExplicitAssembly(HMODULE hMod, BOOL bindable)
+{
+ CONTRACT(PEAssembly *)
+ {
+ PRECONDITION(CheckPointer(hMod));
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ SafeComHolder<IAssembly> pFusionAssembly;
+ SafeComHolder<IBindResult> pNativeFusionAssembly;
+ SafeComHolder<IFusionBindLog> pFusionLog;
+
+ StackSString path;
+ PEImage::GetPathFromDll(hMod, path);
+
+ HRESULT hr = ExplicitBind(path, GetFusionContext(),
+ bindable ? EXPLICITBIND_FLAGS_EXE : EXPLICITBIND_FLAGS_NON_BINDABLE,
+ NULL, &pFusionAssembly, &pNativeFusionAssembly,&pFusionLog);
+ if (FAILED(hr))
+ EEFileLoadException::Throw(path, hr);
+
+ RETURN PEAssembly::OpenHMODULE(hMod, pFusionAssembly,pNativeFusionAssembly, pFusionLog, FALSE);
+}
+
+Assembly *AppDomain::LoadExplicitAssembly(HMODULE hMod, BOOL bindable)
+{
+ CONTRACT(Assembly *)
+ {
+ PRECONDITION(CheckPointer(hMod));
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ PEAssemblyHolder pFile(BindExplicitAssembly(hMod, bindable));
+
+ RETURN LoadAssembly(NULL, pFile, FILE_ACTIVE);
+}
+#endif // FEATURE_FUSION
+
+ULONG AppDomain::AddRef()
+{
+ LIMITED_METHOD_CONTRACT;
+ return InterlockedIncrement(&m_cRef);
+}
+
+ULONG AppDomain::Release()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(m_cRef > 0);
+ }
+ CONTRACTL_END;
+
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (!cRef)
+ {
+ _ASSERTE (m_Stage == STAGE_CREATING || m_Stage == STAGE_CLOSED);
+ ADID adid=GetId();
+ delete this;
+ TESTHOOKCALL(AppDomainDestroyed(adid.m_dwId));
+ }
+ return (cRef);
+}
+
+#ifdef FEATURE_FUSION
+AssemblySink* AppDomain::AllocateAssemblySink(AssemblySpec* pSpec)
+{
+ CONTRACT(AssemblySink *)
+ {
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ AssemblySink* ret = FastInterlockExchangePointer(&m_pAsyncPool, NULL);
+
+ if(ret == NULL)
+ ret = new AssemblySink(this);
+ else
+ ret->AddRef();
+ ret->SetAssemblySpec(pSpec);
+ RETURN ret;
+}
+#endif
+
+AppDomain* AppDomain::s_pAppDomainToRaiseUnloadEvent;
+BOOL AppDomain::s_fProcessUnloadDomainEvent = FALSE;
+
+#ifndef CROSSGEN_COMPILE
+
+void AppDomain::RaiseUnloadDomainEvent_Wrapper(LPVOID ptr)
+{
+ CONTRACTL
+ {
+ THROWS;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ AppDomain* pDomain = (AppDomain *) ptr;
+ pDomain->RaiseUnloadDomainEvent();
+}
+
+void AppDomain::ProcessUnloadDomainEventOnFinalizeThread()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+ Thread *pThread = GetThread();
+ _ASSERTE (pThread && IsFinalizerThread());
+
+ // if we are not unloading domain now, do not process the event
+ if (SystemDomain::AppDomainBeingUnloaded() == NULL)
+ {
+ s_pAppDomainToRaiseUnloadEvent->SetStage(STAGE_UNLOAD_REQUESTED);
+ s_pAppDomainToRaiseUnloadEvent->EnableADUnloadWorker(
+ s_pAppDomainToRaiseUnloadEvent->IsRudeUnload()?EEPolicy::ADU_Rude:EEPolicy::ADU_Safe);
+ FastInterlockExchangePointer(&s_pAppDomainToRaiseUnloadEvent, NULL);
+ return;
+ }
+ FastInterlockExchange((LONG*)&s_fProcessUnloadDomainEvent, TRUE);
+ AppDomain::EnableADUnloadWorkerForFinalizer();
+ pThread->SetThreadStateNC(Thread::TSNC_RaiseUnloadEvent);
+ s_pAppDomainToRaiseUnloadEvent->RaiseUnloadDomainEvent();
+ pThread->ResetThreadStateNC(Thread::TSNC_RaiseUnloadEvent);
+ s_pAppDomainToRaiseUnloadEvent->EnableADUnloadWorker(
+ s_pAppDomainToRaiseUnloadEvent->IsRudeUnload()?EEPolicy::ADU_Rude:EEPolicy::ADU_Safe);
+ FastInterlockExchangePointer(&s_pAppDomainToRaiseUnloadEvent, NULL);
+ FastInterlockExchange((LONG*)&s_fProcessUnloadDomainEvent, FALSE);
+
+ if (pThread->IsAbortRequested())
+ {
+ pThread->UnmarkThreadForAbort(Thread::TAR_Thread);
+ }
+}
+
+void AppDomain::RaiseUnloadDomainEvent()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ EX_TRY
+ {
+ Thread *pThread = GetThread();
+ if (this != pThread->GetDomain())
+ {
+ pThread->DoADCallBack(this, AppDomain::RaiseUnloadDomainEvent_Wrapper, this,ADV_FINALIZER|ADV_COMPILATION);
+ }
+ else
+ {
+ struct _gc
+ {
+ APPDOMAINREF Domain;
+ OBJECTREF Delegate;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ gc.Domain = (APPDOMAINREF) GetRawExposedObject();
+ if (gc.Domain != NULL)
+ {
+ gc.Delegate = gc.Domain->m_pDomainUnloadEventHandler;
+ if (gc.Delegate != NULL)
+ DistributeEventReliably(&gc.Delegate, (OBJECTREF *) &gc.Domain);
+ }
+ GCPROTECT_END();
+ }
+ }
+ EX_CATCH
+ {
+ //@TODO call a MDA here
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+}
+
+void AppDomain::RaiseLoadingAssemblyEvent(DomainAssembly *pAssembly)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ PRECONDITION(this == GetAppDomain());
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+ FAULT_NOT_FATAL();
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+
+ EX_TRY
+ {
+ struct _gc {
+ APPDOMAINREF AppDomainRef;
+ OBJECTREF orThis;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ if ((gc.AppDomainRef = (APPDOMAINREF) GetRawExposedObject()) != NULL) {
+ if (gc.AppDomainRef->m_pAssemblyEventHandler != NULL)
+ {
+ ARG_SLOT args[2];
+ GCPROTECT_BEGIN(gc);
+
+ gc.orThis = pAssembly->GetExposedAssemblyObject();
+
+ MethodDescCallSite onAssemblyLoad(METHOD__APP_DOMAIN__ON_ASSEMBLY_LOAD, &gc.orThis);
+
+ // GetExposedAssemblyObject may cause a gc, so call this before filling args[0]
+ args[1] = ObjToArgSlot(gc.orThis);
+ args[0] = ObjToArgSlot(gc.AppDomainRef);
+
+ onAssemblyLoad.Call(args);
+
+ GCPROTECT_END();
+ }
+ }
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+}
+
+
+BOOL AppDomain::OnUnhandledException(OBJECTREF *pThrowable, BOOL isTerminating/*=TRUE*/)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_ANY;
+
+ BOOL retVal= FALSE;
+
+ GCX_COOP();
+
+ // The Everett behavior was to send the unhandled exception event only to the Default
+ // AppDomain (since that's the only place that exceptions actually went unhandled).
+ //
+ // During Whidbey development, we broadcast the event to all AppDomains in the process.
+ //
+ // But the official shipping Whidbey behavior is that the unhandled exception event is
+ // sent to the Default AppDomain and to whatever AppDomain the exception went unhandled
+ // in. To achieve this, we declare the exception to be unhandled *BEFORE* we marshal
+ // it back to the Default AppDomain at the base of the Finalizer, threadpool and managed
+ // threads.
+ //
+ // The rationale for sending the event to the Default AppDomain as well as the one the
+ // exception went unhandled in is:
+ //
+ // 1) This is compatible with the pre-Whidbey behavior, where only the Default AppDomain
+ // received the notification.
+ //
+ // 2) This is convenient for hosts, which don't want to bother injecting listeners into
+ // every single AppDomain.
+
+ AppDomain *pAppDomain = GetAppDomain();
+ OBJECTREF orSender = 0;
+
+ GCPROTECT_BEGIN(orSender);
+
+ orSender = pAppDomain->GetRawExposedObject();
+
+ retVal = pAppDomain->RaiseUnhandledExceptionEventNoThrow(&orSender, pThrowable, isTerminating);
+#ifndef FEATURE_CORECLR
+// CoreCLR#520:
+// To make this work correctly we need the changes for coreclr 473
+ if (pAppDomain != SystemDomain::System()->DefaultDomain())
+ retVal |= SystemDomain::System()->DefaultDomain()->RaiseUnhandledExceptionEventNoThrow
+ (&orSender, pThrowable, isTerminating);
+#endif
+
+ GCPROTECT_END();
+
+ return retVal;
+}
+
+
+// Move outside of the AppDomain iteration, to avoid issues with the GC Frames being outside
+// the domain transition. This is a chronic issue that causes us to report roots for an AppDomain
+// after we have left it. This causes problems with AppDomain unloading that we only find
+// with stress coverage..
+void AppDomain::RaiseOneExitProcessEvent()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ struct _gc
+ {
+ APPDOMAINREF Domain;
+ OBJECTREF Delegate;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ EX_TRY {
+
+ GCPROTECT_BEGIN(gc);
+ gc.Domain = (APPDOMAINREF) SystemDomain::GetCurrentDomain()->GetRawExposedObject();
+ if (gc.Domain != NULL)
+ {
+ gc.Delegate = gc.Domain->m_pProcessExitEventHandler;
+ if (gc.Delegate != NULL)
+ DistributeEventReliably(&gc.Delegate, (OBJECTREF *) &gc.Domain);
+ }
+ GCPROTECT_END();
+
+ } EX_CATCH {
+ } EX_END_CATCH(SwallowAllExceptions);
+}
+
+// Local wrapper used in AppDomain::RaiseExitProcessEvent,
+// introduced solely to avoid stack overflow because of _alloca in the loop.
+// It's just factored out body of the loop, but it has to be a member method of AppDomain,
+// because it calls private RaiseOneExitProcessEvent
+/*static*/ void AppDomain::RaiseOneExitProcessEvent_Wrapper(AppDomainIterator* pi)
+{
+
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ EX_TRY {
+ ENTER_DOMAIN_PTR(pi->GetDomain(),ADV_ITERATOR)
+ AppDomain::RaiseOneExitProcessEvent();
+ END_DOMAIN_TRANSITION;
+ } EX_CATCH {
+ } EX_END_CATCH(SwallowAllExceptions);
+}
+
+static LONG s_ProcessedExitProcessEventCount = 0;
+
+LONG GetProcessedExitProcessEventCount()
+{
+ LIMITED_METHOD_CONTRACT;
+ return s_ProcessedExitProcessEventCount;
+}
+
+void AppDomain::RaiseExitProcessEvent()
+{
+ if (!g_fEEStarted)
+ return;
+
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ // Only finalizer thread during shutdown can call this function.
+ _ASSERTE ((g_fEEShutDown&ShutDown_Finalize1) && GetThread() == FinalizerThread::GetFinalizerThread());
+
+ _ASSERTE (GetThread()->PreemptiveGCDisabled());
+
+ _ASSERTE (GetThread()->GetDomain()->IsDefaultDomain());
+
+ AppDomainIterator i(TRUE);
+ while (i.Next())
+ {
+ RaiseOneExitProcessEvent_Wrapper(&i);
+ FastInterlockIncrement(&s_ProcessedExitProcessEventCount);
+ }
+}
+
+#ifndef FEATURE_CORECLR
+void AppDomain::RaiseUnhandledExceptionEvent_Wrapper(LPVOID ptr)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+ AppDomain::RaiseUnhandled_Args *args = (AppDomain::RaiseUnhandled_Args *) ptr;
+
+ struct _gc {
+ OBJECTREF orThrowable;
+ OBJECTREF orSender;
+ } gc;
+
+ ZeroMemory(&gc, sizeof(gc));
+
+ _ASSERTE(args->pTargetDomain == GetAppDomain());
+ GCPROTECT_BEGIN(gc);
+ EX_TRY
+ {
+ SetObjectReference(&gc.orThrowable,
+ AppDomainHelper::CrossContextCopyFrom(args->pExceptionDomain,
+ args->pThrowable),
+ args->pTargetDomain);
+
+ SetObjectReference(&gc.orSender,
+ AppDomainHelper::CrossContextCopyFrom(args->pExceptionDomain,
+ args->pSender),
+ args->pTargetDomain);
+ }
+ EX_CATCH
+ {
+ SetObjectReference(&gc.orThrowable, GET_THROWABLE(), args->pTargetDomain);
+ SetObjectReference(&gc.orSender, GetAppDomain()->GetRawExposedObject(), args->pTargetDomain);
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ *(args->pResult) = args->pTargetDomain->RaiseUnhandledExceptionEvent(&gc.orSender,
+ &gc.orThrowable,
+ args->isTerminating);
+ GCPROTECT_END();
+
+}
+#endif //!FEATURE_CORECLR
+
+BOOL
+AppDomain::RaiseUnhandledExceptionEventNoThrow(OBJECTREF *pSender, OBJECTREF *pThrowable, BOOL isTerminating)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+ BOOL bRetVal=FALSE;
+
+ EX_TRY
+ {
+ bRetVal = RaiseUnhandledExceptionEvent(pSender, pThrowable, isTerminating);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions) // Swallow any errors.
+ return bRetVal;
+
+}
+
+BOOL
+AppDomain::HasUnhandledExceptionEventHandler()
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_NOTRIGGER; //essential
+ NOTHROW;
+ }
+ CONTRACTL_END;
+ if (!CanThreadEnter(GetThread()))
+ return FALSE;
+ if (GetRawExposedObject()==NULL)
+ return FALSE;
+ return (((APPDOMAINREF)GetRawExposedObject())->m_pUnhandledExceptionEventHandler!=NULL);
+}
+
+BOOL
+AppDomain::RaiseUnhandledExceptionEvent(OBJECTREF *pSender, OBJECTREF *pThrowable, BOOL isTerminating)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (!HasUnhandledExceptionEventHandler())
+ return FALSE;
+
+ BOOL result = FALSE;
+
+ _ASSERTE(pThrowable != NULL && IsProtectedByGCFrame(pThrowable));
+ _ASSERTE(pSender != NULL && IsProtectedByGCFrame(pSender));
+
+#ifndef FEATURE_CORECLR
+ Thread *pThread = GetThread();
+ if (this != pThread->GetDomain())
+ {
+ RaiseUnhandled_Args args = {pThread->GetDomain(), this, pSender, pThrowable, isTerminating, &result};
+ // call through DoCallBack with a domain transition
+ pThread->DoADCallBack(this, AppDomain::RaiseUnhandledExceptionEvent_Wrapper, &args, ADV_DEFAULTAD);
+ return result;
+ }
+#else
+ _ASSERTE(this == GetThread()->GetDomain());
+#endif
+
+
+ OBJECTREF orDelegate = NULL;
+
+ GCPROTECT_BEGIN(orDelegate);
+
+ APPDOMAINREF orAD = (APPDOMAINREF) GetAppDomain()->GetRawExposedObject();
+
+ if (orAD != NULL)
+ {
+ orDelegate = orAD->m_pUnhandledExceptionEventHandler;
+ if (orDelegate != NULL)
+ {
+ result = TRUE;
+ DistributeUnhandledExceptionReliably(&orDelegate, pSender, pThrowable, isTerminating);
+ }
+ }
+ GCPROTECT_END();
+ return result;
+}
+
+
+#ifndef FEATURE_CORECLR
+// Create a domain based on a string name
+AppDomain* AppDomain::CreateDomainContext(LPCWSTR fileName)
+{
+ CONTRACTL
+ {
+ THROWS;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if(fileName == NULL) return NULL;
+
+ AppDomain* pDomain = NULL;
+
+ MethodDescCallSite valCreateDomain(METHOD__APP_DOMAIN__VAL_CREATE_DOMAIN);
+
+ STRINGREF pFilePath = NULL;
+ GCPROTECT_BEGIN(pFilePath);
+ pFilePath = StringObject::NewString(fileName);
+
+ ARG_SLOT args[1] =
+ {
+ ObjToArgSlot(pFilePath),
+ };
+
+ APPDOMAINREF pDom = (APPDOMAINREF) valCreateDomain.Call_RetOBJECTREF(args);
+ if(pDom != NULL)
+ {
+ Context* pContext = Context::GetExecutionContext(pDom);
+ if(pContext)
+ {
+ pDomain = pContext->GetDomain();
+ }
+ }
+ GCPROTECT_END();
+
+ return pDomain;
+}
+#endif // !FEATURE_CORECLR
+
+#endif // CROSSGEN_COMPILE
+
+// You must be in the correct context before calling this
+// routine. Therefore, it is only good for initializing the
+// default domain.
+void AppDomain::InitializeDomainContext(BOOL allowRedirects,
+ LPCWSTR pwszPath,
+ LPCWSTR pwszConfig)
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (NingenEnabled())
+ {
+#ifdef FEATURE_FUSION
+ CreateFusionContext();
+#endif // FEATURE_FUSION
+
+#ifdef FEATURE_VERSIONING
+ CreateFusionContext();
+#endif // FEATURE_VERSIONING
+
+ return;
+ }
+
+#ifndef CROSSGEN_COMPILE
+ struct _gc {
+ STRINGREF pFilePath;
+ STRINGREF pConfig;
+ OBJECTREF ref;
+ PTRARRAYREF propertyNames;
+ PTRARRAYREF propertyValues;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ if(pwszPath)
+ {
+ gc.pFilePath = StringObject::NewString(pwszPath);
+ }
+
+ if(pwszConfig)
+ {
+ gc.pConfig = StringObject::NewString(pwszConfig);
+ }
+
+#ifndef FEATURE_CORECLR
+ StringArrayList *pPropertyNames;
+ StringArrayList *pPropertyValues;
+ CorHost2::GetDefaultAppDomainProperties(&pPropertyNames, &pPropertyValues);
+
+ _ASSERTE(pPropertyNames->GetCount() == pPropertyValues->GetCount());
+
+ if (pPropertyNames->GetCount() > 0)
+ {
+ gc.propertyNames = (PTRARRAYREF)AllocateObjectArray(pPropertyNames->GetCount(), g_pStringClass);
+ gc.propertyValues = (PTRARRAYREF)AllocateObjectArray(pPropertyValues->GetCount(), g_pStringClass);
+
+ for (DWORD i = 0; i < pPropertyNames->GetCount(); ++i)
+ {
+ STRINGREF propertyName = StringObject::NewString(pPropertyNames->Get(i));
+ gc.propertyNames->SetAt(i, propertyName);
+
+ STRINGREF propertyValue = StringObject::NewString(pPropertyValues->Get(i));
+ gc.propertyValues->SetAt(i, propertyValue);
+ }
+ }
+#endif // !FEATURE_CORECLR
+
+ if ((gc.ref = GetExposedObject()) != NULL)
+ {
+ MethodDescCallSite setupDomain(METHOD__APP_DOMAIN__SETUP_DOMAIN);
+
+ ARG_SLOT args[] =
+ {
+ ObjToArgSlot(gc.ref),
+ BoolToArgSlot(allowRedirects),
+ ObjToArgSlot(gc.pFilePath),
+ ObjToArgSlot(gc.pConfig),
+ ObjToArgSlot(gc.propertyNames),
+ ObjToArgSlot(gc.propertyValues)
+ };
+ setupDomain.Call(args);
+ }
+ GCPROTECT_END();
+
+ CacheStringsForDAC();
+#endif // CROSSGEN_COMPILE
+}
+
+#ifdef FEATURE_FUSION
+
+void AppDomain::SetupLoaderOptimization(DWORD optimization)
+{
+ STANDARD_VM_CONTRACT;
+
+ GCX_COOP();
+
+ if ((GetExposedObject()) != NULL)
+ {
+ MethodDescCallSite setupLoaderOptimization(METHOD__APP_DOMAIN__SETUP_LOADER_OPTIMIZATION);
+
+ ARG_SLOT args[2] =
+ {
+ ObjToArgSlot(GetExposedObject()),
+ optimization
+ };
+ setupLoaderOptimization.Call(args);
+ }
+}
+
+// The fusion context should only be null when appdomain is being setup
+// and there should be no reason to protect the creation.
+IApplicationContext *AppDomain::CreateFusionContext()
+{
+ CONTRACT(IApplicationContext *)
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ if (m_pFusionContext == NULL)
+ {
+ ETWOnStartup (FusionAppCtx_V1, FusionAppCtxEnd_V1);
+
+ GCX_PREEMP();
+
+ SafeComHolderPreemp<IApplicationContext> pFusionContext;
+
+ IfFailThrow(FusionBind::CreateFusionContext(NULL, &pFusionContext));
+
+#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORECLR)
+ CLRPrivBinderWinRT * pWinRtBinder;
+ if (AppX::IsAppXProcess())
+ { // Note: Fusion binder is used in AppX to bind .NET Fx assemblies - some of them depend on .winmd files (e.g. System.Runtime.WindowsRuntime.dll)
+ CLRPrivBinderAppX * pAppXBinder = CLRPrivBinderAppX::GetOrCreateBinder();
+ pWinRtBinder = pAppXBinder->GetWinRtBinder();
+ }
+ else
+ {
+ pWinRtBinder = m_pWinRtBinder;
+ }
+ _ASSERTE(pWinRtBinder != nullptr);
+
+ IfFailThrow(SetApplicationContext_WinRTBinder(
+ pFusionContext,
+ static_cast<IBindContext *>(pWinRtBinder)));
+#endif
+
+#ifdef FEATURE_PREJIT
+ if (NGENImagesAllowed())
+ {
+ // Set the native image settings so fusion will bind native images
+ SString zapString(g_pConfig->ZapSet());
+ FusionBind::SetApplicationContextStringProperty(pFusionContext, ACTAG_ZAP_STRING, zapString);
+ FusionBind::SetApplicationContextDWORDProperty(pFusionContext, ACTAG_ZAP_CONFIG_FLAGS,
+ PEFile::GetNativeImageConfigFlags());
+ }
+#endif // FEATURE_PREJIT
+
+ pFusionContext.SuppressRelease();
+ m_pFusionContext = pFusionContext;
+
+ DWORD dwId = m_dwId.m_dwId;
+ IfFailThrow(m_pFusionContext->Set(ACTAG_APP_DOMAIN_ID, &dwId, sizeof(DWORD), 0));
+
+ if (HasLoadContextHostBinder())
+ FusionBind::SetApplicationContextDWORDProperty(pFusionContext, ACTAG_FX_ONLY,1);
+
+ }
+
+ RETURN m_pFusionContext;
+}
+
+void AppDomain::TurnOnBindingRedirects()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+
+ if ((GetExposedObject()) != NULL)
+ {
+ MethodDescCallSite turnOnBindingRedirects(METHOD__APP_DOMAIN__TURN_ON_BINDING_REDIRECTS);
+ ARG_SLOT args[1] =
+ {
+ ObjToArgSlot(GetExposedObject()),
+ };
+ turnOnBindingRedirects.Call(args);
+ }
+
+ IfFailThrow(m_pFusionContext->Set(ACTAG_DISALLOW_APP_BINDING_REDIRECTS,
+ NULL,
+ 0,
+ 0));
+}
+
+void AppDomain::SetupExecutableFusionContext(LPCWSTR exePath)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(GetAppDomain() == this);
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+
+ struct _gc {
+ STRINGREF pFilePath;
+ OBJECTREF ref;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ gc.pFilePath = StringObject::NewString(exePath);
+
+ if ((gc.ref = GetExposedObject()) != NULL)
+ {
+ MethodDescCallSite setDomainContext(METHOD__APP_DOMAIN__SET_DOMAIN_CONTEXT, &gc.ref);
+ ARG_SLOT args[2] =
+ {
+ ObjToArgSlot(gc.ref),
+ ObjToArgSlot(gc.pFilePath),
+ };
+ setDomainContext.Call(args);
+ }
+
+ GCPROTECT_END();
+
+}
+
+BOOL AppDomain::SetContextProperty(IApplicationContext* pFusionContext,
+ LPCWSTR pProperty, OBJECTREF* obj)
+
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (GetAppDomain()->HasLoadContextHostBinder())
+ COMPlusThrow(kNotSupportedException);
+
+
+ if(obj) {
+ if ((*obj) != NULL){
+ MethodTable* pMT = (*obj)->GetMethodTable();
+ DWORD lgth;
+
+ if(MscorlibBinder::IsClass(pMT, CLASS__STRING)) {
+
+ lgth = (ObjectToSTRINGREF(*(StringObject**)obj))->GetStringLength();
+ CQuickBytes qb;
+ LPWSTR wszValue = (LPWSTR) qb.AllocThrows((lgth+1)*sizeof(WCHAR));
+ memcpy(wszValue, (ObjectToSTRINGREF(*(StringObject**)obj))->GetBuffer(), lgth*sizeof(WCHAR));
+ if(lgth > 0 && wszValue[lgth-1] == '/')
+ lgth--;
+ wszValue[lgth] = W('\0');
+
+ LOG((LF_LOADER,
+ LL_INFO10,
+ "Set: %S: *%S*.\n",
+ pProperty, wszValue));
+
+ IfFailThrow(pFusionContext->Set(pProperty,
+ wszValue,
+ (lgth+1) * sizeof(WCHAR),
+ 0));
+ }
+ else {
+ // Pin byte array for loading
+ Wrapper<OBJECTHANDLE, DoNothing, DestroyPinningHandle> handle(
+ GetAppDomain()->CreatePinningHandle(*obj));
+
+ const BYTE *pbArray = ((U1ARRAYREF)(*obj))->GetDirectConstPointerToNonObjectElements();
+ DWORD cbArray = (*obj)->GetNumComponents();
+
+ IfFailThrow(pFusionContext->Set(pProperty,
+ (LPVOID) pbArray,
+ cbArray,
+ 0));
+ }
+ }
+ else { // Un-set the property
+ IfFailThrow(pFusionContext->Set(pProperty,
+ NULL,
+ 0,
+ 0));
+ }
+ }
+
+ return TRUE;
+}
+#endif // FEATURE_FUSION
+
+#ifdef FEATURE_VERSIONING
+IUnknown *AppDomain::CreateFusionContext()
+{
+ CONTRACT(IUnknown *)
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ if (!m_pFusionContext)
+ {
+ ETWOnStartup (FusionAppCtx_V1, FusionAppCtxEnd_V1);
+ CLRPrivBinderCoreCLR *pTPABinder = NULL;
+
+ GCX_PREEMP();
+
+ // Initialize the assembly binder for the default context loads for CoreCLR.
+ IfFailThrow(CCoreCLRBinderHelper::DefaultBinderSetupContext(GetId().m_dwId, &pTPABinder));
+ m_pFusionContext = reinterpret_cast<IUnknown *>(pTPABinder);
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+ // By default, initial binding context setup for CoreCLR is also the TPABinding context
+ (m_pTPABinderContext = pTPABinder)->AddRef();
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER)
+
+ }
+
+ RETURN m_pFusionContext;
+}
+#endif // FEATURE_VERSIONING
+
+#ifdef FEATURE_FUSION
+LPWSTR AppDomain::GetDynamicDir()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (m_pwDynamicDir == NULL) {
+
+ BaseDomain::LockHolder lh(this);
+
+ if(m_pwDynamicDir == NULL) {
+ IApplicationContext* pFusionContext = GetFusionContext();
+ _ASSERTE(pFusionContext);
+
+ HRESULT hr = S_OK;
+ DWORD dwSize = 0;
+ hr = pFusionContext->GetDynamicDirectory(NULL, &dwSize);
+ AllocMemHolder<WCHAR> tempDynamicDir;
+
+ if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
+ tempDynamicDir = GetLowFrequencyHeap()->AllocMem(S_SIZE_T(dwSize) * S_SIZE_T(sizeof(WCHAR)));
+ hr = pFusionContext->GetDynamicDirectory(tempDynamicDir, &dwSize);
+ }
+ if(hr==HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
+ return NULL;
+ IfFailThrow(hr);
+
+ tempDynamicDir.SuppressRelease();
+ m_pwDynamicDir = tempDynamicDir;
+ }
+ // lh out of scope here
+ }
+
+ return m_pwDynamicDir;;
+}
+#endif //FEATURE_FUSION
+
+
+//---------------------------------------------------------------------------------------
+//
+// AppDomain::IsDebuggerAttached - is a debugger attached to this process
+//
+// Arguments:
+// None
+//
+// Return Value:
+// TRUE if a debugger is attached to this process, FALSE otherwise.
+//
+// Notes:
+// This is identical to CORDebuggerAttached. This exists idependantly for legacy reasons - we used to
+// support attaching to individual AppDomains. This should probably go away eventually.
+//
+
+BOOL AppDomain::IsDebuggerAttached()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (CORDebuggerAttached())
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+#ifdef DEBUGGING_SUPPORTED
+
+// This is called from the debugger to request notification events from
+// Assemblies, Modules, Types in this appdomain.
+BOOL AppDomain::NotifyDebuggerLoad(int flags, BOOL attaching)
+{
+ WRAPPER_NO_CONTRACT;
+ BOOL result = FALSE;
+
+ if (!attaching && !IsDebuggerAttached())
+ return FALSE;
+
+ AssemblyIterator i;
+
+ // Attach to our assemblies
+ LOG((LF_CORDB, LL_INFO100, "AD::NDA: Iterating assemblies\n"));
+ i = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+ while (i.Next(pDomainAssembly.This()))
+ {
+ result = (pDomainAssembly->NotifyDebuggerLoad(flags, attaching) ||
+ result);
+ }
+
+ return result;
+}
+
+void AppDomain::NotifyDebuggerUnload()
+{
+ WRAPPER_NO_CONTRACT;
+ if (!IsDebuggerAttached())
+ return;
+
+ LOG((LF_CORDB, LL_INFO10, "AD::NDD domain [%d] %#08x %ls\n",
+ GetId().m_dwId, this, GetFriendlyNameForLogging()));
+
+ LOG((LF_CORDB, LL_INFO100, "AD::NDD: Interating domain bound assemblies\n"));
+ AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ // Detach from our assemblies
+ while (i.Next(pDomainAssembly.This()))
+ {
+ LOG((LF_CORDB, LL_INFO100, "AD::NDD: Iterating assemblies\n"));
+ pDomainAssembly->NotifyDebuggerUnload();
+ }
+}
+#endif // DEBUGGING_SUPPORTED
+
+void AppDomain::SetSystemAssemblyLoadEventSent(BOOL fFlag)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (fFlag == TRUE)
+ m_dwFlags |= LOAD_SYSTEM_ASSEMBLY_EVENT_SENT;
+ else
+ m_dwFlags &= ~LOAD_SYSTEM_ASSEMBLY_EVENT_SENT;
+}
+
+BOOL AppDomain::WasSystemAssemblyLoadEventSent(void)
+{
+ LIMITED_METHOD_CONTRACT;
+ return ((m_dwFlags & LOAD_SYSTEM_ASSEMBLY_EVENT_SENT) == 0) ? FALSE : TRUE;
+}
+
+#ifndef CROSSGEN_COMPILE
+// U->M thunks created in this domain and not associated with a delegate.
+UMEntryThunkCache *AppDomain::GetUMEntryThunkCache()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (!m_pUMEntryThunkCache)
+ {
+ UMEntryThunkCache *pUMEntryThunkCache = new UMEntryThunkCache(this);
+
+ if (FastInterlockCompareExchangePointer(&m_pUMEntryThunkCache, pUMEntryThunkCache, NULL) != NULL)
+ {
+ // some thread swooped in and set the field
+ delete pUMEntryThunkCache;
+ }
+ }
+ _ASSERTE(m_pUMEntryThunkCache);
+ return m_pUMEntryThunkCache;
+}
+
+#ifdef FEATURE_COMINTEROP
+
+ComCallWrapperCache *AppDomain::GetComCallWrapperCache()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ if (! m_pComCallWrapperCache)
+ {
+ BaseDomain::LockHolder lh(this);
+
+ if (! m_pComCallWrapperCache)
+ m_pComCallWrapperCache = ComCallWrapperCache::Create(this);
+ }
+ _ASSERTE(m_pComCallWrapperCache);
+ return m_pComCallWrapperCache;
+}
+
+RCWRefCache *AppDomain::GetRCWRefCache()
+{
+ CONTRACT(RCWRefCache*)
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ if (!m_pRCWRefCache) {
+ NewHolder<RCWRefCache> pRCWRefCache = new RCWRefCache(this);
+ if (FastInterlockCompareExchangePointer(&m_pRCWRefCache, (RCWRefCache *)pRCWRefCache, NULL) == NULL)
+ {
+ pRCWRefCache.SuppressRelease();
+ }
+ }
+ RETURN m_pRCWRefCache;
+}
+
+RCWCache *AppDomain::CreateRCWCache()
+{
+ CONTRACT(RCWCache*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ // Initialize the global RCW cleanup list here as well. This is so that it
+ // it guaranteed to exist if any RCW's are created, but it is not created
+ // unconditionally.
+ if (!g_pRCWCleanupList)
+ {
+ SystemDomain::LockHolder lh;
+
+ if (!g_pRCWCleanupList)
+ g_pRCWCleanupList = new RCWCleanupList();
+ }
+ _ASSERTE(g_pRCWCleanupList);
+
+ {
+ BaseDomain::LockHolder lh(this);
+
+ if (!m_pRCWCache)
+ m_pRCWCache = new RCWCache(this);
+ }
+
+ RETURN m_pRCWCache;
+}
+
+void AppDomain::ReleaseRCWs(LPVOID pCtxCookie)
+{
+ WRAPPER_NO_CONTRACT;
+ if (m_pRCWCache)
+ m_pRCWCache->ReleaseWrappersWorker(pCtxCookie);
+
+ RemoveWinRTFactoryObjects(pCtxCookie);
+}
+
+void AppDomain::DetachRCWs()
+{
+ WRAPPER_NO_CONTRACT;
+ if (m_pRCWCache)
+ m_pRCWCache->DetachWrappersWorker();
+}
+
+#endif // FEATURE_COMINTEROP
+
+BOOL AppDomain::CanThreadEnter(Thread *pThread)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_Stage < STAGE_EXITED)
+ return TRUE;
+
+ if (pThread == SystemDomain::System()->GetUnloadingThread())
+ return m_Stage < STAGE_FINALIZING;
+ if (pThread == FinalizerThread::GetFinalizerThread())
+ return m_Stage < STAGE_FINALIZED;
+
+ return FALSE;
+}
+
+void AppDomain::AllowThreadEntrance(AppDomain * pApp)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ FORBID_FAULT;
+ PRECONDITION(CheckPointer(pApp));
+ }
+ CONTRACTL_END;
+
+ if (pApp->GetUnloadRequestThread() == NULL)
+ {
+ // This is asynchonous unload, either by a host, or by AppDomain.Unload from AD unload event.
+ if (!pApp->IsUnloadingFromUnloadEvent())
+ {
+ pApp->SetStage(STAGE_UNLOAD_REQUESTED);
+ pApp->EnableADUnloadWorker(
+ pApp->IsRudeUnload()?EEPolicy::ADU_Rude:EEPolicy::ADU_Safe);
+ return;
+ }
+ }
+
+ SystemDomain::LockHolder lh; // we don't want to reopen appdomain if other thread can be preparing to unload it
+
+#ifdef FEATURE_COMINTEROP
+ if (pApp->m_pComCallWrapperCache)
+ pApp->m_pComCallWrapperCache->ResetDomainIsUnloading();
+#endif // FEATURE_COMINTEROP
+
+ pApp->SetStage(STAGE_OPEN);
+}
+
+void AppDomain::RestrictThreadEntrance(AppDomain * pApp)
+{
+ CONTRACTL
+ {
+ DISABLED(NOTHROW);
+ DISABLED(GC_TRIGGERS);
+ MODE_ANY;
+ DISABLED(FORBID_FAULT);
+ PRECONDITION(CheckPointer(pApp));
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_COMINTEROP
+ // Set the flag on our CCW cache so stubs won't enter
+ if (pApp->m_pComCallWrapperCache)
+ pApp->m_pComCallWrapperCache->SetDomainIsUnloading();
+#endif // FEATURE_COMINTEROP
+
+ SystemDomain::LockHolder lh; // we don't want to reopen appdomain if other thread can be preparing to unload it
+ // Release our ID so remoting and thread pool won't enter
+ pApp->SetStage(STAGE_EXITED);
+};
+
+void AppDomain::Exit(BOOL fRunFinalizers, BOOL fAsyncExit)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Exiting domain [%d] %#08x %ls\n",
+ GetId().m_dwId, this, GetFriendlyNameForLogging()));
+
+ RestrictEnterHolder RestrictEnter(this);
+
+ {
+ SystemDomain::LockHolder lh; // we don't want to close appdomain if other thread can be preparing to unload it
+ SetStage(STAGE_EXITING); // Note that we're trying to exit
+ }
+
+ // Raise the event indicating the domain is being unloaded.
+ if (GetDefaultContext())
+ {
+ FastInterlockExchangePointer(&s_pAppDomainToRaiseUnloadEvent, this);
+
+ DWORD timeout = GetEEPolicy()->GetTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload);
+ //if (timeout == INFINITE)
+ //{
+ // timeout = 20000; // 20 seconds
+ //}
+ DWORD timeoutForFinalizer = GetEEPolicy()->GetTimeout(OPR_FinalizerRun);
+ ULONGLONG curTime = CLRGetTickCount64();
+ ULONGLONG endTime = 0;
+ if (timeout != INFINITE)
+ {
+ endTime = curTime + timeout;
+ // We will try to kill AD unload event if it takes too long, and then we move on to the next registered caller.
+ timeout /= 5;
+ }
+
+ while (s_pAppDomainToRaiseUnloadEvent != NULL)
+ {
+ FinalizerThread::FinalizerThreadWait(s_fProcessUnloadDomainEvent?timeout:timeoutForFinalizer);
+ if (endTime != 0 && s_pAppDomainToRaiseUnloadEvent != NULL)
+ {
+ if (CLRGetTickCount64() >= endTime)
+ {
+ SString sThreadId;
+ sThreadId.Printf(W("%x"), FinalizerThread::GetFinalizerThread()->GetThreadId());
+ COMPlusThrow(kCannotUnloadAppDomainException,
+ IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD,
+ sThreadId);
+ }
+ }
+ }
+ }
+
+ //
+ // Set up blocks so no threads can enter except for the finalizer and the thread
+ // doing the unload.
+ //
+
+ RestrictThreadEntrance(this);
+
+ // Cause existing threads to abort out of this domain. This should ensure all
+ // normal threads are outside the domain, and we've already ensured that no new threads
+ // can enter.
+
+ PerAppDomainTPCountList::AppDomainUnloadingHolder tpAdUnloadHolder(GetTPIndex());
+
+
+ if (!NingenEnabled())
+ {
+ UnwindThreads();
+ }
+
+ TESTHOOKCALL(UnwoundThreads(GetId().m_dwId)) ;
+ ProcessEventForHost(Event_DomainUnload, (PVOID)(UINT_PTR)GetId().m_dwId);
+
+ RestrictEnter.SuppressRelease(); //after this point we don't guarantee appdomain consistency
+#ifdef PROFILING_SUPPORTED
+ // Signal profile if present.
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ GCX_PREEMP();
+ g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) this);
+ END_PIN_PROFILER();
+ }
+#endif // PROFILING_SUPPORTED
+ COUNTER_ONLY(GetPerfCounters().m_Loading.cAppDomains--);
+ COUNTER_ONLY(GetPerfCounters().m_Loading.cAppDomainsUnloaded++);
+
+ LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is exited.\n",
+ GetId().m_dwId, this, GetFriendlyNameForLogging()));
+
+ ReJitManager::OnAppDomainExit(this);
+
+ // Send ETW events for this domain's unload and potentially iterate through this
+ // domain's modules & assemblies to send events for their unloads as well. This
+ // needs to occur before STAGE_FINALIZED (to ensure everything is there), so we do
+ // this before any finalization occurs at all.
+ ETW::LoaderLog::DomainUnload(this);
+
+ //
+ // Spin running finalizers until we flush them all. We need to make multiple passes
+ // in case the finalizers create more finalizable objects. This is important to clear
+ // the finalizable objects as roots, as well as to actually execute the finalizers. This
+ // will only finalize instances instances of types that aren't potentially agile becuase we can't
+ // risk finalizing agile objects. So we will be left with instances of potentially agile types
+ // in handles or statics.
+ //
+ // <TODO>@todo: Need to ensure this will terminate in a reasonable amount of time. Eventually
+ // we should probably start passing FALSE for fRunFinalizers. Also I'm not sure we
+ // guarantee that FinalizerThreadWait will ever terminate in general.</TODO>
+ //
+
+ SetStage(STAGE_FINALIZING);
+
+ // Flush finalizers now.
+ FinalizerThread::UnloadAppDomain(this, fRunFinalizers);
+
+ DWORD timeout = GetEEPolicy()->GetTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload);
+ ULONGLONG startTime = CLRGetTickCount64();
+ ULONGLONG elapsedTime = 0;
+ DWORD finalizerWait = 0;
+
+ while (FinalizerThread::GetUnloadingAppDomain() != NULL)
+ {
+
+ if (timeout != INFINITE)
+ {
+ elapsedTime = CLRGetTickCount64() - startTime;
+ }
+ if (timeout > elapsedTime)
+ {
+ finalizerWait = timeout - static_cast<DWORD>(elapsedTime);
+ }
+ FinalizerThread::FinalizerThreadWait(finalizerWait); //will set stage to finalized
+ if (timeout != INFINITE && FinalizerThread::GetUnloadingAppDomain() != NULL)
+ {
+ elapsedTime = CLRGetTickCount64() - startTime;
+ if (timeout <= elapsedTime)
+ {
+ SetRudeUnload();
+ // TODO: Consider escalation from RudeAppDomain
+ timeout = INFINITE;
+ }
+ }
+ }
+
+ tpAdUnloadHolder.SuppressRelease();
+ PerAppDomainTPCountList::ResetAppDomainTPCounts(GetTPIndex());
+
+ LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is finalized.\n",
+ GetId().m_dwId, this, GetFriendlyNameForLogging()));
+
+
+ AppDomainRefHolder This(this);
+ AddRef(); // Hold a reference so CloseDomain won't delete us yet
+ CloseDomain(); // Remove ourself from the list of app domains
+
+ // This needs to be done prior to destroying the handle tables below.
+ ReleaseDomainBoundInfo();
+
+ //
+ // It should be impossible to run non-mscorlib code in this domain now.
+ // Cleanup all of our roots except the handles. We do this to allow as many
+ // finalizers as possible to run correctly. If we delete the handles, they
+ // can't run.
+ //
+ if (!NingenEnabled())
+ {
+#ifdef FEATURE_REMOTING
+ EX_TRY
+ {
+ ADID domainId = GetId();
+ MethodDescCallSite domainUnloaded(METHOD__REMOTING_SERVICES__DOMAIN_UNLOADED);
+
+ ARG_SLOT args[1];
+ args[0] = domainId.m_dwId;
+ domainUnloaded.Call(args);
+ }
+ EX_CATCH
+ {
+ //we don't care if it fails
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // FEATURE_REMOTING
+ }
+
+ ClearGCRoots();
+ ClearGCHandles();
+
+ LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is cleared.\n",
+ GetId().m_dwId, this, GetFriendlyNameForLogging()));
+
+ if (fAsyncExit && fRunFinalizers)
+ {
+ GCX_PREEMP();
+ m_AssemblyCache.Clear();
+ ClearFusionContext();
+ ReleaseFiles();
+ if (!NingenEnabled())
+ {
+ AddMemoryPressure();
+ }
+ }
+ SystemDomain::System()->AddToDelayedUnloadList(this, fAsyncExit);
+ SystemDomain::SetUnloadDomainCleared();
+ if (m_dwId.m_dwId!=0)
+ SystemDomain::ReleaseAppDomainId(m_dwId);
+#ifdef PROFILING_SUPPORTED
+ // Always signal profile if present, even when failed.
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
+ GCX_PREEMP();
+ g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) this, S_OK);
+ END_PIN_PROFILER();
+ }
+#endif // PROFILING_SUPPORTED
+
+}
+
+void AppDomain::Close()
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is collected.\n",
+ GetId().m_dwId, this, GetFriendlyNameForLogging()));
+
+
+#if CHECK_APP_DOMAIN_LEAKS
+ if (g_pConfig->AppDomainLeaks())
+ // at this point shouldn't have any non-agile objects in the heap because we finalized all the non-agile ones.
+ SyncBlockCache::GetSyncBlockCache()->CheckForUnloadedInstances(GetIndex());
+#endif // CHECK_APP_DOMAIN_LEAKS
+ {
+ GCX_PREEMP();
+ RemoveMemoryPressure();
+ }
+ _ASSERTE(m_cRef>0); //should be alive at this point otherwise iterator can revive us and crash
+ {
+ SystemDomain::LockHolder lh; // Avoid races with AppDomainIterator
+ SetStage(STAGE_CLOSED);
+ }
+
+ // CONSIDER: move releasing remoting cache from managed code to here.
+}
+
+
+void AppDomain::ResetUnloadRequestThread(ADID Id)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_ANY;
+ PRECONDITION(!IsADUnloadHelperThread());
+ }
+ CONTRACTL_END;
+
+ GCX_COOP();
+ AppDomainFromIDHolder ad(Id, TRUE);
+ if(!ad.IsUnloaded() && ad->m_Stage < STAGE_UNLOAD_REQUESTED)
+ {
+ Thread *pThread = ad->GetUnloadRequestThread();
+ if(pThread==GetThread())
+ {
+ ad->m_dwThreadsStillInAppDomain=(ULONG)-1;
+
+ if(pThread)
+ {
+ if (pThread->GetUnloadBoundaryFrame() && pThread->IsBeingAbortedForADUnload())
+ {
+ pThread->UnmarkThreadForAbort(Thread::TAR_ADUnload);
+ }
+ ad->GetUnloadRequestThread()->ResetUnloadBoundaryFrame();
+ pThread->ResetBeginAbortedForADUnload();
+ }
+
+ ad->SetUnloadRequestThread(NULL);
+ }
+ }
+}
+
+
+int g_fADUnloadWorkerOK = -1;
+
+HRESULT AppDomain::UnloadById(ADID dwId, BOOL fSync,BOOL fExceptionsPassThrough)
+{
+ CONTRACTL
+ {
+ if(fExceptionsPassThrough) {THROWS;} else {NOTHROW;}
+ MODE_ANY;
+ if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_TRIGGERS);}
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ if (dwId==(ADID)DefaultADID)
+ return COR_E_CANNOTUNLOADAPPDOMAIN;
+
+ Thread *pThread = GetThread();
+
+ // Finalizer thread can not wait until AD unload is done,
+ // because AD unload is going to wait for Finalizer Thread.
+ if (fSync && pThread == FinalizerThread::GetFinalizerThread() &&
+ !pThread->HasThreadStateNC(Thread::TSNC_RaiseUnloadEvent))
+ return COR_E_CANNOTUNLOADAPPDOMAIN;
+
+
+ // AD unload helper thread should have been created.
+ _ASSERTE (g_fADUnloadWorkerOK == 1);
+
+ _ASSERTE (!IsADUnloadHelperThread());
+
+ BOOL fIsRaisingUnloadEvent = (pThread != NULL && pThread->HasThreadStateNC(Thread::TSNC_RaiseUnloadEvent));
+
+ if (fIsRaisingUnloadEvent)
+ {
+ AppDomainFromIDHolder pApp(dwId, TRUE, AppDomainFromIDHolder::SyncType_GC);
+
+ if (pApp.IsUnloaded() || ! pApp->CanLoadCode() || pApp->GetId().m_dwId == 0)
+ return COR_E_APPDOMAINUNLOADED;
+
+ pApp->EnableADUnloadWorker();
+
+ return S_FALSE;
+ }
+
+
+ ADUnloadSinkHolder pSink;
+
+ {
+ SystemDomain::LockHolder ulh;
+
+ AppDomainFromIDHolder pApp(dwId, TRUE, AppDomainFromIDHolder::SyncType_ADLock);
+
+ if (pApp.IsUnloaded() || ! pApp->CanLoadCode() || pApp->GetId().m_dwId == 0)
+ return COR_E_APPDOMAINUNLOADED;
+
+ if (g_fADUnloadWorkerOK != 1)
+ {
+ _ASSERTE(FALSE);
+ return E_UNEXPECTED;
+ }
+
+ if (!fSync)
+ {
+ pApp->EnableADUnloadWorker();
+ return S_OK;
+ }
+
+ pSink = pApp->PrepareForWaitUnloadCompletion();
+
+ pApp->EnableADUnloadWorker();
+
+ // release the holders - we don't care anymore if the appdomain is gone
+ }
+
+#ifdef FEATURE_TESTHOOKS
+ if (fExceptionsPassThrough)
+ {
+ CONTRACT_VIOLATION(FaultViolation);
+ return UnloadWaitNoCatch(dwId,pSink);
+ }
+#endif
+
+ return UnloadWait(dwId,pSink);
+}
+
+HRESULT AppDomain::UnloadWait(ADID Id, ADUnloadSink * pSink)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ MODE_ANY;
+ if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_TRIGGERS);}
+ }
+ CONTRACTL_END;
+
+ HRESULT hr=S_OK;
+ EX_TRY
+ {
+ // IF you ever try to change this to something not using events, please address the fact that
+ // AppDomain::StopEEAndUnwindThreads relies on that events are used.
+
+ pSink->WaitUnloadCompletion();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (SUCCEEDED(hr))
+ hr=pSink->GetUnloadResult();
+
+ if (FAILED(hr))
+ {
+ ResetUnloadRequestThread(Id);
+ }
+ return hr;
+}
+
+#ifdef FEATURE_TESTHOOKS
+HRESULT AppDomain::UnloadWaitNoCatch(ADID Id, ADUnloadSink * pSink)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_MODE_ANY;
+
+ Holder<ADID, DoNothing<ADID>, AppDomain::ResetUnloadRequestThread> resetUnloadHolder(Id);
+
+ // IF you ever try to change this to something not using events, please address the fact that
+ // AppDomain::StopEEAndUnwindThreads relies on that events are used.
+ pSink->WaitUnloadCompletion();
+
+ HRESULT hr = pSink->GetUnloadResult();
+
+ if (SUCCEEDED(hr))
+ resetUnloadHolder.SuppressRelease();
+
+ return hr;
+}
+#endif
+
+void AppDomain::Unload(BOOL fForceUnload)
+{
+ CONTRACTL
+ {
+ THROWS;
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_MULTICOREJIT
+
+ // Avoid profiling file is partially written in ASP.net scenarios, call it earlier
+ GetMulticoreJitManager().StopProfile(true);
+
+#endif
+
+ Thread *pThread = GetThread();
+
+
+ if (! fForceUnload && !g_pConfig->AppDomainUnload())
+ return;
+
+ EPolicyAction action;
+ EClrOperation operation;
+ if (!IsRudeUnload())
+ {
+ operation = OPR_AppDomainUnload;
+ }
+ else
+ {
+ operation = OPR_AppDomainRudeUnload;
+ }
+ action = GetEEPolicy()->GetDefaultAction(operation,NULL);
+ GetEEPolicy()->NotifyHostOnDefaultAction(operation,action);
+
+ switch (action)
+ {
+ case eUnloadAppDomain:
+ break;
+ case eRudeUnloadAppDomain:
+ SetRudeUnload();
+ break;
+ case eExitProcess:
+ case eFastExitProcess:
+ case eRudeExitProcess:
+ case eDisableRuntime:
+ EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_ADUNLOAD);
+ _ASSERTE (!"Should not get here");
+ break;
+ default:
+ break;
+ }
+
+#if (defined(_DEBUG) || defined(BREAK_ON_UNLOAD) || defined(AD_LOG_MEMORY) || defined(AD_SNAPSHOT))
+ static int unloadCount = 0;
+#endif
+
+#ifdef AD_LOG_MEMORY
+ {
+ GCX_PREEMP();
+ static int logMemory = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADLogMemory);
+ typedef void (__cdecl *LogItFcn) ( int );
+ static LogItFcn pLogIt = NULL;
+
+ if (logMemory && ! pLogIt)
+ {
+ HMODULE hMod = CLRLoadLibrary(W("mpdh.dll"));
+ if (hMod)
+ {
+ pLogIt = (LogItFcn)GetProcAddress(hMod, "logIt");
+ if (pLogIt)
+ {
+ pLogIt(9999);
+ pLogIt(9999);
+ }
+ }
+ }
+ }
+#endif // AD_LOG_MEMORY
+
+ if (IsDefaultDomain() && !IsSingleAppDomain())
+ COMPlusThrow(kCannotUnloadAppDomainException, IDS_EE_ADUNLOAD_DEFAULT);
+
+ _ASSERTE(CanUnload());
+
+ if (pThread == FinalizerThread::GetFinalizerThread() || GetUnloadRequestThread() == FinalizerThread::GetFinalizerThread())
+ COMPlusThrow(kCannotUnloadAppDomainException, IDS_EE_ADUNLOAD_IN_FINALIZER);
+
+ _ASSERTE(! SystemDomain::AppDomainBeingUnloaded());
+
+ // should not be running in this AD because unload spawned thread in default domain
+ if (!NingenEnabled())
+ {
+ _ASSERTE(!pThread->IsRunningIn(this, NULL));
+ }
+
+
+#ifdef APPDOMAIN_STATE
+ _ASSERTE_ALL_BUILDS("clr/src/VM/AppDomain.cpp", pThread->GetDomain()->IsDefaultDomain());
+#endif
+
+ LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Unloading domain [%d] %#08x %ls\n", GetId().m_dwId, this, GetFriendlyName()));
+
+ STRESS_LOG3 (LF_APPDOMAIN, LL_INFO100, "Unload domain [%d, %d] %p\n", GetId().m_dwId, GetIndex().m_dwIndex, this);
+
+ UnloadHolder hold(this);
+
+ SystemDomain::System()->SetUnloadRequestingThread(GetUnloadRequestThread());
+ SystemDomain::System()->SetUnloadingThread(pThread);
+
+
+#ifdef _DEBUG
+ static int dumpSB = -1;
+
+ if (dumpSB == -1)
+ dumpSB = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADDumpSB);
+
+ if (dumpSB > 1)
+ {
+ LogSpewAlways("Starting unload %3.3d\n", unloadCount);
+ DumpSyncBlockCache();
+ }
+#endif // _DEBUG
+
+ BOOL bForceGC=m_bForceGCOnUnload;
+
+#ifdef AD_LOG_MEMORY
+ if (pLogIt)
+ bForceGC=TRUE;
+#endif // AD_LOG_MEMORY
+
+#ifdef AD_SNAPSHOT
+ static int takeSnapShot = -1;
+
+ if (takeSnapShot == -1)
+ takeSnapShot = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADTakeSnapShot);
+
+ if (takeSnapShot)
+ bForceGC=TRUE;
+#endif // AD_SNAPSHOT
+
+#ifdef _DEBUG
+ if (dumpSB > 0)
+ bForceGC=TRUE;
+#endif // _DEBUG
+ static int cfgForceGC = -1;
+
+ if (cfgForceGC == -1)
+ cfgForceGC =!CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ADULazyMemoryRelease);
+
+ bForceGC=bForceGC||cfgForceGC;
+ AppDomainRefHolder This(this);
+ AddRef();
+
+ // Do the actual unloading
+ {
+ // We do not want other threads to abort the current one.
+ ThreadPreventAsyncHolder preventAsync;
+ Exit(TRUE, !bForceGC);
+ }
+ if(bForceGC)
+ {
+ GCHeap::GetGCHeap()->GarbageCollect();
+ FinalizerThread::FinalizerThreadWait();
+ SetStage(STAGE_COLLECTED);
+ Close(); //NOTHROW!
+ }
+
+#ifdef AD_LOG_MEMORY
+ if (pLogIt)
+ {
+ GCX_PREEMP();
+ pLogIt(unloadCount);
+ }
+#endif // AD_LOG_MEMORY
+
+#ifdef AD_SNAPSHOT
+ if (takeSnapShot)
+ {
+ char buffer[1024];
+ sprintf(buffer, "vadump -p %d -o > vadump.%d", GetCurrentProcessId(), unloadCount);
+ system(buffer);
+ sprintf(buffer, "umdh -p:%d -d -i:1 -f:umdh.%d", GetCurrentProcessId(), unloadCount);
+ system(buffer);
+ int takeDHSnapShot = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADTakeDHSnapShot);
+ if (takeDHSnapShot)
+ {
+ sprintf(buffer, "dh -p %d -s -g -h -b -f dh.%d", GetCurrentProcessId(), unloadCount);
+ system(buffer);
+ }
+ }
+#endif // AD_SNAPSHOT
+
+#ifdef _DEBUG
+ if (dumpSB > 0)
+ {
+ // do extra finalizer wait to remove any leftover sb entries
+ FinalizerThread::FinalizerThreadWait();
+ GCHeap::GetGCHeap()->GarbageCollect();
+ FinalizerThread::FinalizerThreadWait();
+ LogSpewAlways("Done unload %3.3d\n", unloadCount);
+ DumpSyncBlockCache();
+ ShutdownLogging();
+ WCHAR buffer[128];
+ swprintf_s(buffer, NumItems(buffer), W("DumpSB.%d"), unloadCount);
+ _ASSERTE(WszMoveFileEx(W("COMPLUS.LOG"), buffer, MOVEFILE_REPLACE_EXISTING));
+ // this will open a new file
+ InitLogging();
+ }
+#endif // _DEBUG
+}
+
+void AppDomain::ExceptionUnwind(Frame *pFrame)
+{
+ CONTRACTL
+ {
+ DISABLED(GC_TRIGGERS); // EEResourceException
+ DISABLED(THROWS); // EEResourceException
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind for %8.8x\n", pFrame));
+#if _DEBUG_ADUNLOAD
+ printf("%x AppDomain::ExceptionUnwind for %8.8p\n", GetThread()->GetThreadId(), pFrame);
+#endif
+ Thread *pThread = GetThread();
+ _ASSERTE(pThread);
+
+ if (! pThread->ShouldChangeAbortToUnload(pFrame))
+ {
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: not first transition or abort\n"));
+ return;
+ }
+
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: changing to unload\n"));
+
+ GCX_COOP();
+ OBJECTREF throwable = NULL;
+ EEResourceException e(kAppDomainUnloadedException, W("Remoting_AppDomainUnloaded_ThreadUnwound"));
+ throwable = e.GetThrowable();
+
+ // reset the exception to an AppDomainUnloadedException
+ if (throwable != NULL)
+ {
+ GetThread()->SafeSetThrowables(throwable);
+ }
+}
+
+BOOL AppDomain::StopEEAndUnwindThreads(unsigned int retryCount, BOOL *pFMarkUnloadRequestThread)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ Thread *pThread = NULL;
+ DWORD nThreadsNeedMoreWork=0;
+ if (retryCount != (unsigned int)-1 && retryCount < g_pConfig->AppDomainUnloadRetryCount())
+ {
+ Thread *pCurThread = GetThread();
+ if (pCurThread->CatchAtSafePoint())
+ pCurThread->PulseGCMode();
+
+ {
+ // We know which thread is not in the domain now. We just need to
+ // work on those threads. We do not need to suspend the runtime.
+ ThreadStoreLockHolder tsl;
+
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+ {
+ if (pThread == pCurThread)
+ {
+ continue;
+ }
+
+ if (pThread == FinalizerThread::GetFinalizerThread())
+ {
+ continue;
+ }
+
+ if (pThread->GetUnloadBoundaryFrame() == NULL)
+ {
+ continue;
+ }
+
+ // A thread may have UnloadBoundaryFrame set if
+ // 1. Being unloaded by AD unload helper thread
+ // 2. Escalation from OOM or SO triggers AD unload
+ // Here we only need to work on threads that are in the domain. If we work on other threads,
+ // those threads may be stucked in a finally, and we will not be able to escalate for them,
+ // therefore AD unload is blocked.
+ if (pThread->IsBeingAbortedForADUnload() ||
+ pThread == SystemDomain::System()->GetUnloadRequestingThread())
+ {
+ nThreadsNeedMoreWork++;
+ }
+
+ if (!(IsRudeUnload() ||
+ (pThread != SystemDomain::System()->GetUnloadRequestingThread() || OnlyOneThreadLeft())))
+ {
+ continue;
+ }
+
+ if ((pThread == SystemDomain::System()->GetUnloadRequestingThread()) && *pFMarkUnloadRequestThread)
+ {
+ // Mark thread for abortion only once; later on interrupt only
+ *pFMarkUnloadRequestThread = FALSE;
+ pThread->SetAbortRequest(m_fRudeUnload? EEPolicy::TA_Rude : EEPolicy::TA_V1Compatible);
+ }
+ else
+ {
+ if (pThread->m_State & Thread::TS_Interruptible)
+ {
+ pThread->UserInterrupt(Thread::TI_Abort);
+ }
+ }
+
+ if (pThread->PreemptiveGCDisabledOther())
+ {
+ #ifdef FEATURE_HIJACK
+ Thread::SuspendThreadResult str = pThread->SuspendThread();
+ if (str == Thread::STR_Success)
+ {
+ if (pThread->PreemptiveGCDisabledOther() &&
+ (!pThread->IsAbortInitiated() || pThread->IsRudeAbort()))
+ {
+ pThread->HandleJITCaseForAbort();
+ }
+ pThread->ResumeThread();
+ }
+ #endif
+ }
+ }
+ } // ThreadStoreLockHolder
+
+ if (nThreadsNeedMoreWork && CLRTaskHosted())
+ {
+ // In case a thread is the domain is blocked due to its scheduler being
+ // occupied by another thread.
+ Thread::ThreadAbortWatchDog();
+ }
+ m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork;
+ return !nThreadsNeedMoreWork;
+ }
+
+ // For now piggyback on the GC's suspend EE mechanism
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
+#ifdef _DEBUG
+ // <TODO>@todo: what to do with any threads that didn't stop?</TODO>
+ _ASSERTE(ThreadStore::s_pThreadStore->DbgBackgroundThreadCount() > 0);
+#endif // _DEBUG
+
+ int totalADCount = 0;
+ int finalizerADCount = 0;
+ pThread = NULL;
+
+ RuntimeExceptionKind reKind = kLastException;
+ UINT resId = 0;
+ SmallStackSString ssThreadId;
+
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+ {
+ // we already checked that we're not running in the unload domain
+ if (pThread == GetThread())
+ {
+ continue;
+ }
+
+#ifdef _DEBUG
+ void PrintStackTraceWithADToLog(Thread *pThread);
+ if (LoggingOn(LF_APPDOMAIN, LL_INFO100)) {
+ LOG((LF_APPDOMAIN, LL_INFO100, "\nStackTrace for %x\n", pThread->GetThreadId()));
+ PrintStackTraceWithADToLog(pThread);
+ }
+#endif // _DEBUG
+ int count = 0;
+ Frame *pFrame = pThread->GetFirstTransitionInto(this, &count);
+ if (! pFrame) {
+ _ASSERTE(count == 0);
+ if (pThread->IsBeingAbortedForADUnload())
+ {
+ pThread->ResetBeginAbortedForADUnload();
+ }
+ continue;
+ }
+
+ if (pThread != FinalizerThread::GetFinalizerThread())
+ {
+ totalADCount += count;
+ nThreadsNeedMoreWork++;
+ pThread->SetUnloadBoundaryFrame(pFrame);
+ }
+ else
+ {
+ finalizerADCount = count;
+ }
+
+ // don't setup the exception info for the unloading thread unless it's the last one in
+ if (retryCount != ((unsigned int) -1) && retryCount > g_pConfig->AppDomainUnloadRetryCount() && reKind == kLastException &&
+ (pThread != SystemDomain::System()->GetUnloadRequestingThread() || OnlyOneThreadLeft()))
+ {
+#ifdef AD_BREAK_ON_CANNOT_UNLOAD
+ static int breakOnCannotUnload = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADBreakOnCannotUnload);
+ if (breakOnCannotUnload)
+ _ASSERTE(!"Cannot unload AD");
+#endif // AD_BREAK_ON_CANNOT_UNLOAD
+ reKind = kCannotUnloadAppDomainException;
+ resId = IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD;
+ ssThreadId.Printf(W("%x"), pThread->GetThreadId());
+ STRESS_LOG2(LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads cannot stop thread %x with %d transitions\n", pThread->GetThreadId(), count);
+ // don't break out of this early or the assert totalADCount == (int)m_dwThreadEnterCount below will fire
+ // it's better to chew a little extra time here and make sure our counts are consistent
+ }
+ // only abort the thread requesting the unload if it's the last one in, that way it will get
+ // notification that the unload failed for some other thread not being aborted. And don't abort
+ // the finalizer thread - let it finish it's work as it's allowed to be in there. If it won't finish,
+ // then we will eventually get a CannotUnloadException on it.
+
+ if (pThread != FinalizerThread::GetFinalizerThread() &&
+ // If the domain is rudely unloaded, we will unwind the requesting thread out
+ // Rude unload is going to succeed, or escalated to disable runtime or higher.
+ (IsRudeUnload() ||
+ (pThread != SystemDomain::System()->GetUnloadRequestingThread() || OnlyOneThreadLeft())
+ )
+ )
+ {
+
+ STRESS_LOG2(LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n", pThread->GetThreadId(), count);
+ LOG((LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n", pThread->GetThreadId(), count));
+#if _DEBUG_ADUNLOAD
+ printf("AppDomain::UnwindThreads %x stopping %x with first frame %8.8p\n", GetThread()->GetThreadId(), pThread->GetThreadId(), pFrame);
+#endif
+ if (pThread == SystemDomain::System()->GetUnloadRequestingThread())
+ {
+ // Mark thread for abortion only once; later on interrupt only
+ *pFMarkUnloadRequestThread = FALSE;
+ }
+ pThread->SetAbortRequest(m_fRudeUnload? EEPolicy::TA_Rude : EEPolicy::TA_V1Compatible);
+ }
+ TESTHOOKCALL(UnwindingThreads(GetId().m_dwId)) ;
+ }
+ _ASSERTE(totalADCount + finalizerADCount == (int)m_dwThreadEnterCount);
+
+ //@TODO: This is intended to catch a stress bug. Remove when no longer needed.
+ if (totalADCount + finalizerADCount != (int)m_dwThreadEnterCount)
+ FreeBuildDebugBreak();
+
+ // if our count did get messed up, set it to whatever count we actually found in the domain to avoid looping
+ // or other problems related to incorrect count. This is very much a bug if this happens - a thread should always
+ // exit the domain gracefully.
+ // m_dwThreadEnterCount = totalADCount;
+
+ if (reKind != kLastException)
+ {
+ pThread = NULL;
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+ {
+ if (pThread->IsBeingAbortedForADUnload())
+ {
+ pThread->ResetBeginAbortedForADUnload();
+ }
+ }
+ }
+
+ // CommonTripThread will handle the abort for any threads that we've marked
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ if (reKind != kLastException)
+ COMPlusThrow(reKind, resId, ssThreadId.GetUnicode());
+
+ _ASSERTE((totalADCount==0 && nThreadsNeedMoreWork==0) ||(totalADCount!=0 && nThreadsNeedMoreWork!=0));
+
+ m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork;
+ return (totalADCount == 0);
+}
+
+void AppDomain::UnwindThreads()
+{
+ // This function should guarantee appdomain
+ // consistency even if it fails. Everything that is going
+ // to make the appdomain impossible to reenter
+ // should be factored out
+
+ // <TODO>@todo: need real synchronization here!!!</TODO>
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ int retryCount = -1;
+ m_dwThreadsStillInAppDomain=(ULONG)-1;
+ ULONGLONG startTime = CLRGetTickCount64();
+
+ if (GetEEPolicy()->GetDefaultAction(OPR_AppDomainUnload, NULL) == eRudeUnloadAppDomain &&
+ !IsRudeUnload())
+ {
+ GetEEPolicy()->NotifyHostOnDefaultAction(OPR_AppDomainUnload, eRudeUnloadAppDomain);
+ SetRudeUnload();
+ }
+
+ // Force threads to go through slow path during AD unload.
+ TSSuspendHolder shTrap;
+
+ BOOL fCurrentUnloadMode = IsRudeUnload();
+ BOOL fMarkUnloadRequestThread = TRUE;
+
+ // now wait for all the threads running in our AD to get out
+ do
+ {
+ DWORD timeout = GetEEPolicy()->GetTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload);
+ EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload, NULL);
+ if (timeout != INFINITE && action > eUnloadAppDomain) {
+ // Escalation policy specified.
+ ULONGLONG curTime = CLRGetTickCount64();
+ ULONGLONG elapseTime = curTime - startTime;
+ if (elapseTime > timeout)
+ {
+ // Escalate
+ switch (action)
+ {
+ case eRudeUnloadAppDomain:
+ GetEEPolicy()->NotifyHostOnTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload, action);
+ SetRudeUnload();
+ STRESS_LOG1(LF_APPDOMAIN, LL_INFO100,"Escalating to RADU, adid=%d",GetId().m_dwId);
+ break;
+ case eExitProcess:
+ case eFastExitProcess:
+ case eRudeExitProcess:
+ case eDisableRuntime:
+ GetEEPolicy()->NotifyHostOnTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload, action);
+ EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT);
+ _ASSERTE (!"Should not reach here");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+#ifdef _DEBUG
+ if (LoggingOn(LF_APPDOMAIN, LL_INFO100))
+ DumpADThreadTrack();
+#endif // _DEBUG
+ BOOL fNextUnloadMode = IsRudeUnload();
+ if (fCurrentUnloadMode != fNextUnloadMode)
+ {
+ // We have changed from normal unload to rude unload. We need to mark the thread
+ // with RudeAbort, but we can only do this safely if the runtime is suspended.
+ fCurrentUnloadMode = fNextUnloadMode;
+ retryCount = -1;
+ }
+ if (StopEEAndUnwindThreads(retryCount, &fMarkUnloadRequestThread))
+ break;
+ if (timeout != INFINITE)
+ {
+ // Turn off the timeout used by AD.
+ retryCount = 1;
+ }
+ else
+ {
+ // GCStress takes a long time to unwind, due to expensive creation of
+ // a threadabort exception.
+ if (!GCStress<cfg_any>::IsEnabled())
+ ++retryCount;
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads iteration %d waiting on thread count %d\n", retryCount, m_dwThreadEnterCount));
+#if _DEBUG_ADUNLOAD
+ printf("AppDomain::UnwindThreads iteration %d waiting on thread count %d\n", retryCount, m_dwThreadEnterCount);
+#endif
+ }
+
+ if (m_dwThreadEnterCount != 0)
+ {
+#ifdef _DEBUG
+ GetThread()->UserSleep(20);
+#else // !_DEBUG
+ GetThread()->UserSleep(10);
+#endif // !_DEBUG
+ }
+ }
+ while (TRUE) ;
+}
+
+void AppDomain::ClearGCHandles()
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ SetStage(STAGE_HANDLETABLE_NOACCESS);
+
+ GCHeap::GetGCHeap()->WaitUntilConcurrentGCComplete();
+
+ // Keep async pin handles alive by moving them to default domain
+ HandleAsyncPinHandles();
+
+ // Remove our handle table as a source of GC roots
+ HandleTableBucket *pBucket = m_hHandleTableBucket;
+
+#ifdef _DEBUG
+ if (((HandleTable *)(pBucket->pTable[0]))->uADIndex != m_dwIndex)
+ _ASSERTE (!"AD index mismatch");
+#endif // _DEBUG
+
+ Ref_RemoveHandleTableBucket(pBucket);
+}
+
+// When an AD is unloaded, we will release all objects in this AD.
+// If a future asynchronous operation, like io completion port function,
+// we need to keep the memory space fixed so that the gc heap is not corrupted.
+void AppDomain::HandleAsyncPinHandles()
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HandleTableBucket *pBucket = m_hHandleTableBucket;
+ // IO completion port picks IO job using FIFO. Here is how we know which AsyncPinHandle can be freed.
+ // 1. We mark all non-pending AsyncPinHandle with READYTOCLEAN.
+ // 2. We queue a dump Overlapped to the IO completion as a marker.
+ // 3. When the Overlapped is picked up by completion port, we wait until all previous IO jobs are processed.
+ // 4. Then we can delete all AsyncPinHandle marked with READYTOCLEAN.
+ HandleTableBucket *pBucketInDefault = SystemDomain::System()->DefaultDomain()->m_hHandleTableBucket;
+ Ref_RelocateAsyncPinHandles(pBucket, pBucketInDefault);
+
+ OverlappedDataObject::RequestCleanup();
+}
+
+void AppDomain::ClearGCRoots()
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ Thread *pThread = NULL;
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
+
+ // Tell the JIT managers to delete any entries in their structures. All the cooperative mode threads are stopped at
+ // this point, so only need to synchronize the preemptive mode threads.
+ ExecutionManager::Unload(GetLoaderAllocator());
+
+ while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL)
+ {
+ // Delete the thread local static store
+ pThread->DeleteThreadStaticData(this);
+
+#ifdef FEATURE_LEAK_CULTURE_INFO
+ pThread->ResetCultureForDomain(GetId());
+#endif // FEATURE_LEAK_CULTURE_INFO
+
+ // <TODO>@TODO: A pre-allocated AppDomainUnloaded exception might be better.</TODO>
+ if (m_hHandleTableBucket->Contains(pThread->m_LastThrownObjectHandle))
+ {
+ // Never delete a handle to a preallocated exception object.
+ if (!CLRException::IsPreallocatedExceptionHandle(pThread->m_LastThrownObjectHandle))
+ {
+ DestroyHandle(pThread->m_LastThrownObjectHandle);
+ }
+
+ pThread->m_LastThrownObjectHandle = NULL;
+ }
+
+ // Clear out the exceptions objects held by a thread.
+ pThread->GetExceptionState()->ClearThrowablesForUnload(m_hHandleTableBucket);
+ }
+
+ //delete them while we still have the runtime suspended
+ // This must be deleted before the loader heaps are deleted.
+ if (m_pMarshalingData != NULL)
+ {
+ delete m_pMarshalingData;
+ m_pMarshalingData = NULL;
+ }
+
+ if (m_pLargeHeapHandleTable != NULL)
+ {
+ delete m_pLargeHeapHandleTable;
+ m_pLargeHeapHandleTable = NULL;
+ }
+
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+}
+
+#ifdef _DEBUG
+
+void AppDomain::TrackADThreadEnter(Thread *pThread, Frame *pFrame)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ // REENTRANT
+ PRECONDITION(CheckPointer(pThread));
+ PRECONDITION(pFrame != (Frame*)(size_t) INVALID_POINTER_CD);
+ }
+ CONTRACTL_END;
+
+ while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
+ ;
+ if (m_pThreadTrackInfoList == NULL)
+ m_pThreadTrackInfoList = new (nothrow) ThreadTrackInfoList;
+ // If we don't assert here, we will AV in the for loop below
+ _ASSERTE(m_pThreadTrackInfoList);
+
+ ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
+
+ ThreadTrackInfo *pTrack = NULL;
+ int i;
+ for (i=0; i < pTrackList->Count(); i++) {
+ if ((*(pTrackList->Get(i)))->pThread == pThread) {
+ pTrack = *(pTrackList->Get(i));
+ break;
+ }
+ }
+ if (! pTrack) {
+ pTrack = new (nothrow) ThreadTrackInfo;
+ // If we don't assert here, we will AV in the for loop below.
+ _ASSERTE(pTrack);
+ pTrack->pThread = pThread;
+ ThreadTrackInfo **pSlot = pTrackList->Append();
+ *pSlot = pTrack;
+ }
+
+ InterlockedIncrement((LONG*)&m_dwThreadEnterCount);
+ Frame **pSlot;
+ if (pTrack)
+ {
+ pSlot = pTrack->frameStack.Insert(0);
+ *pSlot = pFrame;
+ }
+ int totThreads = 0;
+ for (i=0; i < pTrackList->Count(); i++)
+ totThreads += (*(pTrackList->Get(i)))->frameStack.Count();
+ _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
+
+ InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
+}
+
+
+void AppDomain::TrackADThreadExit(Thread *pThread, Frame *pFrame)
+{
+ CONTRACTL
+ {
+ if (GetThread()) {MODE_COOPERATIVE;}
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
+ ;
+ ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
+ _ASSERTE(pTrackList);
+ ThreadTrackInfo *pTrack = NULL;
+ int i;
+ for (i=0; i < pTrackList->Count(); i++)
+ {
+ if ((*(pTrackList->Get(i)))->pThread == pThread)
+ {
+ pTrack = *(pTrackList->Get(i));
+ break;
+ }
+ }
+ _ASSERTE(pTrack);
+ _ASSERTE(*(pTrack->frameStack.Get(0)) == pFrame);
+ pTrack->frameStack.Delete(0);
+ InterlockedDecrement((LONG*)&m_dwThreadEnterCount);
+
+ int totThreads = 0;
+ for (i=0; i < pTrackList->Count(); i++)
+ totThreads += (*(pTrackList->Get(i)))->frameStack.Count();
+ _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
+
+ InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
+}
+
+void AppDomain::DumpADThreadTrack()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
+ ;
+ ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
+ if (!pTrackList)
+ goto end;
+
+ {
+ LOG((LF_APPDOMAIN, LL_INFO10000, "\nThread dump of %d threads for [%d] %#08x %S\n",
+ m_dwThreadEnterCount, GetId().m_dwId, this, GetFriendlyNameForLogging()));
+ int totThreads = 0;
+ for (int i=0; i < pTrackList->Count(); i++)
+ {
+ ThreadTrackInfo *pTrack = *(pTrackList->Get(i));
+ if (pTrack->frameStack.Count()==0)
+ continue;
+ LOG((LF_APPDOMAIN, LL_INFO100, " ADEnterCount for %x is %d\n", pTrack->pThread->GetThreadId(), pTrack->frameStack.Count()));
+ totThreads += pTrack->frameStack.Count();
+ for (int j=0; j < pTrack->frameStack.Count(); j++)
+ LOG((LF_APPDOMAIN, LL_INFO100, " frame %8.8x\n", *(pTrack->frameStack.Get(j))));
+ }
+ _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
+ }
+end:
+ InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
+}
+#endif // _DEBUG
+
+#ifdef FEATURE_REMOTING
+OBJECTREF AppDomain::GetAppDomainProxy()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ OBJECTREF orProxy = CRemotingServices::CreateProxyForDomain(this);
+
+ _ASSERTE(orProxy->IsTransparentProxy());
+
+ return orProxy;
+}
+#endif
+
+#endif // CROSSGEN_COMPILE
+
+void *SharedDomain::operator new(size_t size, void *pInPlace)
+{
+ LIMITED_METHOD_CONTRACT;
+ return pInPlace;
+}
+
+void SharedDomain::operator delete(void *pMem)
+{
+ LIMITED_METHOD_CONTRACT;
+ // Do nothing - new() was in-place
+}
+
+
+void SharedDomain::Attach()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // Create the global SharedDomain and initialize it.
+ m_pSharedDomain = new (&g_pSharedDomainMemory[0]) SharedDomain();
+ SystemDomain::GetGlobalLoaderAllocator()->m_pDomain = m_pSharedDomain;
+ // This cannot fail since g_pSharedDomainMemory is a static array.
+ CONSISTENCY_CHECK(CheckPointer(m_pSharedDomain));
+
+ LOG((LF_CLASSLOADER,
+ LL_INFO10,
+ "Created shared domain at %p\n",
+ m_pSharedDomain));
+
+ // We need to initialize the memory pools etc. for the system domain.
+ m_pSharedDomain->Init(); // Setup the memory heaps
+
+ // allocate a Virtual Call Stub Manager for the shared domain
+ m_pSharedDomain->InitVSD();
+}
+
+#ifndef CROSSGEN_COMPILE
+void SharedDomain::Detach()
+{
+ if (m_pSharedDomain)
+ {
+ m_pSharedDomain->Terminate();
+ delete m_pSharedDomain;
+ m_pSharedDomain = NULL;
+ }
+}
+#endif // CROSSGEN_COMPILE
+
+#endif // !DACCESS_COMPILE
+
+SharedDomain *SharedDomain::GetDomain()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return m_pSharedDomain;
+}
+
+#ifndef DACCESS_COMPILE
+
+#define INITIAL_ASSEMBLY_MAP_SIZE 17
+void SharedDomain::Init()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ BaseDomain::Init();
+
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ m_FileCreateLock.Init(CrstSharedAssemblyCreate, CRST_DEFAULT,TRUE);
+
+ LockOwner lock = { &m_DomainCrst, IsOwnerOfCrst };
+ m_assemblyMap.Init(INITIAL_ASSEMBLY_MAP_SIZE, CompareSharedAssembly, TRUE, &lock);
+#endif // FEATURE_LOADER_OPTIMIZATION
+
+ ETW::LoaderLog::DomainLoad(this);
+}
+
+#ifndef CROSSGEN_COMPILE
+void SharedDomain::Terminate()
+{
+ // make sure we delete the StringLiteralMap before unloading
+ // the asemblies since the string literal map entries can
+ // point to metadata string literals.
+ GetLoaderAllocator()->CleanupStringLiteralMap();
+
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ PtrHashMap::PtrIterator i = m_assemblyMap.begin();
+
+ while (!i.end())
+ {
+ Assembly *pAssembly = (Assembly*) i.GetValue();
+ delete pAssembly;
+ ++i;
+ }
+
+ ListLockEntry* pElement;
+ pElement = m_FileCreateLock.Pop(TRUE);
+ while (pElement)
+ {
+#ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
+ _ASSERTE (dbg_fDrasticShutdown || g_fInControlC);
+#endif
+ delete(pElement);
+ pElement = (FileLoadLock*) m_FileCreateLock.Pop(TRUE);
+ }
+ m_FileCreateLock.Destroy();
+#endif // FEATURE_LOADER_OPTIMIZATION
+ BaseDomain::Terminate();
+}
+#endif // CROSSGEN_COMPILE
+
+
+
+#ifdef FEATURE_LOADER_OPTIMIZATION
+
+BOOL SharedDomain::CompareSharedAssembly(UPTR u1, UPTR u2)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // This is the input to the lookup
+ SharedAssemblyLocator *pLocator = (SharedAssemblyLocator *) (u1<<1);
+
+ // This is the value stored in the table
+ Assembly *pAssembly = (Assembly *) u2;
+ if (pLocator->GetType()==SharedAssemblyLocator::DOMAINASSEMBLY)
+ {
+ if (!pAssembly->GetManifestFile()->Equals(pLocator->GetDomainAssembly()->GetFile()))
+ return FALSE;
+
+ return pAssembly->CanBeShared(pLocator->GetDomainAssembly());
+ }
+ else
+ if (pLocator->GetType()==SharedAssemblyLocator::PEASSEMBLY)
+ return pAssembly->GetManifestFile()->Equals(pLocator->GetPEAssembly());
+ else
+ if (pLocator->GetType()==SharedAssemblyLocator::PEASSEMBLYEXACT)
+ return pAssembly->GetManifestFile() == pLocator->GetPEAssembly();
+ _ASSERTE(!"Unexpected type of assembly locator");
+ return FALSE;
+}
+
+DWORD SharedAssemblyLocator::Hash()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+ if (m_type==DOMAINASSEMBLY)
+ return GetDomainAssembly()->HashIdentity();
+ if (m_type==PEASSEMBLY||m_type==PEASSEMBLYEXACT)
+ return GetPEAssembly()->HashIdentity();
+ _ASSERTE(!"Unexpected type of assembly locator");
+ return 0;
+}
+
+Assembly * SharedDomain::FindShareableAssembly(SharedAssemblyLocator * pLocator)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ Assembly * match= (Assembly *) m_assemblyMap.LookupValue(pLocator->Hash(), pLocator);
+ if (match != (Assembly *) INVALIDENTRY)
+ return match;
+ else
+ return NULL;
+}
+
+SIZE_T SharedDomain::GetShareableAssemblyCount()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_assemblyMap.GetCount();
+}
+
+void SharedDomain::AddShareableAssembly(Assembly * pAssembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ // We have a lock on the file. There should be no races to add the same assembly.
+
+ {
+ LockHolder holder(this);
+
+ EX_TRY
+ {
+ pAssembly->SetIsTenured();
+ m_assemblyMap.InsertValue(pAssembly->HashIdentity(), pAssembly);
+ }
+ EX_HOOK
+ {
+ // There was an error adding the assembly to the assembly hash (probably an OOM),
+ // so we need to unset the tenured bit so that correct cleanup can happen.
+ pAssembly->UnsetIsTenured();
+ }
+ EX_END_HOOK
+ }
+
+ LOG((LF_CODESHARING,
+ LL_INFO100,
+ "Successfully added shareable assembly \"%s\".\n",
+ pAssembly->GetManifestFile()->GetSimpleName()));
+}
+
+#endif // FEATURE_LOADER_OPTIMIZATION
+#endif // !DACCESS_COMPILE
+
+DWORD DomainLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex /*=(DWORD)-1*/)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ { // SO tolerance exception for debug-only assertion.
+ CONTRACT_VIOLATION(SOToleranceViolation);
+ CONSISTENCY_CHECK(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
+ }
+
+ if (pMT->IsDynamicStatics())
+ {
+ _ASSERTE(!pMT->ContainsGenericVariables());
+ DWORD dynamicClassID = pMT->GetModuleDynamicEntryID();
+ if(m_aDynamicEntries <= dynamicClassID)
+ return FALSE;
+ return (m_pDynamicClassTable[dynamicClassID].m_dwFlags);
+ }
+ else
+ {
+ if (iClassIndex == (DWORD)-1)
+ iClassIndex = pMT->GetClassIndex();
+ return GetPrecomputedStaticsClassData()[iClassIndex];
+ }
+}
+
+#ifndef DACCESS_COMPILE
+
+void DomainLocalModule::SetClassInitialized(MethodTable* pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
+
+ _ASSERTE(!IsClassInitialized(pMT));
+ _ASSERTE(!IsClassInitError(pMT));
+
+ SetClassFlags(pMT, ClassInitFlags::INITIALIZED_FLAG);
+}
+
+void DomainLocalModule::SetClassInitError(MethodTable* pMT)
+{
+ WRAPPER_NO_CONTRACT;
+
+ BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
+
+ SetClassFlags(pMT, ClassInitFlags::ERROR_FLAG);
+}
+
+void DomainLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
+ } CONTRACTL_END;
+
+ if (pMT->IsDynamicStatics())
+ {
+ _ASSERTE(!pMT->ContainsGenericVariables());
+ DWORD dwID = pMT->GetModuleDynamicEntryID();
+ EnsureDynamicClassIndex(dwID);
+ m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags;
+ }
+ else
+ {
+ GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags;
+ }
+}
+
+void DomainLocalModule::EnsureDynamicClassIndex(DWORD dwID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
+ }
+ CONTRACTL_END;
+
+ if (dwID < m_aDynamicEntries)
+ {
+ _ASSERTE(m_pDynamicClassTable.Load() != NULL);
+ return;
+ }
+
+ SIZE_T aDynamicEntries = max(16, m_aDynamicEntries.Load());
+ while (aDynamicEntries <= dwID)
+ {
+ aDynamicEntries *= 2;
+ }
+
+ DynamicClassInfo* pNewDynamicClassTable;
+ pNewDynamicClassTable = (DynamicClassInfo*)
+ (void*)GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(
+ S_SIZE_T(sizeof(DynamicClassInfo)) * S_SIZE_T(aDynamicEntries));
+
+ memcpy(pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * m_aDynamicEntries);
+
+ // Note: Memory allocated on loader heap is zero filled
+ // memset(pNewDynamicClassTable + m_aDynamicEntries, 0, (aDynamicEntries - m_aDynamicEntries) * sizeof(DynamicClassInfo));
+
+ _ASSERTE(m_aDynamicEntries%2 == 0);
+
+ // Commit new dynamic table. The lock-free helpers depend on the order.
+ MemoryBarrier();
+ m_pDynamicClassTable = pNewDynamicClassTable;
+ MemoryBarrier();
+ m_aDynamicEntries = aDynamicEntries;
+}
+
+#ifndef CROSSGEN_COMPILE
+void DomainLocalModule::AllocateDynamicClass(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(!pMT->ContainsGenericVariables());
+ _ASSERTE(!pMT->IsSharedByGenericInstantiations());
+ _ASSERTE(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
+ _ASSERTE(pMT->IsDynamicStatics());
+
+ DWORD dynamicEntryIDIndex = pMT->GetModuleDynamicEntryID();
+
+ EnsureDynamicClassIndex(dynamicEntryIDIndex);
+
+ _ASSERTE(m_aDynamicEntries > dynamicEntryIDIndex);
+
+ EEClass *pClass = pMT->GetClass();
+
+ DWORD dwStaticBytes = pClass->GetNonGCRegularStaticFieldBytes();
+ DWORD dwNumHandleStatics = pClass->GetNumHandleRegularStatics();
+
+ _ASSERTE(!IsClassAllocated(pMT));
+ _ASSERTE(!IsClassInitialized(pMT));
+ _ASSERTE(!IsClassInitError(pMT));
+
+ DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry;
+
+ // We need this check because maybe a class had a cctor but no statics
+ if (dwStaticBytes > 0 || dwNumHandleStatics > 0)
+ {
+ if (pDynamicStatics == NULL)
+ {
+ LoaderHeap * pLoaderAllocator = GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap();
+
+ if (pMT->Collectible())
+ {
+ pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(sizeof(CollectibleDynamicEntry)));
+ }
+ else
+ {
+ SIZE_T dynamicEntrySize = DynamicEntry::GetOffsetOfDataBlob() + dwStaticBytes;
+
+#ifdef FEATURE_64BIT_ALIGNMENT
+ // Allocate memory with extra alignment only if it is really necessary
+ if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE)
+ {
+ static_assert_no_msg(sizeof(NormalDynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0);
+ pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocAlignedMem(dynamicEntrySize, MAX_PRIMITIVE_FIELD_SIZE);
+ }
+ else
+#endif
+ pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(dynamicEntrySize));
+ }
+
+ // Note: Memory allocated on loader heap is zero filled
+
+ m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry = pDynamicStatics;
+ }
+
+ if (pMT->Collectible() && (dwStaticBytes != 0))
+ {
+ GCX_COOP();
+ OBJECTREF nongcStaticsArray = NULL;
+ GCPROTECT_BEGIN(nongcStaticsArray);
+#ifdef FEATURE_64BIT_ALIGNMENT
+ // Allocate memory with extra alignment only if it is really necessary
+ if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE)
+ nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_I8, (dwStaticBytes + (sizeof(CLR_I8)-1)) / (sizeof(CLR_I8)));
+ else
+#endif
+ nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_U1, dwStaticBytes);
+ ((CollectibleDynamicEntry *)pDynamicStatics)->m_hNonGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(nongcStaticsArray);
+ GCPROTECT_END();
+ }
+ if (dwNumHandleStatics > 0)
+ {
+ if (!pMT->Collectible())
+ {
+ GetAppDomain()->AllocateStaticFieldObjRefPtrs(dwNumHandleStatics,
+ &((NormalDynamicEntry *)pDynamicStatics)->m_pGCStatics);
+ }
+ else
+ {
+ GCX_COOP();
+ OBJECTREF gcStaticsArray = NULL;
+ GCPROTECT_BEGIN(gcStaticsArray);
+ gcStaticsArray = AllocateObjectArray(dwNumHandleStatics, g_pObjectClass);
+ ((CollectibleDynamicEntry *)pDynamicStatics)->m_hGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(gcStaticsArray);
+ GCPROTECT_END();
+ }
+ }
+ }
+}
+
+
+void DomainLocalModule::PopulateClass(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(!pMT->ContainsGenericVariables());
+
+ // <todo> the only work actually done here for non-dynamics is the freezing related work.
+ // See if we can eliminate this and make this a dynamic-only path </todo>
+ DWORD iClassIndex = pMT->GetClassIndex();
+
+ if (!IsClassAllocated(pMT, iClassIndex))
+ {
+ BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
+
+ if (!IsClassAllocated(pMT, iClassIndex))
+ {
+ // Allocate dynamic space if necessary
+ if (pMT->IsDynamicStatics())
+ AllocateDynamicClass(pMT);
+
+ // determine flags to set on the statics block
+ DWORD dwFlags = ClassInitFlags::ALLOCATECLASS_FLAG;
+
+ if (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics())
+ {
+ _ASSERTE(!IsClassInitialized(pMT));
+ _ASSERTE(!IsClassInitError(pMT));
+ dwFlags |= ClassInitFlags::INITIALIZED_FLAG;
+ }
+
+ if (pMT->Collectible())
+ {
+ dwFlags |= ClassInitFlags::COLLECTIBLE_FLAG;
+ }
+
+ // Set all flags at the same time to avoid races
+ SetClassFlags(pMT, dwFlags);
+ }
+ }
+
+ return;
+}
+#endif // CROSSGEN_COMPILE
+
+void DomainLocalBlock::EnsureModuleIndex(ModuleIndex index)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(m_pDomain->OwnDomainLocalBlockLock());
+ }
+ CONTRACTL_END;
+
+ if (m_aModuleIndices > index.m_dwIndex)
+ {
+ _ASSERTE(m_pModuleSlots != NULL);
+ return;
+ }
+
+ SIZE_T aModuleIndices = max(16, m_aModuleIndices);
+ while (aModuleIndices <= index.m_dwIndex)
+ {
+ aModuleIndices *= 2;
+ }
+
+ PTR_DomainLocalModule* pNewModuleSlots = (PTR_DomainLocalModule*) (void*)m_pDomain->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PTR_DomainLocalModule)) * S_SIZE_T(aModuleIndices));
+
+ memcpy(pNewModuleSlots, m_pModuleSlots, sizeof(SIZE_T)*m_aModuleIndices);
+
+ // Note: Memory allocated on loader heap is zero filled
+ // memset(pNewModuleSlots + m_aModuleIndices, 0 , (aModuleIndices - m_aModuleIndices)*sizeof(PTR_DomainLocalModule) );
+
+ // Commit new table. The lock-free helpers depend on the order.
+ MemoryBarrier();
+ m_pModuleSlots = pNewModuleSlots;
+ MemoryBarrier();
+ m_aModuleIndices = aModuleIndices;
+
+}
+
+void DomainLocalBlock::SetModuleSlot(ModuleIndex index, PTR_DomainLocalModule pLocalModule)
+{
+ // Need to synchronize with table growth in this domain
+ BaseDomain::DomainLocalBlockLockHolder lh(m_pDomain);
+
+ EnsureModuleIndex(index);
+
+ _ASSERTE(index.m_dwIndex < m_aModuleIndices);
+
+ // We would like this assert here, unfortunately, loading a module in this appdomain can fail
+ // after here and we will keep the module around and reuse the slot when we retry (if
+ // the failure happened due to a transient error, such as OOM). In that case the slot wont
+ // be null.
+ //_ASSERTE(m_pModuleSlots[index.m_dwIndex] == 0);
+
+ m_pModuleSlots[index.m_dwIndex] = pLocalModule;
+}
+
+#ifndef CROSSGEN_COMPILE
+
+DomainAssembly* AppDomain::RaiseTypeResolveEventThrowing(DomainAssembly* pAssembly, LPCSTR szName, ASSEMBLYREF *pResultingAssemblyRef)
+{
+ CONTRACTL
+ {
+ MODE_ANY;
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+
+
+ DomainAssembly* pResolvedAssembly = NULL;
+ _ASSERTE(strcmp(szName, g_AppDomainClassName));
+
+ GCX_COOP();
+
+ struct _gc {
+ OBJECTREF AppDomainRef;
+ OBJECTREF AssemblyRef;
+ STRINGREF str;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
+ {
+ if (pAssembly != NULL)
+ gc.AssemblyRef = pAssembly->GetExposedAssemblyObject();
+
+ MethodDescCallSite onTypeResolve(METHOD__APP_DOMAIN__ON_TYPE_RESOLVE, &gc.AppDomainRef);
+
+ gc.str = StringObject::NewString(szName);
+ ARG_SLOT args[3] =
+ {
+ ObjToArgSlot(gc.AppDomainRef),
+ ObjToArgSlot(gc.AssemblyRef),
+ ObjToArgSlot(gc.str)
+ };
+ ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onTypeResolve.Call_RetOBJECTREF(args);
+
+ if (ResultingAssemblyRef != NULL)
+ {
+ pResolvedAssembly = ResultingAssemblyRef->GetDomainAssembly();
+
+ if (pResultingAssemblyRef)
+ *pResultingAssemblyRef = ResultingAssemblyRef;
+ else
+ {
+ if (pResolvedAssembly->IsCollectible())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible"));
+ }
+ }
+ }
+ }
+ GCPROTECT_END();
+
+ return pResolvedAssembly;
+}
+
+
+Assembly* AppDomain::RaiseResourceResolveEvent(DomainAssembly* pAssembly, LPCSTR szName)
+{
+ CONTRACT(Assembly*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ Assembly* pResolvedAssembly = NULL;
+
+ GCX_COOP();
+
+ struct _gc {
+ OBJECTREF AppDomainRef;
+ OBJECTREF AssemblyRef;
+ STRINGREF str;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
+ {
+ if (pAssembly != NULL)
+ gc.AssemblyRef=pAssembly->GetExposedAssemblyObject();
+
+ MethodDescCallSite onResourceResolve(METHOD__APP_DOMAIN__ON_RESOURCE_RESOLVE, &gc.AppDomainRef);
+ gc.str = StringObject::NewString(szName);
+ ARG_SLOT args[3] =
+ {
+ ObjToArgSlot(gc.AppDomainRef),
+ ObjToArgSlot(gc.AssemblyRef),
+ ObjToArgSlot(gc.str)
+ };
+ ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onResourceResolve.Call_RetOBJECTREF(args);
+ if (ResultingAssemblyRef != NULL)
+ {
+ pResolvedAssembly = ResultingAssemblyRef->GetAssembly();
+ if (pResolvedAssembly->IsCollectible())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
+ }
+ }
+ }
+ GCPROTECT_END();
+
+ RETURN pResolvedAssembly;
+}
+
+
+Assembly *
+AppDomain::RaiseAssemblyResolveEvent(
+ AssemblySpec * pSpec,
+ BOOL fIntrospection,
+ BOOL fPreBind)
+{
+ CONTRACT(Assembly*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACT_END;
+
+ BinderMethodID methodId;
+ StackSString ssName;
+ pSpec->GetFileOrDisplayName(0, ssName);
+
+#ifdef FEATURE_REFLECTION_ONLY_LOAD
+ if ( (!fIntrospection) && (!fPreBind) )
+ {
+ methodId = METHOD__APP_DOMAIN__ON_ASSEMBLY_RESOLVE; // post-bind execution event (the classic V1.0 event)
+ }
+ else if ((!fIntrospection) && fPreBind)
+ {
+ RETURN NULL; // There is currently no prebind execution resolve event
+ }
+ else if (fIntrospection && !fPreBind)
+ {
+ RETURN NULL; // There is currently no post-bind introspection resolve event
+ }
+ else
+ {
+ _ASSERTE( fIntrospection && fPreBind );
+ methodId = METHOD__APP_DOMAIN__ON_REFLECTION_ONLY_ASSEMBLY_RESOLVE; // event for introspection assemblies
+ }
+#else // FEATURE_REFLECTION_ONLY_LOAD
+ if (!fPreBind)
+ {
+ methodId = METHOD__APP_DOMAIN__ON_ASSEMBLY_RESOLVE; // post-bind execution event (the classic V1.0 event)
+ }
+ else
+ {
+ RETURN NULL;
+ }
+
+#endif // FEATURE_REFLECTION_ONLY_LOAD
+
+ // Elevate threads allowed loading level. This allows the host to load an assembly even in a restricted
+ // condition. Note, however, that this exposes us to possible recursion failures, if the host tries to
+ // load the assemblies currently being loaded. (Such cases would then throw an exception.)
+
+ OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE);
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+
+ GCX_COOP();
+
+ Assembly* pAssembly = NULL;
+
+ struct _gc {
+ OBJECTREF AppDomainRef;
+ OBJECTREF AssemblyRef;
+ STRINGREF str;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
+ {
+ if (pSpec->GetParentAssembly() != NULL)
+ {
+ if ( pSpec->IsIntrospectionOnly()
+#ifdef FEATURE_FUSION
+ || pSpec->GetParentLoadContext() == LOADCTX_TYPE_UNKNOWN
+#endif
+ )
+ {
+ gc.AssemblyRef=pSpec->GetParentAssembly()->GetExposedAssemblyObject();
+ }
+ }
+ MethodDescCallSite onAssemblyResolve(methodId, &gc.AppDomainRef);
+
+ gc.str = StringObject::NewString(ssName);
+ ARG_SLOT args[3] = {
+ ObjToArgSlot(gc.AppDomainRef),
+ ObjToArgSlot(gc.AssemblyRef),
+ ObjToArgSlot(gc.str)
+ };
+
+ ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onAssemblyResolve.Call_RetOBJECTREF(args);
+
+ if (ResultingAssemblyRef != NULL)
+ {
+ pAssembly = ResultingAssemblyRef->GetAssembly();
+ if (pAssembly->IsCollectible())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
+ }
+ }
+ }
+ GCPROTECT_END();
+
+ if (pAssembly != NULL)
+ {
+ if ((!(pAssembly->IsIntrospectionOnly())) != (!fIntrospection))
+ {
+ // Cannot return an introspection assembly from an execution callback or vice-versa
+ COMPlusThrow(kFileLoadException, pAssembly->IsIntrospectionOnly() ? IDS_CLASSLOAD_ASSEMBLY_RESOLVE_RETURNED_INTROSPECTION : IDS_CLASSLOAD_ASSEMBLY_RESOLVE_RETURNED_EXECUTION);
+ }
+
+ // Check that the public key token matches the one specified in the spec
+ // MatchPublicKeys throws as appropriate
+ pSpec->MatchPublicKeys(pAssembly);
+ }
+
+ RETURN pAssembly;
+} // AppDomain::RaiseAssemblyResolveEvent
+
+#ifndef FEATURE_CORECLR
+
+//---------------------------------------------------------------------------------------
+//
+// Ask the AppDomainManager for the entry assembly of the application
+//
+// Note:
+// Most AppDomainManagers will fall back on the root assembly for the domain, so we need
+// to make sure this is set before we call through to the AppDomainManager itself.
+//
+
+Assembly *AppDomain::GetAppDomainManagerEntryAssembly()
+{
+ CONTRACT(Assembly *)
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(HasAppDomainManagerInfo());
+ PRECONDITION(CheckPointer(m_pRootAssembly));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ GCX_COOP();
+
+ Assembly *pEntryAssembly = NULL;
+
+ struct
+ {
+ APPDOMAINREF orDomain;
+ OBJECTREF orAppDomainManager;
+ ASSEMBLYREF orEntryAssembly;
+ }
+ gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+
+ gc.orDomain = static_cast<APPDOMAINREF>(GetExposedObject());
+ gc.orAppDomainManager = gc.orDomain->GetAppDomainManager();
+ _ASSERTE(gc.orAppDomainManager != NULL);
+
+ MethodDescCallSite getEntryAssembly(METHOD__APPDOMAIN_MANAGER__GET_ENTRY_ASSEMBLY, &gc.orAppDomainManager);
+ ARG_SLOT argThis = ObjToArgSlot(gc.orAppDomainManager);
+ gc.orEntryAssembly = static_cast<ASSEMBLYREF>(getEntryAssembly.Call_RetOBJECTREF(&argThis));
+
+ if (gc.orEntryAssembly != NULL)
+ {
+ pEntryAssembly = gc.orEntryAssembly->GetAssembly();
+ }
+
+ GCPROTECT_END();
+
+ // If the AppDomainManager did not return an entry assembly, we'll assume the default assembly
+ if (pEntryAssembly == NULL)
+ {
+ pEntryAssembly = m_pRootAssembly;
+ }
+
+ RETURN(pEntryAssembly);
+}
+
+#endif // !FEATURE_CORECLR
+
+//---------------------------------------------------------------------------------------
+//
+// Determine the type of AppDomainManager to use for the default AppDomain
+//
+// Notes:
+// v2.0 of the CLR used environment variables APPDOMAIN_MANAGER_ASM and APPDOMAIN_MANAGER_TYPE to set the
+// domain manager. For compatibility these are still supported, along with appDomainManagerAsm and
+// appDomainManagerType config file switches. If the config switches are supplied, the entry point must be
+// fully trusted.
+//
+
+void AppDomain::InitializeDefaultDomainManager()
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ PRECONDITION(GetId().m_dwId == DefaultADID);
+ PRECONDITION(!HasAppDomainManagerInfo());
+ }
+ CONTRACTL_END;
+
+ //
+ // The AppDomainManager for the default domain can be specified by:
+ // 1. Native hosting API
+ // 2. Application config file if the application is fully trusted
+ // 3. Environment variables
+ //
+
+
+ if (CorHost2::HasAppDomainManagerInfo())
+ {
+ SetAppDomainManagerInfo(CorHost2::GetAppDomainManagerAsm(),
+ CorHost2::GetAppDomainManagerType(),
+ CorHost2::GetAppDomainManagerInitializeNewDomainFlags());
+ m_fAppDomainManagerSetInConfig = FALSE;
+
+ LOG((LF_APPDOMAIN, LL_INFO10, "Setting default AppDomainManager '%S', '%S' from hosting API.\n", GetAppDomainManagerAsm(), GetAppDomainManagerType()));
+ }
+#ifndef FEATURE_CORECLR
+ else
+ {
+ CLRConfigStringHolder wszConfigAppDomainManagerAssembly(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_AppDomainManagerAsm));
+ CLRConfigStringHolder wszConfigAppDomainManagerType(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_AppDomainManagerType));
+
+ if (wszConfigAppDomainManagerAssembly != NULL &&
+ wszConfigAppDomainManagerType != NULL)
+ {
+ SetAppDomainManagerInfo(wszConfigAppDomainManagerAssembly,
+ wszConfigAppDomainManagerType,
+ eInitializeNewDomainFlags_None);
+ m_fAppDomainManagerSetInConfig = TRUE;
+
+ LOG((LF_APPDOMAIN, LL_INFO10, "Setting default AppDomainManager '%S', '%S' from application config file.\n", GetAppDomainManagerAsm(), GetAppDomainManagerType()));
+ }
+ else
+ {
+ CLRConfigStringHolder wszEnvironmentAppDomainManagerAssembly(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LEGACY_APPDOMAIN_MANAGER_ASM));
+ CLRConfigStringHolder wszEnvironmentAppDomainManagerType(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LEGACY_APPDOMAIN_MANAGER_TYPE));
+
+ if (wszEnvironmentAppDomainManagerAssembly != NULL &&
+ wszEnvironmentAppDomainManagerType != NULL)
+ {
+ SetAppDomainManagerInfo(wszEnvironmentAppDomainManagerAssembly,
+ wszEnvironmentAppDomainManagerType,
+ eInitializeNewDomainFlags_None);
+ m_fAppDomainManagerSetInConfig = FALSE;
+
+ LOG((LF_APPDOMAIN, LL_INFO10, "Setting default AppDomainManager '%S', '%S' from environment variables.\n", GetAppDomainManagerAsm(), GetAppDomainManagerType()));
+
+ // Reset the environmetn variables so that child processes do not inherit our domain manager
+ // by default.
+ WszSetEnvironmentVariable(CLRConfig::EXTERNAL_LEGACY_APPDOMAIN_MANAGER_ASM.name, NULL);
+ WszSetEnvironmentVariable(CLRConfig::EXTERNAL_LEGACY_APPDOMAIN_MANAGER_TYPE.name, NULL);
+ }
+ }
+ }
+#endif // !FEATURE_CORECLR
+
+ // If we found an AppDomain manager to use, create and initialize it
+ // Otherwise, initialize the config flags.
+ if (HasAppDomainManagerInfo())
+ {
+ // If the initialization flags promise that the domain manager isn't going to modify security, then do a
+ // pre-resolution of the domain now so that we can do some basic verification of the state later. We
+ // don't care about the actual result now, just that the resolution took place to compare against later.
+ if (GetAppDomainManagerInitializeNewDomainFlags() & eInitializeNewDomainFlags_NoSecurityChanges)
+ {
+ BOOL fIsFullyTrusted;
+ BOOL fIsHomogeneous;
+ GetSecurityDescriptor()->PreResolve(&fIsFullyTrusted, &fIsHomogeneous);
+ }
+
+ OBJECTREF orThis = GetExposedObject();
+ GCPROTECT_BEGIN(orThis);
+
+ MethodDescCallSite createDomainManager(METHOD__APP_DOMAIN__CREATE_APP_DOMAIN_MANAGER);
+ ARG_SLOT args[] =
+ {
+ ObjToArgSlot(orThis)
+ };
+
+ createDomainManager.Call(args);
+
+ GCPROTECT_END();
+ }
+ else
+ {
+ OBJECTREF orThis = GetExposedObject();
+ GCPROTECT_BEGIN(orThis);
+
+ MethodDescCallSite initCompatFlags(METHOD__APP_DOMAIN__INITIALIZE_COMPATIBILITY_FLAGS);
+ ARG_SLOT args[] =
+ {
+ ObjToArgSlot(orThis)
+ };
+
+ initCompatFlags.Call(args);
+
+ GCPROTECT_END();
+ }
+}
+
+#ifdef FEATURE_CLICKONCE
+
+//---------------------------------------------------------------------------------------
+//
+// If we are launching a ClickOnce application, setup the default domain with the deails
+// of the application.
+//
+
+void AppDomain::InitializeDefaultClickOnceDomain()
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ PRECONDITION(GetId().m_dwId == DefaultADID);
+ }
+ CONTRACTL_END;
+
+ //
+ // If the CLR is being started to run a ClickOnce application, then capture the information about the
+ // application to setup the default domain wtih.
+ //
+
+ if (CorCommandLine::m_pwszAppFullName != NULL)
+ {
+ struct
+ {
+ OBJECTREF orThis;
+ STRINGREF orAppFullName;
+ PTRARRAYREF orManifestPathsArray;
+ PTRARRAYREF orActivationDataArray;
+ }
+ gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+
+ gc.orAppFullName = StringObject::NewString(CorCommandLine::m_pwszAppFullName);
+
+ // If specific manifests have been pointed at, make a note of them
+ if (CorCommandLine::m_dwManifestPaths > 0)
+ {
+ _ASSERTE(CorCommandLine::m_ppwszManifestPaths != NULL);
+
+ gc.orManifestPathsArray = static_cast<PTRARRAYREF>(AllocateObjectArray(CorCommandLine::m_dwManifestPaths, g_pStringClass));
+ for (DWORD i = 0; i < CorCommandLine::m_dwManifestPaths; ++i)
+ {
+ STRINGREF str = StringObject::NewString(CorCommandLine::m_ppwszManifestPaths[i]);
+ gc.orManifestPathsArray->SetAt(i, str);
+ }
+ }
+
+ // Check for any activation parameters to pass to the ClickOnce application
+ if (CorCommandLine::m_dwActivationData > 0)
+ {
+ _ASSERTE(CorCommandLine::m_ppwszActivationData != NULL);
+
+ gc.orActivationDataArray = static_cast<PTRARRAYREF>(AllocateObjectArray(CorCommandLine::m_dwActivationData, g_pStringClass));
+ for (DWORD i = 0; i < CorCommandLine::m_dwActivationData; ++i)
+ {
+ STRINGREF str = StringObject::NewString(CorCommandLine::m_ppwszActivationData[i]);
+ gc.orActivationDataArray->SetAt(i, str);
+ }
+ }
+
+ gc.orThis = GetExposedObject();
+
+ MethodDescCallSite setupDefaultClickOnceDomain(METHOD__APP_DOMAIN__SETUP_DEFAULT_CLICKONCE_DOMAIN);
+ ARG_SLOT args[] =
+ {
+ ObjToArgSlot(gc.orThis),
+ ObjToArgSlot(gc.orAppFullName),
+ ObjToArgSlot(gc.orManifestPathsArray),
+ ObjToArgSlot(gc.orActivationDataArray),
+ };
+ setupDefaultClickOnceDomain.Call(args);
+
+ GCPROTECT_END();
+ }
+}
+
+BOOL AppDomain::IsClickOnceAppDomain()
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ return ((APPDOMAINREF)GetExposedObject())->HasActivationContext();
+}
+
+#endif // FEATURE_CLICKONCE
+
+//---------------------------------------------------------------------------------------
+//
+// Intialize the security settings in the default AppDomain.
+//
+
+void AppDomain::InitializeDefaultDomainSecurity()
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ PRECONDITION(GetId().m_dwId == DefaultADID);
+ }
+ CONTRACTL_END;
+
+ OBJECTREF orThis = GetExposedObject();
+ GCPROTECT_BEGIN(orThis);
+
+ MethodDescCallSite initializeSecurity(METHOD__APP_DOMAIN__INITIALIZE_DOMAIN_SECURITY);
+ ARG_SLOT args[] =
+ {
+ ObjToArgSlot(orThis),
+ ObjToArgSlot(NULL),
+ ObjToArgSlot(NULL),
+ static_cast<ARG_SLOT>(FALSE),
+ ObjToArgSlot(NULL),
+ static_cast<ARG_SLOT>(FALSE)
+ };
+
+ initializeSecurity.Call(args);
+
+ GCPROTECT_END();
+}
+
+CLREvent * AppDomain::g_pUnloadStartEvent;
+
+void AppDomain::CreateADUnloadWorker()
+{
+ STANDARD_VM_CONTRACT;
+
+#ifdef FEATURE_CORECLR
+ // Do not create adUnload thread if there is only default domain
+ if(IsSingleAppDomain())
+ return;
+#endif
+
+Retry:
+ BOOL fCreator = FALSE;
+ if (FastInterlockCompareExchange((LONG *)&g_fADUnloadWorkerOK,-2,-1)==-1) //we're first
+ {
+#ifdef _TARGET_X86_ // use the smallest possible stack on X86
+ DWORD stackSize = 128 * 1024;
+#else
+ DWORD stackSize = 512 * 1024; // leave X64 unchanged since we have plenty of VM
+#endif
+ Thread *pThread = SetupUnstartedThread();
+ if (pThread->CreateNewThread(stackSize, ADUnloadThreadStart, pThread))
+ {
+ fCreator = TRUE;
+ DWORD dwRet;
+ dwRet = pThread->StartThread();
+
+ // When running under a user mode native debugger there is a race
+ // between the moment we've created the thread (in CreateNewThread) and
+ // the moment we resume it (in StartThread); the debugger may receive
+ // the "ct" (create thread) notification, and it will attempt to
+ // suspend/resume all threads in the process. Now imagine the debugger
+ // resumes this thread first, and only later does it try to resume the
+ // newly created thread (the ADU worker thread). In these conditions our
+ // call to ResumeThread may come before the debugger's call to ResumeThread
+ // actually causing dwRet to equal 2.
+ // We cannot use IsDebuggerPresent() in the condition below because the
+ // debugger may have been detached between the time it got the notification
+ // and the moment we execute the test below.
+ _ASSERTE(dwRet == 1 || dwRet == 2);
+ }
+ else
+ {
+ pThread->DecExternalCount(FALSE);
+ FastInterlockExchange((LONG *)&g_fADUnloadWorkerOK, -1);
+ ThrowOutOfMemory();
+ }
+ }
+
+ YIELD_WHILE (g_fADUnloadWorkerOK == -2);
+
+ if (g_fADUnloadWorkerOK == -1) {
+ if (fCreator)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ {
+ goto Retry;
+ }
+ }
+}
+
+/*static*/ void AppDomain::ADUnloadWorkerHelper(AppDomain *pDomain)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ ADUnloadSink* pADUnloadSink=pDomain->GetADUnloadSinkForUnload();
+ HRESULT hr=S_OK;
+
+ EX_TRY
+ {
+ pDomain->Unload(FALSE);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if(pADUnloadSink)
+ {
+ SystemDomain::LockHolder lh;
+ pADUnloadSink->ReportUnloadResult(hr,NULL);
+ pADUnloadSink->Release();
+ }
+}
+
+void AppDomain::DoADUnloadWork()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ DWORD i = 1;
+ while (TRUE) {
+
+ AppDomain *pDomainToUnload = NULL;
+
+ {
+ // Take the lock so that no domain can be added or removed from the system domain
+ SystemDomain::LockHolder lh;
+
+ DWORD numDomain = SystemDomain::GetCurrentAppDomainMaxIndex();
+ for (; i <= numDomain; i ++) {
+ AppDomain * pDomain = SystemDomain::TestGetAppDomainAtIndex(ADIndex(i));
+ //
+ // @todo: We used to also select a domain if pDomain->IsUnload() returned true. But that causes
+ // problems when we've failed to completely unload the AD in the past. If we've reached the CLEARED
+ // stage, for instance, then there will be no default context and AppDomain::Exit() will simply crash.
+ //
+ if (pDomain && pDomain->IsUnloadRequested())
+ {
+ pDomainToUnload = pDomain;
+ i ++;
+ break;
+ }
+ }
+ }
+
+ if (!pDomainToUnload) {
+ break;
+ }
+
+ // We are the only thread that can unload domains so no one else can delete the appdomain
+ ADUnloadWorkerHelper(pDomainToUnload);
+ }
+}
+
+static void DoADUnloadWorkHelper()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+
+ EX_TRY {
+ AppDomain::DoADUnloadWork();
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+}
+
+ULONGLONG g_ObjFinalizeStartTime = 0;
+Volatile<BOOL> g_FinalizerIsRunning = FALSE;
+Volatile<ULONG> g_FinalizerLoopCount = 0;
+
+ULONGLONG GetObjFinalizeStartTime()
+{
+ LIMITED_METHOD_CONTRACT;
+ return g_ObjFinalizeStartTime;
+}
+
+void FinalizerThreadAbortOnTimeout()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ {
+ // If finalizer thread is blocked because scheduler is running another task,
+ // or it is waiting for another thread, we first see if we get finalizer thread
+ // running again.
+ Thread::ThreadAbortWatchDog();
+ }
+
+ EX_TRY
+ {
+ Thread *pFinalizerThread = FinalizerThread::GetFinalizerThread();
+ EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(OPR_FinalizerRun, pFinalizerThread);
+ switch (action)
+ {
+ case eAbortThread:
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Safe,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
+ break;
+ case eRudeAbortThread:
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Rude,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
+ break;
+ case eUnloadAppDomain:
+ {
+ AppDomain *pDomain = pFinalizerThread->GetDomain();
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Safe,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
+ if (!pDomain->IsDefaultDomain())
+ {
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
+ }
+ }
+ break;
+ case eRudeUnloadAppDomain:
+ {
+ AppDomain *pDomain = pFinalizerThread->GetDomain();
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Rude,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
+ if (!pDomain->IsDefaultDomain())
+ {
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ pDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
+ }
+ }
+ break;
+ case eExitProcess:
+ case eFastExitProcess:
+ case eRudeExitProcess:
+ case eDisableRuntime:
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT);
+ _ASSERTE (!"Should not get here");
+ break;
+ default:
+ break;
+ }
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+}
+
+enum WorkType
+{
+ WT_UnloadDomain = 0x1,
+ WT_ThreadAbort = 0x2,
+ WT_FinalizerThread = 0x4,
+ WT_ClearCollectedDomains=0x8
+};
+
+static Volatile<DWORD> s_WorkType = 0;
+
+
+DWORD WINAPI AppDomain::ADUnloadThreadStart(void *args)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ DISABLED(GC_TRIGGERS);
+
+ // This function will always be at the very bottom of the stack. The only
+ // user code it calls is the AppDomainUnload notifications which we will
+ // not be hardenning for Whidbey.
+ //
+ ENTRY_POINT;
+ }
+ CONTRACTL_END;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ ClrFlsSetThreadType (ThreadType_ADUnloadHelper);
+
+ Thread *pThread = (Thread*)args;
+ bool fOK = (pThread->HasStarted() != 0);
+
+ {
+ GCX_MAYBE_PREEMP(fOK);
+
+ if (fOK)
+ {
+ EX_TRY
+ {
+ if (CLRTaskHosted())
+ {
+ // ADUnload helper thread is critical. We do not want it to share scheduler
+ // with other tasks.
+ pThread->LeaveRuntime(0);
+ }
+ }
+ EX_CATCH
+ {
+ fOK = false;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ }
+
+ _ASSERTE (g_fADUnloadWorkerOK == -2);
+
+ FastInterlockExchange((LONG *)&g_fADUnloadWorkerOK,fOK?1:-1);
+
+ if (!fOK)
+ {
+ DestroyThread(pThread);
+ goto Exit;
+ }
+
+ pThread->SetBackground(TRUE);
+
+ pThread->SetThreadStateNC(Thread::TSNC_ADUnloadHelper);
+
+ while (TRUE) {
+ DWORD TAtimeout = INFINITE;
+ ULONGLONG endTime = Thread::GetNextSelfAbortEndTime();
+ ULONGLONG curTime = CLRGetTickCount64();
+ if (endTime <= curTime) {
+ TAtimeout = 5;
+ }
+ else
+ {
+ ULONGLONG diff = endTime - curTime;
+ if (diff < MAXULONG)
+ {
+ TAtimeout = (DWORD)diff;
+ }
+ }
+ ULONGLONG finalizeStartTime = GetObjFinalizeStartTime();
+ DWORD finalizeTimeout = INFINITE;
+ DWORD finalizeTimeoutSetting = GetEEPolicy()->GetTimeout(OPR_FinalizerRun);
+ if (finalizeTimeoutSetting != INFINITE && g_FinalizerIsRunning)
+ {
+ if (finalizeStartTime == 0)
+ {
+ finalizeTimeout = finalizeTimeoutSetting;
+ }
+ else
+ {
+ endTime = finalizeStartTime + finalizeTimeoutSetting;
+ if (endTime <= curTime) {
+ finalizeTimeout = 0;
+ }
+ else
+ {
+ ULONGLONG diff = endTime - curTime;
+ if (diff < MAXULONG)
+ {
+ finalizeTimeout = (DWORD)diff;
+ }
+ }
+ }
+ }
+
+ if (AppDomain::HasWorkForFinalizerThread())
+ {
+ if (finalizeTimeout > finalizeTimeoutSetting)
+ {
+ finalizeTimeout = finalizeTimeoutSetting;
+ }
+ }
+
+ DWORD timeout = INFINITE;
+ if (finalizeTimeout <= TAtimeout)
+ {
+ timeout = finalizeTimeout;
+ }
+ else
+ {
+ timeout = TAtimeout;
+ }
+
+ if (timeout != 0)
+ {
+ LOG((LF_APPDOMAIN, LL_INFO10, "Waiting to start unload\n"));
+ g_pUnloadStartEvent->Wait(timeout,FALSE);
+ }
+
+ if (finalizeTimeout != INFINITE || (s_WorkType & WT_FinalizerThread) != 0)
+ {
+ STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for Finalizer thread\n");
+ FastInterlockAnd(&s_WorkType, ~WT_FinalizerThread);
+ // only watch finalizer thread is finalizer method or unloadevent is being processed
+ if (GetObjFinalizeStartTime() == finalizeStartTime && finalizeStartTime != 0 && g_FinalizerIsRunning)
+ {
+ if (CLRGetTickCount64() >= finalizeStartTime+finalizeTimeoutSetting)
+ {
+ GCX_COOP();
+ FinalizerThreadAbortOnTimeout();
+ }
+ }
+ if (s_fProcessUnloadDomainEvent && g_FinalizerIsRunning)
+ {
+ GCX_COOP();
+ FinalizerThreadAbortOnTimeout();
+ }
+ }
+
+ if (TAtimeout != INFINITE || (s_WorkType & WT_ThreadAbort) != 0)
+ {
+ STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for thread abort\n");
+ FastInterlockAnd(&s_WorkType, ~WT_ThreadAbort);
+ GCX_COOP();
+ Thread::ThreadAbortWatchDog();
+ }
+
+ if ((s_WorkType & WT_UnloadDomain) != 0 && !AppDomain::HasWorkForFinalizerThread())
+ {
+ STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for AD unload\n");
+ FastInterlockAnd(&s_WorkType, ~WT_UnloadDomain);
+ GCX_COOP();
+ DoADUnloadWorkHelper();
+ }
+
+ if ((s_WorkType & WT_ClearCollectedDomains) != 0)
+ {
+ STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for AD cleanup\n");
+ FastInterlockAnd(&s_WorkType, ~WT_ClearCollectedDomains);
+ GCX_COOP();
+ SystemDomain::System()->ClearCollectedDomains();
+ }
+
+ }
+Exit:;
+ }
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return 0;
+}
+
+void AppDomain::EnableADUnloadWorker()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT; // Called during a SO
+ }
+ CONTRACTL_END;
+
+ EEPolicy::AppDomainUnloadTypes type = EEPolicy::ADU_Safe;
+
+#ifdef _DEBUG
+ DWORD hostTestADUnload = g_pConfig->GetHostTestADUnload();
+ if (hostTestADUnload == 2) {
+ type = EEPolicy::ADU_Rude;
+ }
+#endif // _DEBUG
+
+ EnableADUnloadWorker(type);
+}
+
+void AppDomain::EnableADUnloadWorker(EEPolicy::AppDomainUnloadTypes type, BOOL fHasStack)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT; // Called during a SO
+ }
+ CONTRACTL_END;
+
+ FastInterlockOr (&s_WorkType, WT_UnloadDomain);
+
+ LONG stage = m_Stage;
+ static_assert_no_msg(sizeof(m_Stage) == sizeof(int));
+
+ _ASSERTE(!IsDefaultDomain());
+
+ // Mark unload requested.
+ if (type == EEPolicy::ADU_Rude) {
+ SetRudeUnload();
+ }
+ while (stage < STAGE_UNLOAD_REQUESTED) {
+ stage = FastInterlockCompareExchange((LONG*)&m_Stage,STAGE_UNLOAD_REQUESTED,stage);
+ }
+
+ if (!fHasStack)
+ {
+ // Can not call Set due to limited stack.
+ return;
+ }
+ LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker\n"));
+ g_pUnloadStartEvent->Set();
+}
+
+void AppDomain::EnableADUnloadWorkerForThreadAbort()
+{
+ LIMITED_METHOD_CONTRACT;
+ STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "Enabling unload worker for thread abort\n");
+ LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker for thread abort\n"));
+ FastInterlockOr (&s_WorkType, WT_ThreadAbort);
+ g_pUnloadStartEvent->Set();
+}
+
+
+void AppDomain::EnableADUnloadWorkerForFinalizer()
+{
+ LIMITED_METHOD_CONTRACT;
+ if (GetEEPolicy()->GetTimeout(OPR_FinalizerRun) != INFINITE)
+ {
+ LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker for Finalizer Thread\n"));
+ FastInterlockOr (&s_WorkType, WT_FinalizerThread);
+ g_pUnloadStartEvent->Set();
+ }
+}
+
+void AppDomain::EnableADUnloadWorkerForCollectedADCleanup()
+{
+ LIMITED_METHOD_CONTRACT;
+ LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker for collected domains\n"));
+ FastInterlockOr (&s_WorkType, WT_ClearCollectedDomains);
+ g_pUnloadStartEvent->Set();
+}
+
+
+void SystemDomain::ClearCollectedDomains()
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ NOTHROW;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ AppDomain* pDomainsToClear=NULL;
+ {
+ CrstHolder lh(&m_DelayedUnloadCrst);
+ for (AppDomain** ppDomain=&m_pDelayedUnloadList;(*ppDomain)!=NULL; )
+ {
+ if ((*ppDomain)->m_Stage==AppDomain::STAGE_COLLECTED)
+ {
+ AppDomain* pAppDomain=*ppDomain;
+ *ppDomain=(*ppDomain)->m_pNextInDelayedUnloadList;
+ pAppDomain->m_pNextInDelayedUnloadList=pDomainsToClear;
+ pDomainsToClear=pAppDomain;
+ }
+ else
+ ppDomain=&((*ppDomain)->m_pNextInDelayedUnloadList);
+ }
+ }
+
+ for (AppDomain* pDomain=pDomainsToClear;pDomain!=NULL;)
+ {
+ AppDomain* pNext=pDomain->m_pNextInDelayedUnloadList;
+ pDomain->Close(); //NOTHROW!
+ pDomain->Release();
+ pDomain=pNext;
+ }
+}
+
+void SystemDomain::ProcessClearingDomains()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ CrstHolder lh(&m_DelayedUnloadCrst);
+
+ for (AppDomain** ppDomain=&m_pDelayedUnloadList;(*ppDomain)!=NULL; )
+ {
+ if ((*ppDomain)->m_Stage==AppDomain::STAGE_HANDLETABLE_NOACCESS)
+ {
+ AppDomain* pAppDomain=*ppDomain;
+ pAppDomain->SetStage(AppDomain::STAGE_CLEARED);
+ }
+ ppDomain=&((*ppDomain)->m_pNextInDelayedUnloadList);
+ }
+
+ if (!m_UnloadIsAsync)
+ {
+ // For synchronous mode, we are now done with the list.
+ m_pDelayedUnloadList = NULL;
+ }
+}
+
+void SystemDomain::ProcessDelayedUnloadDomains()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ int iGCRefPoint=GCHeap::GetGCHeap()->CollectionCount(GCHeap::GetGCHeap()->GetMaxGeneration());
+ if (GCHeap::GetGCHeap()->IsConcurrentGCInProgress())
+ iGCRefPoint--;
+
+ BOOL bAppDomainToCleanup = FALSE;
+ LoaderAllocator * pAllocatorsToDelete = NULL;
+
+ {
+ CrstHolder lh(&m_DelayedUnloadCrst);
+
+ for (AppDomain* pDomain=m_pDelayedUnloadList; pDomain!=NULL; pDomain=pDomain->m_pNextInDelayedUnloadList)
+ {
+ if (pDomain->m_Stage==AppDomain::STAGE_CLEARED)
+ {
+ // Compare with 0 to handle overflows gracefully
+ if (0 < iGCRefPoint - pDomain->GetGCRefPoint())
+ {
+ bAppDomainToCleanup=TRUE;
+ pDomain->SetStage(AppDomain::STAGE_COLLECTED);
+ }
+ }
+ }
+
+ LoaderAllocator ** ppAllocator=&m_pDelayedUnloadListOfLoaderAllocators;
+ while (*ppAllocator!= NULL)
+ {
+ LoaderAllocator * pAllocator = *ppAllocator;
+ if (0 < iGCRefPoint - pAllocator->GetGCRefPoint())
+ {
+ *ppAllocator = pAllocator->m_pLoaderAllocatorDestroyNext;
+
+ pAllocator->m_pLoaderAllocatorDestroyNext = pAllocatorsToDelete;
+ pAllocatorsToDelete = pAllocator;
+ }
+ else
+ {
+ ppAllocator = &pAllocator->m_pLoaderAllocatorDestroyNext;
+ }
+ }
+ }
+
+ if (bAppDomainToCleanup)
+ AppDomain::EnableADUnloadWorkerForCollectedADCleanup();
+
+ // Delete collected loader allocators on the finalizer thread. We cannot offload it to appdomain unload thread because of
+ // there is not guaranteed to be one, and it is not that expensive operation anyway.
+ while (pAllocatorsToDelete != NULL)
+ {
+ LoaderAllocator * pAllocator = pAllocatorsToDelete;
+ pAllocatorsToDelete = pAllocator->m_pLoaderAllocatorDestroyNext;
+ delete pAllocator;
+ }
+}
+
+#endif // CROSSGEN_COMPILE
+
+AppDomainFromIDHolder::AppDomainFromIDHolder(ADID adId, BOOL bUnsafePoint, SyncType synctype)
+{
+ WRAPPER_NO_CONTRACT;
+ ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
+#ifdef _DEBUG
+ m_bAcquired=false;
+ m_bChecked=false;
+ m_type=synctype;
+
+#endif
+ Assign(adId, bUnsafePoint);
+}
+
+AppDomainFromIDHolder::AppDomainFromIDHolder(SyncType synctype)
+{
+ LIMITED_METHOD_CONTRACT;
+ ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
+ m_pDomain=NULL;
+#ifdef _DEBUG
+ m_bAcquired=false;
+ m_bChecked=false;
+ m_type=synctype;
+#endif
+}
+
+#ifndef CROSSGEN_COMPILE
+void ADUnloadSink::ReportUnloadResult (HRESULT hr, OBJECTREF* pException)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(m_UnloadCompleteEvent.IsValid());
+ }
+ CONTRACTL_END;
+
+ //pException is unused;
+ m_UnloadResult=hr;
+ m_UnloadCompleteEvent.Set();
+};
+
+void ADUnloadSink::WaitUnloadCompletion()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(m_UnloadCompleteEvent.IsValid());
+ }
+ CONTRACTL_END;
+
+ CONTRACT_VIOLATION(FaultViolation);
+ m_UnloadCompleteEvent.WaitEx(INFINITE, (WaitMode)(WaitMode_Alertable | WaitMode_ADUnload));
+};
+
+ADUnloadSink* AppDomain::PrepareForWaitUnloadCompletion()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(SystemDomain::IsUnderDomainLock());
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ ADUnloadSink* pADSink=GetADUnloadSink();
+ PREFIX_ASSUME(pADSink!=NULL);
+ if (m_Stage < AppDomain::STAGE_UNLOAD_REQUESTED) //we're first
+ {
+ pADSink->Reset();
+ SetUnloadRequestThread(GetThread());
+ }
+ return pADSink;
+};
+
+ADUnloadSink::ADUnloadSink()
+{
+ CONTRACTL
+ {
+ CONSTRUCTOR_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END;
+
+ m_cRef=1;
+ m_UnloadCompleteEvent.CreateManualEvent(FALSE);
+ m_UnloadResult=S_OK;
+};
+
+ADUnloadSink::~ADUnloadSink()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ m_UnloadCompleteEvent.CloseEvent();
+
+};
+
+
+ULONG ADUnloadSink::AddRef()
+{
+ LIMITED_METHOD_CONTRACT;
+ return InterlockedIncrement(&m_cRef);
+};
+
+ULONG ADUnloadSink::Release()
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG ulRef = InterlockedDecrement(&m_cRef);
+ if (ulRef == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return ulRef;
+};
+
+void ADUnloadSink::Reset()
+{
+ LIMITED_METHOD_CONTRACT;
+ m_UnloadResult=S_OK;
+ m_UnloadCompleteEvent.Reset();
+}
+
+ADUnloadSink* AppDomain::GetADUnloadSink()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(SystemDomain::IsUnderDomainLock());
+ if(m_ADUnloadSink)
+ m_ADUnloadSink->AddRef();
+ return m_ADUnloadSink;
+};
+
+ADUnloadSink* AppDomain::GetADUnloadSinkForUnload()
+{
+ // unload thread only. Doesn't need to have AD lock
+ LIMITED_METHOD_CONTRACT;
+ if(m_ADUnloadSink)
+ m_ADUnloadSink->AddRef();
+ return m_ADUnloadSink;
+}
+#endif // CROSSGEN_COMPILE
+
+void AppDomain::EnumStaticGCRefs(promote_func* fn, ScanContext* sc)
+{
+ CONTRACT_VOID
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ _ASSERTE(GCHeap::IsGCInProgress() &&
+ GCHeap::IsServerHeap() &&
+ IsGCSpecialThread());
+
+ AppDomain::AssemblyIterator asmIterator = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+ while (asmIterator.Next(pDomainAssembly.This()))
+ {
+ // @TODO: Review when DomainAssemblies get added.
+ _ASSERTE(pDomainAssembly != NULL);
+ pDomainAssembly->EnumStaticGCRefs(fn, sc);
+ }
+
+ RETURN;
+}
+
+#endif // !DACCESS_COMPILE
+
+//------------------------------------------------------------------------
+UINT32 BaseDomain::GetTypeID(PTR_MethodTable pMT) {
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(pMT->GetDomain() == this);
+ } CONTRACTL_END;
+
+ return m_typeIDMap.GetTypeID(pMT);
+}
+
+//------------------------------------------------------------------------
+// Returns the ID of the type if found. If not found, returns INVALID_TYPE_ID
+UINT32 BaseDomain::LookupTypeID(PTR_MethodTable pMT)
+{
+ CONTRACTL {
+ NOTHROW;
+ SO_TOLERANT;
+ WRAPPER(GC_TRIGGERS);
+ PRECONDITION(pMT->GetDomain() == this);
+ } CONTRACTL_END;
+
+ return m_typeIDMap.LookupTypeID(pMT);
+}
+
+//------------------------------------------------------------------------
+PTR_MethodTable BaseDomain::LookupType(UINT32 id) {
+ CONTRACTL {
+ NOTHROW;
+ SO_TOLERANT;
+ WRAPPER(GC_TRIGGERS);
+ CONSISTENCY_CHECK(id != TYPE_ID_THIS_CLASS);
+ } CONTRACTL_END;
+
+ PTR_MethodTable pMT = m_typeIDMap.LookupType(id);
+ if (pMT == NULL && !IsSharedDomain()) {
+ pMT = SharedDomain::GetDomain()->LookupType(id);
+ }
+
+ CONSISTENCY_CHECK(CheckPointer(pMT));
+ CONSISTENCY_CHECK(pMT->IsInterface());
+ return pMT;
+}
+
+#ifndef DACCESS_COMPILE
+
+#ifndef FEATURE_CORECLR
+//------------------------------------------------------------------------
+DWORD* SetupCompatibilityFlags()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ WCHAR buf[2] = { '\0', '\0' };
+
+ FAULT_NOT_FATAL(); // we can simply give up
+
+ if (WszGetEnvironmentVariable(W("UnsupportedCompatSwitchesEnabled"), buf, COUNTOF(buf)) == 0)
+ return NULL;
+
+ if (buf[0] != '1' || buf[1] != '\0')
+ return NULL;
+
+ static const LPCWSTR rgFlagNames[] = {
+#define COMPATFLAGDEF(name) TEXT(#name),
+#include "compatibilityflagsdef.h"
+ };
+
+ int size = (compatCount+31) / 32;
+ DWORD* pFlags = new (nothrow) DWORD[size];
+ if (pFlags == NULL)
+ return NULL;
+ ZeroMemory(pFlags, size * sizeof(DWORD));
+
+ for (int i = 0; i < COUNTOF(rgFlagNames); i++)
+ {
+ if (WszGetEnvironmentVariable(rgFlagNames[i], buf, COUNTOF(buf)) == 0)
+ continue;
+
+ if (buf[0] != '1' || buf[1] != '\0')
+ continue;
+
+ pFlags[i / 32] |= 1 << (i % 32);
+ }
+
+ return pFlags;
+}
+
+//------------------------------------------------------------------------
+static VolatilePtr<DWORD> g_pCompatibilityFlags = (DWORD*)(-1);
+
+DWORD* GetGlobalCompatibilityFlags()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ if (g_pCompatibilityFlags == (DWORD*)(-1))
+ {
+ DWORD *pCompatibilityFlags = SetupCompatibilityFlags();
+
+ if (FastInterlockCompareExchangePointer(g_pCompatibilityFlags.GetPointer(), pCompatibilityFlags, reinterpret_cast<DWORD *>(-1)) != (VOID*)(-1))
+ {
+ delete [] pCompatibilityFlags;
+ }
+ }
+
+ return g_pCompatibilityFlags;
+}
+#endif // !FEATURE_CORECLR
+
+//------------------------------------------------------------------------
+BOOL GetCompatibilityFlag(CompatibilityFlag flag)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+#ifndef FEATURE_CORECLR
+ DWORD *pFlags = GetGlobalCompatibilityFlags();
+
+ if (pFlags != NULL)
+ return (pFlags[flag / 32] & (1 << (flag % 32))) ? TRUE : FALSE;
+ else
+ return FALSE;
+#else // !FEATURE_CORECLR
+ return FALSE;
+#endif // !FEATURE_CORECLR
+}
+#endif // !DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+BOOL
+AppDomain::AssemblyIterator::Next(
+ CollectibleAssemblyHolder<DomainAssembly *> * pDomainAssemblyHolder)
+{
+ CONTRACTL {
+ NOTHROW;
+ WRAPPER(GC_TRIGGERS); // Triggers only in MODE_COOPERATIVE (by taking the lock)
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ CrstHolder ch(m_pAppDomain->GetAssemblyListLock());
+ return Next_Unlocked(pDomainAssemblyHolder);
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Note: Does not lock the assembly list, but locks collectible assemblies for adding references.
+//
+BOOL
+AppDomain::AssemblyIterator::Next_Unlocked(
+ CollectibleAssemblyHolder<DomainAssembly *> * pDomainAssemblyHolder)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+#ifndef DACCESS_COMPILE
+ _ASSERTE(m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread());
+#endif
+
+ while (m_Iterator.Next())
+ {
+ // Get element from the list/iterator (without adding reference to the assembly)
+ DomainAssembly * pDomainAssembly = dac_cast<PTR_DomainAssembly>(m_Iterator.GetElement());
+ if (pDomainAssembly == NULL)
+ {
+ continue;
+ }
+
+ if (pDomainAssembly->IsError())
+ {
+ if (m_assemblyIterationFlags & kIncludeFailedToLoad)
+ {
+ *pDomainAssemblyHolder = pDomainAssembly;
+ return TRUE;
+ }
+ continue; // reject
+ }
+
+ // First, reject DomainAssemblies whose load status is not to be included in
+ // the enumeration
+
+ if (pDomainAssembly->IsAvailableToProfilers() &&
+ (m_assemblyIterationFlags & kIncludeAvailableToProfilers))
+ {
+ // The assembly has reached the state at which we would notify profilers,
+ // and we're supposed to include such assemblies in the enumeration. So
+ // don't reject it (i.e., noop here, and don't bother with the rest of
+ // the load status checks). Check for this first, since
+ // kIncludeAvailableToProfilers contains some loaded AND loading
+ // assemblies.
+ }
+ else if (pDomainAssembly->IsLoaded())
+ {
+ // A loaded assembly
+ if (!(m_assemblyIterationFlags & kIncludeLoaded))
+ {
+ continue; // reject
+ }
+ }
+ else
+ {
+ // A loading assembly
+ if (!(m_assemblyIterationFlags & kIncludeLoading))
+ {
+ continue; // reject
+ }
+ }
+
+ // Next, reject DomainAssemblies whose execution / introspection status is
+ // not to be included in the enumeration
+
+ if (pDomainAssembly->IsIntrospectionOnly())
+ {
+ // introspection assembly
+ if (!(m_assemblyIterationFlags & kIncludeIntrospection))
+ {
+ continue; // reject
+ }
+ }
+ else
+ {
+ // execution assembly
+ if (!(m_assemblyIterationFlags & kIncludeExecution))
+ {
+ continue; // reject
+ }
+ }
+
+ // Next, reject collectible assemblies
+ if (pDomainAssembly->IsCollectible())
+ {
+ if (m_assemblyIterationFlags & kExcludeCollectible)
+ {
+ _ASSERTE(!(m_assemblyIterationFlags & kIncludeCollected));
+ continue; // reject
+ }
+
+ // Un-tenured collectible assemblies should not be returned. (This can only happen in a brief
+ // window during collectible assembly creation. No thread should need to have a pointer
+ // to the just allocated DomainAssembly at this stage.)
+ if (!pDomainAssembly->GetAssembly()->GetManifestModule()->IsTenured())
+ {
+ continue; // reject
+ }
+
+ if (pDomainAssembly->GetLoaderAllocator()->AddReferenceIfAlive())
+ { // The assembly is alive
+
+ // Set the holder value (incl. increasing ref-count)
+ *pDomainAssemblyHolder = pDomainAssembly;
+
+ // Now release the reference we took in the if-condition
+ pDomainAssembly->GetLoaderAllocator()->Release();
+ return TRUE;
+ }
+ // The assembly is not alive anymore (and we didn't increase its ref-count in the
+ // if-condition)
+
+ if (!(m_assemblyIterationFlags & kIncludeCollected))
+ {
+ continue; // reject
+ }
+ // Set the holder value to assembly with 0 ref-count without increasing the ref-count (won't
+ // call Release either)
+ pDomainAssemblyHolder->Assign(pDomainAssembly, FALSE);
+ return TRUE;
+ }
+
+ *pDomainAssemblyHolder = pDomainAssembly;
+ return TRUE;
+ }
+
+ *pDomainAssemblyHolder = NULL;
+ return FALSE;
+} // AppDomain::AssemblyIterator::Next_Unlocked
+
+#ifndef DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+// Can be called only from AppDomain shutdown code:AppDomain::ShutdownAssemblies.
+// Does not add-ref collectible assemblies (as the LoaderAllocator might not be reachable from the
+// DomainAssembly anymore).
+//
+BOOL
+AppDomain::AssemblyIterator::Next_UnsafeNoAddRef(
+ DomainAssembly ** ppDomainAssembly)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ // Make sure we are iterating all assemblies (see the only caller code:AppDomain::ShutdownAssemblies)
+ _ASSERTE(m_assemblyIterationFlags ==
+ (kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeIntrospection | kIncludeFailedToLoad | kIncludeCollected));
+ // It also means that we do not exclude anything
+ _ASSERTE((m_assemblyIterationFlags & kExcludeCollectible) == 0);
+
+ // We are on shutdown path, so lock shouldn't be neccessary, but all _Unlocked methods on AssemblyList
+ // have asserts that the lock is held, so why not to take it ...
+ CrstHolder ch(m_pAppDomain->GetAssemblyListLock());
+
+ while (m_Iterator.Next())
+ {
+ // Get element from the list/iterator (without adding reference to the assembly)
+ *ppDomainAssembly = dac_cast<PTR_DomainAssembly>(m_Iterator.GetElement());
+ if (*ppDomainAssembly == NULL)
+ {
+ continue;
+ }
+
+ return TRUE;
+ }
+
+ *ppDomainAssembly = NULL;
+ return FALSE;
+} // AppDomain::AssemblyIterator::Next_UnsafeNoAddRef
+
+#ifdef FEATURE_CORECLR
+
+//---------------------------------------------------------------------------------------
+//
+BOOL AppDomain::IsImageFromTrustedPath(PEImage* pPEImage)
+{
+ CONTRACTL
+ {
+ MODE_ANY;
+ GC_TRIGGERS;
+ THROWS;
+ PRECONDITION(CheckPointer(pPEImage));
+ }
+ CONTRACTL_END;
+
+ BOOL fIsInGAC = FALSE;
+ const SString &sImagePath = pPEImage->GetPath();
+
+ if (!sImagePath.IsEmpty())
+ {
+ // If we're not in a sandboxed domain, everything is full trust all the time
+ if (GetSecurityDescriptor()->IsFullyTrusted())
+ {
+ return TRUE;
+ }
+
+ fIsInGAC = GetTPABinderContext()->IsInTpaList(sImagePath);
+ }
+
+ return fIsInGAC;
+}
+
+BOOL AppDomain::IsImageFullyTrusted(PEImage* pPEImage)
+{
+ WRAPPER_NO_CONTRACT;
+ return IsImageFromTrustedPath(pPEImage);
+}
+
+#ifdef FEATURE_LEGACYNETCF
+BOOL RuntimeIsLegacyNetCF(DWORD adid)
+{
+ AppDomain * pAppDomain = GetAppDomain();
+
+ _ASSERTE(adid == 0 || adid == pAppDomain->GetId().m_dwId);
+
+ if (pAppDomain == NULL)
+ return FALSE;
+
+ if (pAppDomain->GetAppDomainCompatMode() == BaseDomain::APPDOMAINCOMPAT_APP_EARLIER_THAN_WP8)
+ return TRUE;
+
+ return FALSE;
+}
+#endif
+
+#endif //FEATURE_CORECLR
+
+#endif //!DACCESS_COMPILE
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+
+// Returns a BOOL indicating if the binding model has been locked for the AppDomain
+BOOL AppDomain::IsBindingModelLocked()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ return m_fIsBindingModelLocked.Load();
+}
+
+// Marks the binding model locked for AppDomain
+BOOL AppDomain::LockBindingModel()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ BOOL fDidWeLockBindingModel = FALSE;
+
+ if (InterlockedCompareExchangeT<BOOL>(&m_fIsBindingModelLocked, TRUE, FALSE) == FALSE)
+ {
+ fDidWeLockBindingModel = TRUE;
+ }
+
+ return fDidWeLockBindingModel;
+}
+
+BOOL AppDomain::IsHostAssemblyResolverInUse()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (GetFusionContext() != GetTPABinderContext());
+}
+
+// Helper used by the assembly binder to check if the specified AppDomain can use apppath assembly resolver
+BOOL RuntimeCanUseAppPathAssemblyResolver(DWORD adid)
+{
+ CONTRACTL
+ {
+ NOTHROW; // Cannot throw since it is invoked by the Binder that expects to get a hresult
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ ADID id(adid);
+
+ // We need to be in COOP mode to get the AppDomain*
+ GCX_COOP();
+
+ AppDomain *pTargetDomain = SystemDomain::GetAppDomainFromId(id, ADV_CURRENTAD);
+ _ASSERTE(pTargetDomain != NULL);
+
+ pTargetDomain->LockBindingModel();
+
+ return !pTargetDomain->IsHostAssemblyResolverInUse();
+}
+
+// Returns S_OK if the assembly was successfully loaded
+HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(ppLoadedAssembly != NULL);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_FAIL;
+
+ // DevDiv #933506: Exceptions thrown during AssemblyLoadContext.Load should propagate
+ // EX_TRY
+ {
+ // Switch to COOP mode since we are going to work with managed references
+ GCX_COOP();
+
+ struct
+ {
+ ASSEMBLYNAMEREF oRefAssemblyName;
+ ASSEMBLYREF oRefLoadedAssembly;
+ } _gcRefs;
+
+ ZeroMemory(&_gcRefs, sizeof(_gcRefs));
+
+ GCPROTECT_BEGIN(_gcRefs);
+
+ // Get the pointer to the managed assembly load context
+ INT_PTR ptrManagedAssemblyLoadContext = pLoadContextToBindWithin->GetManagedAssemblyLoadContext();
+
+ // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.Resolve method.
+ //
+ // First, initialize an assembly spec for the requested assembly
+ //
+ AssemblySpec spec;
+ hr = spec.Init(pIAssemblyName);
+ if (SUCCEEDED(hr))
+ {
+ // Next, allocate an AssemblyName managed object
+ _gcRefs.oRefAssemblyName = (ASSEMBLYNAMEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__ASSEMBLY_NAME));
+
+ // Initialize the AssemblyName object from the AssemblySpec
+ spec.AssemblyNameInit(&_gcRefs.oRefAssemblyName, NULL);
+
+ // Finally, setup arguments for invocation
+ BinderMethodID idHAR_Resolve = METHOD__ASSEMBLYLOADCONTEXT__RESOLVE;
+ MethodDescCallSite methLoadAssembly(idHAR_Resolve);
+
+ // Setup the arguments for the call
+ ARG_SLOT args[2] =
+ {
+ PtrToArgSlot(ptrManagedAssemblyLoadContext), // IntPtr for managed assembly load context instance
+ ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance
+ };
+
+ // Make the call
+ _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methLoadAssembly.Call_RetOBJECTREF(args);
+ if (_gcRefs.oRefLoadedAssembly != NULL)
+ {
+ // We were able to get the assembly loaded. Now, get its name since the host could have
+ // performed the resolution using an assembly with different name.
+ DomainAssembly *pDomainAssembly = _gcRefs.oRefLoadedAssembly->GetDomainAssembly();
+ PEAssembly *pLoadedPEAssembly = NULL;
+ bool fFailLoad = false;
+ if (!pDomainAssembly)
+ {
+ // Reflection emitted assemblies will not have a domain assembly.
+ fFailLoad = true;
+ }
+ else
+ {
+ pLoadedPEAssembly = pDomainAssembly->GetFile();
+ if (pLoadedPEAssembly->HasHostAssembly() != true)
+ {
+ // Reflection emitted assemblies will not have a domain assembly.
+ fFailLoad = true;
+ }
+ }
+
+ // The loaded assembly's ICLRPrivAssembly* is saved as HostAssembly in PEAssembly
+ if (fFailLoad)
+ {
+ SString name;
+ spec.GetFileOrDisplayName(0, name);
+ COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED, name);
+ }
+
+ // Is the assembly already bound using a binding context that will be incompatible?
+ // An example is attempting to consume an assembly bound to WinRT binder.
+ ICLRPrivAssembly *pAssemblyBindingContext = pLoadedPEAssembly->GetHostAssembly();
+
+#ifdef FEATURE_COMINTEROP
+ if (AreSameBinderInstance(pAssemblyBindingContext, GetAppDomain()->GetWinRtBinder()))
+ {
+ // It is invalid to return an assembly bound to an incompatible binder
+ *ppLoadedAssembly = NULL;
+ SString name;
+ spec.GetFileOrDisplayName(0, name);
+ COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_INCOMPATIBLE_BINDING_CONTEXT, name);
+ }
+#endif // FEATURE_COMINTEROP
+
+ // Get the ICLRPrivAssembly reference to return back to.
+ *ppLoadedAssembly = clr::SafeAddRef(pLoadedPEAssembly->GetHostAssembly());
+ hr = S_OK;
+ }
+ }
+
+ GCPROTECT_END();
+ }
+ // EX_CATCH_HRESULT(hr);
+
+ return hr;
+
+}
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+
+//approximate size of loader data
+//maintained for each assembly
+#define APPROX_LOADER_DATA_PER_ASSEMBLY 8196
+
+size_t AppDomain::EstimateSize()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ size_t retval = sizeof(AppDomain);
+ retval += GetLoaderAllocator()->EstimateSize();
+ //very rough estimate
+ retval += GetAssemblyCount() * APPROX_LOADER_DATA_PER_ASSEMBLY;
+ return retval;
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+DomainLocalModule::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+
+ // Enumerate the DomainLocalModule itself. DLMs are allocated to be larger than
+ // sizeof(DomainLocalModule) to make room for ClassInit flags and non-GC statics.
+ // "DAC_ENUM_DTHIS()" probably does not account for this, so we might not enumerate
+ // all of the ClassInit flags and non-GC statics.
+ // sizeof(DomainLocalModule) == 0x28
+ DAC_ENUM_DTHIS();
+
+ if (m_pDomainFile.IsValid())
+ {
+ m_pDomainFile->EnumMemoryRegions(flags);
+ }
+
+ if (m_pDynamicClassTable.Load().IsValid())
+ {
+ DacEnumMemoryRegion(dac_cast<TADDR>(m_pDynamicClassTable.Load()),
+ m_aDynamicEntries * sizeof(DynamicClassInfo));
+
+ for (SIZE_T i = 0; i < m_aDynamicEntries; i++)
+ {
+ PTR_DynamicEntry entry = dac_cast<PTR_DynamicEntry>(m_pDynamicClassTable[i].m_pDynamicEntry.Load());
+ if (entry.IsValid())
+ {
+ // sizeof(DomainLocalModule::DynamicEntry) == 8
+ entry.EnumMem();
+ }
+ }
+ }
+}
+
+void
+DomainLocalBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ // Block is contained in AppDomain, don't enum this.
+
+ if (m_pModuleSlots.IsValid())
+ {
+ DacEnumMemoryRegion(dac_cast<TADDR>(m_pModuleSlots),
+ m_aModuleIndices * sizeof(TADDR));
+
+ for (SIZE_T i = 0; i < m_aModuleIndices; i++)
+ {
+ PTR_DomainLocalModule domMod = m_pModuleSlots[i];
+ if (domMod.IsValid())
+ {
+ domMod->EnumMemoryRegions(flags);
+ }
+ }
+ }
+}
+
+void
+BaseDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ bool enumThis)
+{
+ SUPPORTS_DAC;
+ if (enumThis)
+ {
+ // This is wrong. Don't do it.
+ // BaseDomain cannot be instantiated.
+ // The only thing this code can hope to accomplish is to potentially break
+ // memory enumeration walking through the derived class if we
+ // explicitly call the base class enum first.
+// DAC_ENUM_VTHIS();
+ }
+
+ EMEM_OUT(("MEM: %p BaseDomain\n", dac_cast<TADDR>(this)));
+}
+
+void
+AppDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ bool enumThis)
+{
+ SUPPORTS_DAC;
+
+ if (enumThis)
+ {
+ //sizeof(AppDomain) == 0xeb0
+ DAC_ENUM_VTHIS();
+ }
+ BaseDomain::EnumMemoryRegions(flags, false);
+
+ // We don't need AppDomain name in triage dumps.
+ if (flags != CLRDATA_ENUM_MEM_TRIAGE)
+ {
+ m_friendlyName.EnumMemoryRegions(flags);
+ }
+
+ m_Assemblies.EnumMemoryRegions(flags);
+ AssemblyIterator assem = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution | kIncludeIntrospection));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+
+ while (assem.Next(pDomainAssembly.This()))
+ {
+ pDomainAssembly->EnumMemoryRegions(flags);
+ }
+
+ m_sDomainLocalBlock.EnumMemoryRegions(flags);
+
+ m_LoaderAllocator.EnumMemoryRegions(flags);
+}
+
+void
+SystemDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ bool enumThis)
+{
+ SUPPORTS_DAC;
+ if (enumThis)
+ {
+ DAC_ENUM_VTHIS();
+ }
+ BaseDomain::EnumMemoryRegions(flags, false);
+
+ if (m_pSystemFile.IsValid())
+ {
+ m_pSystemFile->EnumMemoryRegions(flags);
+ }
+ if (m_pSystemAssembly.IsValid())
+ {
+ m_pSystemAssembly->EnumMemoryRegions(flags);
+ }
+ if (m_pDefaultDomain.IsValid())
+ {
+ m_pDefaultDomain->EnumMemoryRegions(flags, true);
+ }
+
+ m_appDomainIndexList.EnumMem();
+ (&m_appDomainIndexList)->EnumMemoryRegions(flags);
+}
+
+void
+SharedDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ bool enumThis)
+{
+ SUPPORTS_DAC;
+ if (enumThis)
+ {
+ DAC_ENUM_VTHIS();
+ }
+ BaseDomain::EnumMemoryRegions(flags, false);
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ m_assemblyMap.EnumMemoryRegions(flags);
+ SharedAssemblyIterator assem;
+ while (assem.Next())
+ {
+ assem.GetAssembly()->EnumMemoryRegions(flags);
+ }
+#endif
+}
+
+#endif //DACCESS_COMPILE
+
+
+PTR_LoaderAllocator SystemDomain::GetGlobalLoaderAllocator()
+{
+ return PTR_LoaderAllocator(PTR_HOST_MEMBER_TADDR(SystemDomain,System(),m_GlobalAllocator));
+}
+
+#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+#ifndef CROSSGEN_COMPILE
+// Return the total processor time (user and kernel) used by threads executing in this AppDomain so far. The
+// result is in 100ns units.
+ULONGLONG AppDomain::QueryProcessorUsage()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifndef DACCESS_COMPILE
+ Thread *pThread = NULL;
+
+ // Need to update our accumulated processor time count with current values from each thread that is
+ // currently executing in this domain.
+
+ // Take the thread store lock while we enumerate threads.
+ ThreadStoreLockHolder tsl;
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+ {
+ // Skip unstarted and dead threads and those that are currently executing in a different AppDomain.
+ if (pThread->IsUnstarted() || pThread->IsDead() || pThread->GetDomain(INDEBUG(TRUE)) != this)
+ continue;
+
+ // Add the amount of time spent by the thread in the AppDomain since the last time we asked (calling
+ // Thread::QueryThreadProcessorUsage() will reset the thread's counter).
+ UpdateProcessorUsage(pThread->QueryThreadProcessorUsage());
+ }
+#endif // !DACCESS_COMPILE
+
+ // Return the updated total.
+ return m_ullTotalProcessorUsage;
+}
+
+// Add to the current count of processor time used by threads within this AppDomain. This API is called by
+// threads transitioning between AppDomains.
+void AppDomain::UpdateProcessorUsage(ULONGLONG ullAdditionalUsage)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Need to be careful to synchronize here, multiple threads could be racing to update this count.
+ ULONGLONG ullOldValue;
+ ULONGLONG ullNewValue;
+ do
+ {
+ ullOldValue = m_ullTotalProcessorUsage;
+ ullNewValue = ullOldValue + ullAdditionalUsage;
+ } while (InterlockedCompareExchange64((LONGLONG*)&m_ullTotalProcessorUsage,
+ (LONGLONG)ullNewValue,
+ (LONGLONG)ullOldValue) != (LONGLONG)ullOldValue);
+}
+#endif // CROSSGEN_COMPILE
+
+#endif // FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+#if defined(FEATURE_TYPEEQUIVALENCE)
+
+#ifndef DACCESS_COMPILE
+TypeEquivalenceHashTable * AppDomain::GetTypeEquivalenceCache()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM());
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Take the critical section all of the time in debug builds to ensure that it is safe to take
+ // the critical section in the unusual times when it may actually be needed in retail builds
+#ifdef _DEBUG
+ CrstHolder ch(&m_TypeEquivalenceCrst);
+#endif
+
+ if (m_pTypeEquivalenceTable.Load() == NULL)
+ {
+#ifndef _DEBUG
+ CrstHolder ch(&m_TypeEquivalenceCrst);
+#endif
+ if (m_pTypeEquivalenceTable.Load() == NULL)
+ {
+ m_pTypeEquivalenceTable = TypeEquivalenceHashTable::Create(this, 12, &m_TypeEquivalenceCrst);
+ }
+ }
+ return m_pTypeEquivalenceTable;
+}
+#endif //!DACCESS_COMPILE
+
+#endif //FEATURE_TYPEEQUIVALENCE
+
+#if defined(FEATURE_HOSTED_BINDER)
+#if !defined(DACCESS_COMPILE)
+
+//---------------------------------------------------------------------------------------------------------------------
+void AppDomain::PublishHostedAssembly(
+ DomainAssembly * pDomainAssembly)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ if (pDomainAssembly->GetFile()->HasHostAssembly())
+ {
+ // We have to serialize all Add operations
+ CrstHolder lockAdd(&m_crstHostAssemblyMapAdd);
+ _ASSERTE(m_hostAssemblyMap.Lookup(pDomainAssembly->GetFile()->GetHostAssembly()) == nullptr);
+
+ // Wrapper for m_hostAssemblyMap.Add that avoids call out into host
+ HostAssemblyMap::AddPhases addCall;
+
+ // 1. Preallocate one element
+ addCall.PreallocateForAdd(&m_hostAssemblyMap);
+ {
+ // 2. Take the reader lock which can be taken during stack walking
+ // We cannot call out into host from ForbidSuspend region (i.e. no allocations/deallocations)
+ ForbidSuspendThreadHolder suspend;
+ {
+ CrstHolder lock(&m_crstHostAssemblyMap);
+ // 3. Add the element to the hash table (no call out into host)
+ addCall.Add(pDomainAssembly);
+ }
+ }
+ // 4. Cleanup the old memory (if any)
+ addCall.DeleteOldTable();
+ }
+ else
+ {
+#ifdef FEATURE_APPX_BINDER
+ // In AppX processes, all PEAssemblies that are reach this stage should have host binders.
+ _ASSERTE(!AppX::IsAppXProcess());
+#endif
+ }
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+void AppDomain::UpdatePublishHostedAssembly(
+ DomainAssembly * pAssembly,
+ PTR_PEFile pFile)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END
+
+ if (pAssembly->GetFile()->HasHostAssembly())
+ {
+ // We have to serialize all Add operations
+ CrstHolder lockAdd(&m_crstHostAssemblyMapAdd);
+ {
+ // Wrapper for m_hostAssemblyMap.Add that avoids call out into host
+ OriginalFileHostAssemblyMap::AddPhases addCall;
+ bool fAddOrigFile = false;
+
+ // For cases where the pefile is being updated
+ // 1. Preallocate one element
+ if (pFile != pAssembly->GetFile())
+ {
+ addCall.PreallocateForAdd(&m_hostAssemblyMapForOrigFile);
+ fAddOrigFile = true;
+ }
+
+ {
+ // We cannot call out into host from ForbidSuspend region (i.e. no allocations/deallocations)
+ ForbidSuspendThreadHolder suspend;
+ {
+ CrstHolder lock(&m_crstHostAssemblyMap);
+
+ // Remove from hash table.
+ _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) != nullptr);
+ m_hostAssemblyMap.Remove(pAssembly->GetFile()->GetHostAssembly());
+
+ // Update PEFile on DomainAssembly. (This may cause the key for the hash to change, which is why we need this function)
+ pAssembly->UpdatePEFileWorker(pFile);
+
+ _ASSERTE(fAddOrigFile == (pAssembly->GetOriginalFile() != pAssembly->GetFile()));
+ if (fAddOrigFile)
+ {
+ // Add to the orig file hash table if we might be in a case where we've cached the original pefile and not the final pe file (for use during GetAssemblyIfLoaded)
+ addCall.Add(pAssembly);
+ }
+
+ // Add back to the hashtable (the call to Remove above guarantees that we will not call into host for table reallocation)
+ _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) == nullptr);
+ m_hostAssemblyMap.Add(pAssembly);
+ }
+ }
+
+ // 4. Cleanup the old memory (if any)
+ if (fAddOrigFile)
+ addCall.DeleteOldTable();
+ }
+ }
+ else
+ {
+#ifdef FEATURE_APPX_BINDER
+ // In AppX processes, all PEAssemblies that are reach this stage should have host binders.
+ _ASSERTE(!AppX::IsAppXProcess());
+#endif
+
+ pAssembly->UpdatePEFileWorker(pFile);
+ }
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+void AppDomain::UnPublishHostedAssembly(
+ DomainAssembly * pAssembly)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END
+
+ if (pAssembly->GetFile()->HasHostAssembly())
+ {
+ ForbidSuspendThreadHolder suspend;
+ {
+ CrstHolder lock(&m_crstHostAssemblyMap);
+ _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) != nullptr);
+ m_hostAssemblyMap.Remove(pAssembly->GetFile()->GetHostAssembly());
+
+ // We also have an entry in m_hostAssemblyMapForOrigFile. Handle that case.
+ if (pAssembly->GetOriginalFile() != pAssembly->GetFile())
+ {
+ m_hostAssemblyMapForOrigFile.Remove(pAssembly->GetOriginalFile()->GetHostAssembly());
+ }
+ }
+ }
+ else
+ {
+ // In AppX processes, all PEAssemblies that are reach this stage should have host binders.
+ _ASSERTE(!AppX::IsAppXProcess());
+ }
+}
+
+#if defined(FEATURE_CORECLR) && defined(FEATURE_COMINTEROP)
+HRESULT AppDomain::SetWinrtApplicationContext(SString &appLocalWinMD)
+{
+ STANDARD_VM_CONTRACT;
+
+ _ASSERTE(m_pWinRtBinder != nullptr);
+
+ _ASSERTE(GetTPABinderContext() != NULL);
+ BINDER_SPACE::ApplicationContext *pApplicationContext = GetTPABinderContext()->GetAppContext();
+ _ASSERTE(pApplicationContext != NULL);
+
+ return m_pWinRtBinder->SetApplicationContext(pApplicationContext, appLocalWinMD);
+}
+
+#endif // FEATURE_CORECLR && FEATURE_COMINTEROP
+
+#endif //!DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------------------------------------
+PTR_DomainAssembly AppDomain::FindAssembly(PTR_ICLRPrivAssembly pHostAssembly)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ if (pHostAssembly == nullptr)
+ return NULL;
+
+ {
+ ForbidSuspendThreadHolder suspend;
+ {
+ CrstHolder lock(&m_crstHostAssemblyMap);
+ PTR_DomainAssembly returnValue = m_hostAssemblyMap.Lookup(pHostAssembly);
+ if (returnValue == NULL)
+ {
+ // If not found in the m_hostAssemblyMap, look in the m_hostAssemblyMapForOrigFile
+ // This is necessary as it may happen during in a second AppDomain that the PEFile
+ // first discovered in the AppDomain may not be used by the DomainFile, but the CLRPrivBinderFusion
+ // will in some cases find the pHostAssembly associated with this no longer used PEFile
+ // instead of the PEFile that was finally decided upon.
+ returnValue = m_hostAssemblyMapForOrigFile.Lookup(pHostAssembly);
+ }
+
+ return returnValue;
+ }
+ }
+}
+
+#endif //FEATURE_HOSTED_BINDER
+
+#if !defined(DACCESS_COMPILE) && defined(FEATURE_CORECLR)
+
+void ZapperSetBindingPaths(ICorCompilationDomain *pDomain, SString &trustedPlatformAssemblies, SString &platformResourceRoots, SString &appPaths, SString &appNiPaths)
+{
+ CLRPrivBinderCoreCLR *pBinder = static_cast<CLRPrivBinderCoreCLR*>(((CompilationDomain *)pDomain)->GetFusionContext());
+ _ASSERTE(pBinder != NULL);
+ pBinder->SetupBindingPaths(trustedPlatformAssemblies, platformResourceRoots, appPaths, appNiPaths);
+#ifdef FEATURE_COMINTEROP
+ SString emptString;
+ ((CompilationDomain*)pDomain)->SetWinrtApplicationContext(emptString);
+#endif
+}
+
+#ifdef FEATURE_LEGACYNETCF
+void ZapperSetAppCompatWP8(ICorCompilationDomain *pDomain)
+{
+ ((CompilationDomain*)pDomain)->SetAppDomainCompatMode(BaseDomain::APPDOMAINCOMPAT_APP_EARLIER_THAN_WP8);
+}
+#endif
+
+#endif
+
+#if defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+bool IsSingleAppDomain()
+{
+ STARTUP_FLAGS flags = CorHost2::GetStartupFlags();
+ if(flags & STARTUP_SINGLE_APPDOMAIN)
+ return TRUE;
+ else
+ return FALSE;
+}
+#else
+bool IsSingleAppDomain()
+{
+ return FALSE;
+}
+#endif