diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2016-11-23 19:09:09 +0900 |
commit | 4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch) | |
tree | 98110734c91668dfdbb126fcc0e15ddbd93738ca /src/vm/classhash.cpp | |
parent | fa45f57ed55137c75ac870356a1b8f76c84b229c (diff) | |
download | coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2 coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip |
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'src/vm/classhash.cpp')
-rw-r--r-- | src/vm/classhash.cpp | 1103 |
1 files changed, 1103 insertions, 0 deletions
diff --git a/src/vm/classhash.cpp b/src/vm/classhash.cpp new file mode 100644 index 0000000000..408a6e8da4 --- /dev/null +++ b/src/vm/classhash.cpp @@ -0,0 +1,1103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// Hash table associated with each module that records for all types defined in that module the mapping +// between type name and token (or TypeHandle). +// + +#include "common.h" +#include "classhash.h" +#include "ngenhash.inl" +#include "fstring.h" +#include "classhash.inl" + +PTR_EEClassHashEntry EEClassHashEntry::GetEncloser() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + return m_pEncloser.Get(); +} + +PTR_VOID EEClassHashEntry::GetData() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + // TypeHandles are encoded as a relative pointer rather than a regular pointer to avoid the need for image + // fixups (any TypeHandles in this hash are defined in the same module). + if ((dac_cast<TADDR>(m_Data) & EECLASSHASH_TYPEHANDLE_DISCR) == 0) + return RelativePointer<PTR_VOID>::GetValueMaybeNullAtPtr(PTR_HOST_INT_MEMBER_TADDR(EEClassHashEntry, this, m_Data)); + + return m_Data; +} + +#ifndef DACCESS_COMPILE +void EEClassHashEntry::SetData(PTR_VOID data) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + // TypeHandles are encoded as a relative pointer rather than a regular pointer to avoid the need for image + // fixups (any TypeHandles in this hash are defined in the same module). + if (((TADDR)data & EECLASSHASH_TYPEHANDLE_DISCR) == 0) + RelativePointer<PTR_VOID>::SetValueMaybeNullAtPtr((TADDR)&m_Data, data); + else + m_Data = data; +} + +void EEClassHashEntry::SetEncloser(EEClassHashEntry *pEncloser) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + m_pEncloser.Set(pEncloser); +} + +/*static*/ +EEClassHashTable *EEClassHashTable::Create(Module *pModule, DWORD dwNumBuckets, BOOL bCaseInsensitive, AllocMemTracker *pamTracker) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); + + } + CONTRACTL_END; + + LoaderHeap *pHeap = pModule->GetAssembly()->GetLowFrequencyHeap(); + EEClassHashTable *pThis = (EEClassHashTable*)pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(EEClassHashTable))); + + // The base class get initialized through chaining of constructors. We allocated the hash instance via the + // loader heap instead of new so use an in-place new to call the constructors now. + new (pThis) EEClassHashTable(pModule, pHeap, dwNumBuckets); + + pThis->m_bCaseInsensitive = bCaseInsensitive; + + return pThis; +} + +EEClassHashEntry_t *EEClassHashTable::AllocNewEntry(AllocMemTracker *pamTracker) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + INJECT_FAULT(COMPlusThrowOM();); + MODE_ANY; + + PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); + } + CONTRACTL_END; + + // Simply defer to the base class for entry allocation (this is required, the base class wraps the entry + // it returns to us in its own metadata). + return BaseAllocateEntry(pamTracker); +} + +#endif // !DACCESS_COMPILE + +VOID EEClassHashTable::UncompressModuleAndNonExportClassDef(HashDatum Data, Module **ppModule, mdTypeDef *pCL) +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + DWORD dwData = (DWORD)dac_cast<TADDR>(Data); + _ASSERTE((dwData & EECLASSHASH_TYPEHANDLE_DISCR) == EECLASSHASH_TYPEHANDLE_DISCR); + _ASSERTE(!(dwData & EECLASSHASH_MDEXPORT_DISCR)); + + *pCL = ((dwData >> 1) & 0x00ffffff) | mdtTypeDef; + *ppModule = m_pModule; +} + +bool EEClassHashTable::UncompressModuleAndClassDef(HashDatum Data, Loader::LoadFlag loadFlag, + Module **ppModule, mdTypeDef *pCL, + mdExportedType *pmdFoundExportedType) +{ + CONTRACT(bool) + { + INSTANCE_CHECK; + 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();); } + MODE_ANY; + + PRECONDITION(CheckPointer(pCL)); + PRECONDITION(CheckPointer(ppModule)); + POSTCONDITION(*ppModule != nullptr || loadFlag != Loader::Load); + SUPPORTS_DAC; + } + CONTRACT_END + + DWORD dwData = (DWORD)dac_cast<TADDR>(Data); + _ASSERTE((dwData & EECLASSHASH_TYPEHANDLE_DISCR) == EECLASSHASH_TYPEHANDLE_DISCR); + if(dwData & EECLASSHASH_MDEXPORT_DISCR) { + *pmdFoundExportedType = ((dwData >> 1) & 0x00ffffff) | mdtExportedType; + + *ppModule = m_pModule->GetAssembly()-> + FindModuleByExportedType(*pmdFoundExportedType, loadFlag, mdTypeDefNil, pCL); + } + else { + UncompressModuleAndNonExportClassDef(Data, ppModule, pCL); + *pmdFoundExportedType = mdTokenNil; + _ASSERTE(*ppModule != nullptr); // Should never fail. + } + + RETURN (*ppModule != nullptr); +} + +/* static */ +mdToken EEClassHashTable::UncompressModuleAndClassDef(HashDatum Data) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END + + DWORD dwData = (DWORD)dac_cast<TADDR>(Data); // 64Bit: Pointer truncation is OK here - it's not actually a pointer + _ASSERTE((dwData & EECLASSHASH_TYPEHANDLE_DISCR) == EECLASSHASH_TYPEHANDLE_DISCR); + + if(dwData & EECLASSHASH_MDEXPORT_DISCR) + return ((dwData >> 1) & 0x00ffffff) | mdtExportedType; + else + return ((dwData >> 1) & 0x00ffffff) | mdtTypeDef; +} + +VOID EEClassHashTable::ConstructKeyFromData(PTR_EEClassHashEntry pEntry, // IN : Entry to compare + ConstructKeyCallback *pCallback) // This class will process the output +{ + CONTRACTL + { + THROWS; + WRAPPER(MODE_ANY); + WRAPPER(GC_TRIGGERS); + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else WRAPPER(FORBID_FAULT); + SUPPORTS_DAC; + } + CONTRACTL_END; + + LPUTF8 Key[2]; + Key[0] = Key[1] = NULL; + + { +#ifdef _DEBUG_IMPL + _ASSERTE(!(m_bCaseInsensitive && FORBIDGC_LOADER_USE_ENABLED())); +#endif + + // cqb - If m_bCaseInsensitive is true for the hash table, the bytes in Key will be allocated + // from cqb. This is to prevent wasting bytes in the Loader Heap. Thusly, it is important to note that + // in this case, the lifetime of Key is bounded by the lifetime of cqb, which will free the memory + // it allocated on destruction. + + _ASSERTE(m_pModule); + LPSTR pszName = NULL; + LPSTR pszNameSpace = NULL; + IMDInternalImport *pInternalImport = NULL; + + PTR_VOID Data = NULL; + if (!m_bCaseInsensitive) + Data = pEntry->GetData(); + else + Data = (PTR_EEClassHashEntry(pEntry->GetData()))->GetData(); + + // Lower bit is a discriminator. If the lower bit is NOT SET, it means we have + // a TypeHandle, otherwise, we have a mdtTypedef/mdtExportedType. + if ((dac_cast<TADDR>(Data) & EECLASSHASH_TYPEHANDLE_DISCR) == 0) + { + TypeHandle pType = TypeHandle::FromPtr(Data); + _ASSERTE (pType.GetMethodTable()); + MethodTable *pMT = pType.GetMethodTable(); + _ASSERTE(pMT != NULL); + IfFailThrow(pMT->GetMDImport()->GetNameOfTypeDef(pMT->GetCl(), (LPCSTR *)&pszName, (LPCSTR *)&pszNameSpace)); + } + else // We have a mdtoken + { + // call the lightweight version first + mdToken mdtUncompressed = UncompressModuleAndClassDef(Data); + if (TypeFromToken(mdtUncompressed) == mdtExportedType) + { + IfFailThrow(m_pModule->GetClassLoader()->GetAssembly()->GetManifestImport()->GetExportedTypeProps( + mdtUncompressed, + (LPCSTR *)&pszNameSpace, + (LPCSTR *)&pszName, + NULL, //mdImpl + NULL, // type def + NULL)); // flags + } + else + { + _ASSERTE(TypeFromToken(mdtUncompressed) == mdtTypeDef); + + Module * pUncompressedModule; + mdTypeDef UncompressedCl; + UncompressModuleAndNonExportClassDef(Data, &pUncompressedModule, &UncompressedCl); + _ASSERTE (pUncompressedModule && "Uncompressed token of unexpected type"); + pInternalImport = pUncompressedModule->GetMDImport(); + _ASSERTE(pInternalImport && "Uncompressed token has no MD import"); + IfFailThrow(pInternalImport->GetNameOfTypeDef(UncompressedCl, (LPCSTR *)&pszName, (LPCSTR *)&pszNameSpace)); + } + } + + if (!m_bCaseInsensitive) + { + Key[0] = pszNameSpace; + Key[1] = pszName; + } + else + { + CONTRACT_VIOLATION(ThrowsViolation|FaultViolation); + +#ifndef DACCESS_COMPILE + // We can call the nothrow version here because we fulfilled the requirement of calling + // InitTables() in the "new" method. + INT32 iNSLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszNameSpace); + if (!iNSLength) + { + COMPlusThrowOM(); + } + + INT32 iNameLength = InternalCasingHelper::InvariantToLowerNoThrow(NULL, 0, pszName); + if (!iNameLength) + { + COMPlusThrowOM(); + } + + // Prefast overflow sanity check before alloc. + INT32 iAllocSize; + if (!ClrSafeInt<INT32>::addition(iNSLength, iNameLength, iAllocSize)) + COMPlusThrowOM(); + LPUTF8 pszOutNameSpace = (LPUTF8) _alloca(iAllocSize); + if (iNSLength == 1) + { + *pszOutNameSpace = '\0'; + } + else + { + if (!InternalCasingHelper::InvariantToLowerNoThrow(pszOutNameSpace, iNSLength, pszNameSpace)) + { + COMPlusThrowOM(); + } + } + LPUTF8 pszOutName = (LPUTF8) pszOutNameSpace + iNSLength; + + if (!InternalCasingHelper::InvariantToLowerNoThrow(pszOutName, iNameLength, pszName)) + { + COMPlusThrowOM(); + } + Key[0] = pszOutNameSpace; + Key[1] = pszOutName; +#else + DacNotImpl(); +#endif // #ifndef DACCESS_COMPILE + } + } + + pCallback->UseKeys(Key); +} + +#ifndef DACCESS_COMPILE + +EEClassHashEntry_t *EEClassHashTable::InsertValue(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID Data, EEClassHashEntry_t *pEncloser, AllocMemTracker *pamTracker) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + + INJECT_FAULT(COMPlusThrowOM();); + PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); + } + CONTRACTL_END; + + _ASSERTE(pszNamespace != NULL); + _ASSERTE(pszClassName != NULL); + _ASSERTE(m_pModule); + + EEClassHashEntry *pEntry = BaseAllocateEntry(pamTracker); + + pEntry->SetData(Data); + pEntry->SetEncloser(pEncloser); +#ifdef _DEBUG + pEntry->DebugKey[0] = pszNamespace; + pEntry->DebugKey[1] = pszClassName; +#endif + + BaseInsertEntry(Hash(pszNamespace, pszClassName), pEntry); + + return pEntry; +} + +#ifdef _DEBUG +class ConstructKeyCallbackValidate : public EEClassHashTable::ConstructKeyCallback +{ +public: + virtual void UseKeys(__in_ecount(2) LPUTF8 *Key) + { + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_DEBUG_ONLY; + _ASSERTE (strcmp(pNewEntry->DebugKey[1], Key[1]) == 0); + _ASSERTE (strcmp(pNewEntry->DebugKey[0], Key[0]) == 0); + SUPPORTS_DAC; + } + + EEClassHashEntry_t *pNewEntry; + +}; +#endif // _DEBUG + +// This entrypoint lets the caller separate the allocation of the entrypoint from the actual insertion into the hashtable. (This lets us +// do multiple insertions without having to worry about an OOM occuring inbetween.) +// +// The newEntry must have been allocated using AllocEntry. It must not be referenced by any other entity (other than a holder or tracker) +// If this function throws, the caller is responsible for freeing the entry. +EEClassHashEntry_t *EEClassHashTable::InsertValueUsingPreallocatedEntry(EEClassHashEntry_t *pNewEntry, LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID Data, EEClassHashEntry_t *pEncloser) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + FORBID_FAULT; + + PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); + } + CONTRACTL_END; + + pNewEntry->SetData(Data); + pNewEntry->SetEncloser(pEncloser); + +#ifdef _DEBUG + pNewEntry->DebugKey[0] = pszNamespace; + pNewEntry->DebugKey[1] = pszClassName; +#endif + + BaseInsertEntry(Hash(pszNamespace, pszClassName), pNewEntry); + + return pNewEntry; +} + +EEClassHashEntry_t *EEClassHashTable::InsertValueIfNotFound(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID *pData, EEClassHashEntry_t *pEncloser, BOOL IsNested, BOOL *pbFound, AllocMemTracker *pamTracker) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + + PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); + } + CONTRACTL_END; + + _ASSERTE(m_pModule); + _ASSERTE(pszNamespace != NULL); + _ASSERTE(pszClassName != NULL); + _ASSERTE(m_pModule); + + EEClassHashEntry_t * pNewEntry = FindItem(pszNamespace, pszClassName, IsNested, NULL); + + if (pNewEntry) + { + *pData = pNewEntry->GetData(); + *pbFound = TRUE; + return pNewEntry; + } + + // Reached here implies that we didn't find the entry and need to insert it + *pbFound = FALSE; + + pNewEntry = BaseAllocateEntry(pamTracker); + + pNewEntry->SetData(*pData); + pNewEntry->SetEncloser(pEncloser); + +#ifdef _DEBUG + pNewEntry->DebugKey[0] = pszNamespace; + pNewEntry->DebugKey[1] = pszClassName; +#endif + + BaseInsertEntry(Hash(pszNamespace, pszClassName), pNewEntry); + + return pNewEntry; +} + +#endif // !DACCESS_COMPILE + +EEClassHashEntry_t *EEClassHashTable::FindItem(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, BOOL IsNested, LookupContext *pContext) +{ + CONTRACTL + { + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + _ASSERTE(m_pModule); + _ASSERTE(pszNamespace != NULL); + _ASSERTE(pszClassName != NULL); + + // It's legal for the caller not to pass us a LookupContext (when the type being queried is not nested + // there will never be any need to iterate over the search results). But we might need to iterate + // internally (since we lookup via hash and hashes may collide). So substitute our own private context if + // one was not provided. + LookupContext sAltContext; + if (pContext == NULL) + pContext = &sAltContext; + + // The base class provides the ability to enumerate all entries with the same hash code. We call this and + // further check which of these entries actually match the full key (there can be multiple hits with + // nested types in the picture). + PTR_EEClassHashEntry pSearch = BaseFindFirstEntryByHash(Hash(pszNamespace, pszClassName), pContext); + + while (pSearch) + { + LPCUTF8 rgKey[] = { pszNamespace, pszClassName }; + + if (CompareKeys(pSearch, rgKey)) + { + // If (IsNested), then we're looking for a nested class + // If (pSearch->pEncloser), we've found a nested class + if ((IsNested != FALSE) == (pSearch->GetEncloser() != NULL)) + { + if (m_bCaseInsensitive) + g_IBCLogger.LogClassHashTableAccess(dac_cast<PTR_EEClassHashEntry>(pSearch->GetData())); + else + g_IBCLogger.LogClassHashTableAccess(pSearch); + + return pSearch; + } + } + + pSearch = BaseFindNextEntryByHash(pContext); + } + + return NULL; +} + +EEClassHashEntry_t *EEClassHashTable::FindNextNestedClass(NameHandle* pName, PTR_VOID *pData, LookupContext *pContext) +{ + CONTRACTL + { + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + _ASSERTE(m_pModule); + _ASSERTE(pName); + + if (pName->GetNameSpace()) + { + return FindNextNestedClass(pName->GetNameSpace(), pName->GetName(), pData, pContext); + } + else { +#ifndef DACCESS_COMPILE + return FindNextNestedClass(pName->GetName(), pData, pContext); // this won't support dac-- + // it allocates a new namespace string +#else + DacNotImpl(); + return NULL; +#endif + } +} + + +EEClassHashEntry_t *EEClassHashTable::FindNextNestedClass(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID *pData, LookupContext *pContext) +{ + CONTRACTL + { + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + _ASSERTE(m_pModule); + + PTR_EEClassHashEntry pSearch = BaseFindNextEntryByHash(pContext); + + while (pSearch) + { + LPCUTF8 rgKey[] = { pszNamespace, pszClassName }; + + if (pSearch->GetEncloser() && CompareKeys(pSearch, rgKey)) + { + *pData = pSearch->GetData(); + return pSearch; + } + + pSearch = BaseFindNextEntryByHash(pContext); + } + + return NULL; +} + +const UTF8 Utf8Empty[] = { 0 }; + +EEClassHashEntry_t *EEClassHashTable::FindNextNestedClass(LPCUTF8 pszFullyQualifiedName, PTR_VOID *pData, LookupContext *pContext) +{ + CONTRACTL + { + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + } + CONTRACTL_END; + + _ASSERTE(m_pModule); + + CQuickBytes szNamespace; + + LPCUTF8 pNamespace = Utf8Empty; + LPCUTF8 p; + + if ((p = ns::FindSep(pszFullyQualifiedName)) != NULL) + { + SIZE_T d = p - pszFullyQualifiedName; + + FAULT_NOT_FATAL(); + pNamespace = szNamespace.SetStringNoThrow(pszFullyQualifiedName, d); + + if (NULL == pNamespace) + { + return NULL; + } + + p++; + } + else + { + p = pszFullyQualifiedName; + } + + return FindNextNestedClass(pNamespace, p, pData, pContext); +} + + +EEClassHashEntry_t * EEClassHashTable::GetValue(LPCUTF8 pszFullyQualifiedName, PTR_VOID *pData, BOOL IsNested, LookupContext *pContext) +{ + CONTRACTL + { + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + _ASSERTE(m_pModule); + + CQuickBytes szNamespace; + + LPCUTF8 pNamespace = Utf8Empty; + + LPCUTF8 p = ns::FindSep(pszFullyQualifiedName); + + if (p != NULL) + { + SIZE_T d = p - pszFullyQualifiedName; + + FAULT_NOT_FATAL(); + pNamespace = szNamespace.SetStringNoThrow(pszFullyQualifiedName, d); + + if (NULL == pNamespace) + { + return NULL; + } + + p++; + } + else + { + p = pszFullyQualifiedName; + } + + EEClassHashEntry_t * ret = GetValue(pNamespace, p, pData, IsNested, pContext); + + return ret; +} + + +EEClassHashEntry_t * EEClassHashTable::GetValue(LPCUTF8 pszNamespace, LPCUTF8 pszClassName, PTR_VOID *pData, BOOL IsNested, LookupContext *pContext) +{ + CONTRACTL + { + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + + _ASSERTE(m_pModule); + EEClassHashEntry_t *pItem = FindItem(pszNamespace, pszClassName, IsNested, pContext); + if (pItem) + *pData = pItem->GetData(); + + return pItem; +} + + +EEClassHashEntry_t * EEClassHashTable::GetValue(NameHandle* pName, PTR_VOID *pData, BOOL IsNested, LookupContext *pContext) +{ + CONTRACTL + { + // for DAC builds m_bCaseInsensitive should be false + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + + _ASSERTE(pName); + _ASSERTE(m_pModule); + if(pName->GetNameSpace() == NULL) { + return GetValue(pName->GetName(), pData, IsNested, pContext); + } + else { + return GetValue(pName->GetNameSpace(), pName->GetName(), pData, IsNested, pContext); + } +} + +class ConstructKeyCallbackCompare : public EEClassHashTable::ConstructKeyCallback +{ +public: + virtual void UseKeys(__in_ecount(2) LPUTF8 *pKey1) + { + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + SUPPORTS_DAC; + + bReturn = ( + ((pKey1[0] == pKey2[0]) && (pKey1[1] == pKey2[1])) || + ((strcmp (pKey1[0], pKey2[0]) == 0) && (strcmp (pKey1[1], pKey2[1]) == 0)) + ); + } + + LPCUTF8 *pKey2; + BOOL bReturn; +}; + +// Returns TRUE if two keys are the same string. +// +// The case-insensitive table can throw OOM out of this function. The case-sensitive table can't. +BOOL EEClassHashTable::CompareKeys(PTR_EEClassHashEntry pEntry, LPCUTF8 * pKey2) +{ + CONTRACTL + { + if (m_bCaseInsensitive) THROWS; else NOTHROW; + if (m_bCaseInsensitive) GC_TRIGGERS; else GC_NOTRIGGER; + if (m_bCaseInsensitive) INJECT_FAULT(COMPlusThrowOM();); else FORBID_FAULT; + MODE_ANY; + SUPPORTS_DAC; + } + CONTRACTL_END; + + + _ASSERTE(m_pModule); + _ASSERTE (pEntry); + _ASSERTE (pKey2); + + ConstructKeyCallbackCompare cback; + + cback.pKey2 = pKey2; + + { + CONTRACT_VIOLATION(ThrowsViolation); + ConstructKeyFromData(pEntry, &cback); + } + + return cback.bReturn; +} + + +#ifndef DACCESS_COMPILE + +#ifdef FEATURE_NATIVE_IMAGE_GENERATION +void EEClassHashTable::Save(DataImage *image, CorProfileData *profileData) +{ + STANDARD_VM_CONTRACT; + + // See comment on PrepareExportedTypesForSaving for what's going on here. + if (m_pModule->IsManifest()) + PrepareExportedTypesForSaving(image); + + // The base class handles most of the saving logic (it controls the layout of the hash memory). It will + // call us back for some per-entry related details (should we save this entry?, is this entry hot? etc.). + // See the methods immediately following this one. + BaseSave(image, profileData); +} + +// Should a particular entry be persisted into the ngen image? +bool EEClassHashTable::ShouldSave(DataImage *pImage, EEClassHashEntry_t *pEntry) +{ + LIMITED_METHOD_CONTRACT; + + // We always save all entries. + return true; +} + +// Does profile data indicate that this entry is hot (likely to be read at runtime)? +bool EEClassHashTable::IsHotEntry(EEClassHashEntry_t *pEntry, CorProfileData *pProfileData) +{ + STANDARD_VM_CONTRACT; + + PTR_VOID datum = pEntry->GetData(); + mdToken token; + + if (m_bCaseInsensitive) + datum = PTR_EEClassHashEntry((TADDR)datum)->GetData(); + + if ((((ULONG_PTR) datum) & EECLASSHASH_TYPEHANDLE_DISCR) == 0) + { + TypeHandle t = TypeHandle::FromPtr(datum); + _ASSERTE(!t.IsNull()); + MethodTable *pMT = t.GetMethodTable(); + if (pMT == NULL) + return false; + + token = pMT->GetCl(); + } + else if (((ULONG_PTR)datum & EECLASSHASH_MDEXPORT_DISCR) == 0) + { + DWORD dwDatum = (DWORD)(DWORD_PTR)(datum); + token = ((dwDatum >> 1) & 0x00ffffff) | mdtTypeDef; + } + else + return false; + + if (pProfileData->GetTypeProfilingFlagsOfToken(token) & (1 << ReadClassHashTable)) + return true; + + return false; +} + +// This is our chance to fixup our hash entries before they're committed to the ngen image. Return true if the +// entry will remain immutable at runtime (this might allow the entry to be stored in a read-only, shareable +// portion of the image). +bool EEClassHashTable::SaveEntry(DataImage *pImage, CorProfileData *pProfileData, EEClassHashEntry_t *pOldEntry, EEClassHashEntry_t *pNewEntry, EntryMappingTable *pMap) +{ + STANDARD_VM_CONTRACT; + + // If we're a nested class we have a reference to the entry of our enclosing class. But this reference + // will have been broken by the saving process (the base class re-creates and re-orders all entries in + // order to optimize them for ngen images). So we read the old encloser address from the old version of + // our entry and, if there is one, we use the supplied map to transform that address into the new version. + // We can then write the updated address back into our encloser field in the new copy of the entry. + EEClassHashEntry_t *pOldEncloser = pOldEntry->GetEncloser(); + if (pOldEncloser) + pNewEntry->SetEncloser(pMap->GetNewEntryAddress(pOldEncloser)); + + // We have to do something similar with our TypeHandle references (because they're stored as relative + // pointers which became invalid when the entry was moved). The following sequence is a no-op if the data + // field contains a token rather than a TypeHandle. + PTR_VOID oldData = pOldEntry->GetData(); + pNewEntry->SetData(oldData); + +#ifdef _DEBUG + if (!pImage->IsStored(pNewEntry->DebugKey[0])) + pImage->StoreStructure(pNewEntry->DebugKey[0], (ULONG)(strlen(pNewEntry->DebugKey[0])+1), DataImage::ITEM_DEBUG); + if (!pImage->IsStored(pNewEntry->DebugKey[1])) + pImage->StoreStructure(pNewEntry->DebugKey[1], (ULONG)(strlen(pNewEntry->DebugKey[1])+1), DataImage::ITEM_DEBUG); +#endif // _DEBUG + + // The entry is immutable at runtime if it's encoded as a TypeHandle. If it's a token then it might be + // bashed into a TypeHandle later on. + return ((TADDR)pNewEntry->GetData() & EECLASSHASH_TYPEHANDLE_DISCR) == 0; +} + +// The manifest module contains exported type entries in the EEClassHashTables. During ngen, these entries get +// populated to the corresponding TypeHandle. However, at runtime, it is not guaranteed for the module +// containing the exported type to be loaded. Hence, we cannot use the TypeHandle. Instead, we bash the +// TypeHandle back to the export token. +void EEClassHashTable::PrepareExportedTypesForSaving(DataImage *image) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(GetAppDomain()->IsCompilationDomain()); + PRECONDITION(m_pModule->IsManifest()); + } + CONTRACTL_END + + IMDInternalImport *pImport = m_pModule->GetMDImport(); + + HENUMInternalHolder phEnum(pImport); + phEnum.EnumInit(mdtExportedType, mdTokenNil); + mdToken mdExportedType; + + for (int i = 0; pImport->EnumNext(&phEnum, &mdExportedType); i++) + { + mdTypeDef typeDef; + LPCSTR pszNameSpace, pszName; + mdToken mdImpl; + DWORD dwFlags; + if (FAILED(pImport->GetExportedTypeProps( + mdExportedType, + &pszNameSpace, + &pszName, + &mdImpl, + &typeDef, + &dwFlags))) + { + THROW_BAD_FORMAT(BFA_NOFIND_EXPORTED_TYPE, m_pModule); + continue; + } + + CorTokenType tokenType = (CorTokenType) TypeFromToken(mdImpl); + CONSISTENCY_CHECK(tokenType == mdtFile || + tokenType == mdtAssemblyRef || + tokenType == mdtExportedType); + + // If mdImpl is a file or an assembly, than it points to the location + // of the type. If mdImpl is another exported type, then it is the enclosing + // exported type for current (nested) type. + BOOL isNested = (tokenType == mdtExportedType); + + // ilasm does not consistently set the dwFlags to correctly reflect nesting + //CONSISTENCY_CHECK(!isNested || IsTdNested(dwFlags)); + + EEClassHashEntry_t * pEntry = NULL; + + if (!isNested) + { + pEntry = FindItem(pszNameSpace, pszName, FALSE/*nested*/, NULL); + } + else + { + PTR_VOID data; + LookupContext sContext; + + // This following line finds the 1st "nested" class EEClassHashEntry_t. + if ((pEntry = FindItem(pszNameSpace, pszName, TRUE/*nested*/, &sContext)) != NULL) + { + // The (immediate) encloser of EEClassHashEntry_t (i.e. pEntry) is stored in pEntry->pEncloser. + // It needs to be a type of "mdImpl". + // "CompareNestedEntryWithExportedType" will check if "pEntry->pEncloser" is a type of "mdImpl", + // as well as walking up the enclosing chain. + _ASSERTE (TypeFromToken(mdImpl) == mdtExportedType); + while ((!m_pModule->GetClassLoader()->CompareNestedEntryWithExportedType(pImport, + mdImpl, + this, + pEntry->GetEncloser())) && + (pEntry = FindNextNestedClass(pszNameSpace, pszName, &data, &sContext)) != NULL); + } + } + + if (!pEntry) { + THROW_BAD_FORMAT(BFA_NOFIND_EXPORTED_TYPE, m_pModule); + continue; + } + + if (((ULONG_PTR)(pEntry->GetData())) & EECLASSHASH_TYPEHANDLE_DISCR) + continue; + + TypeHandle th = TypeHandle::FromPtr(pEntry->GetData()); + +#ifdef _DEBUG + MethodTable * pMT = th.GetMethodTable(); + _ASSERTE(tokenType != mdtFile || pMT->GetModule()->GetModuleRef() == mdImpl); + // "typeDef" is just a hint for unsigned assemblies, and ILASM sets it to 0 + // Hence, we need to relax this assert. + _ASSERTE(pMT->GetCl() == typeDef || typeDef == mdTokenNil); +#endif + + if (image->CanEagerBindToTypeHandle(th) && image->CanHardBindToZapModule(th.GetLoaderModule())) + continue; + + // Bash the TypeHandle back to the export token + pEntry->SetData(EEClassHashTable::CompressClassDef(mdExportedType)); + } +} + +void EEClassHashTable::Fixup(DataImage *pImage) +{ + STANDARD_VM_CONTRACT; + + // The base class does all the main fixup work. We're called back at FixupEntry below for each entry so we + // can fixup additional pointers. + BaseFixup(pImage); +} + +void EEClassHashTable::FixupEntry(DataImage *pImage, EEClassHashEntry_t *pEntry, void *pFixupBase, DWORD cbFixupOffset) +{ + STANDARD_VM_CONTRACT; + + // Cross-entry references require special fixup. Fortunately they know how to do this themselves. + pEntry->m_pEncloser.Fixup(pImage, this); + +#ifdef _DEBUG + pImage->FixupPointerField(pFixupBase, cbFixupOffset + offsetof(EEClassHashEntry_t, DebugKey[0])); + pImage->FixupPointerField(pFixupBase, cbFixupOffset + offsetof(EEClassHashEntry_t, DebugKey[1])); +#endif // _DEBUG + + // Case insensitive tables and normal hash entries pointing to TypeHandles contain relative pointers. + // These don't require runtime fixups (because relative pointers remain constant even in the presence of + // base relocations) but we still have to register a fixup of type IMAGE_REL_BASED_RELPTR. This does the + // work necessary to update the value of the relative pointer once the ngen image is finally layed out + // (the layout process can change the relationship between this field and the target it points to, so we + // don't know the final delta until the image is just about to be written out). + if (m_bCaseInsensitive || ((TADDR)pEntry->GetData() & EECLASSHASH_TYPEHANDLE_DISCR) == 0) + { + pImage->FixupField(pFixupBase, + cbFixupOffset + offsetof(EEClassHashEntry_t, m_Data), + pEntry->GetData(), + 0, + IMAGE_REL_BASED_RELPTR); + } +} +#endif // FEATURE_NATIVE_IMAGE_GENERATION + +/*===========================MakeCaseInsensitiveTable=========================== +**Action: Creates a case-insensitive lookup table for class names. We create a +** full path (namespace & class name) in lowercase and then use that as the +** key in our table. The hash datum is a pointer to the EEClassHashEntry in this +** table. +** +!! You MUST have already acquired the appropriate lock before calling this.!! +** +**Returns:The newly allocated and completed hashtable. +==============================================================================*/ + +class ConstructKeyCallbackCaseInsensitive : public EEClassHashTable::ConstructKeyCallback +{ +public: + virtual void UseKeys(__in_ecount(2) LPUTF8 *key) + { + WRAPPER_NO_CONTRACT; + + //Build the cannonical name (convert it to lowercase). + //Key[0] is the namespace, Key[1] is class name. + + pLoader->CreateCanonicallyCasedKey(key[0], key[1], ppszLowerNameSpace, ppszLowerClsName); + } + + ClassLoader *pLoader; + LPUTF8 *ppszLowerNameSpace; + LPUTF8 *ppszLowerClsName; + +}; + +EEClassHashTable *EEClassHashTable::MakeCaseInsensitiveTable(Module *pModule, AllocMemTracker *pamTracker) +{ + EEClassHashEntry_t *pTempEntry; + LPUTF8 pszLowerClsName = NULL; + LPUTF8 pszLowerNameSpace = NULL; + + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM();); + + PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); + } + CONTRACTL_END; + + + + _ASSERTE(m_pModule); + _ASSERTE (pModule == m_pModule); + + // Allocate the table and verify that we actually got one. + EEClassHashTable * pCaseInsTable = EEClassHashTable::Create(pModule, + max(BaseGetElementCount() / 2, 11), + TRUE /* bCaseInsensitive */, + pamTracker); + + // Walk all of the buckets and insert them into our new case insensitive table + BaseIterator sIter; + BaseInitIterator(&sIter); + while ((pTempEntry = sIter.Next()) != NULL) + { + ConstructKeyCallbackCaseInsensitive cback; + + cback.pLoader = pModule->GetClassLoader(); + cback.ppszLowerNameSpace = &pszLowerNameSpace; + cback.ppszLowerClsName = &pszLowerClsName; + ConstructKeyFromData(pTempEntry, &cback); + + //Add the newly created name to our hash table. The hash datum is a pointer + //to the entry associated with that name in this hashtable. + pCaseInsTable->InsertValue(pszLowerNameSpace, pszLowerClsName, (PTR_VOID)pTempEntry, pTempEntry->GetEncloser(), pamTracker); + } + + return pCaseInsTable; +} +#endif // !DACCESS_COMPILE + +#ifdef DACCESS_COMPILE +void EEClassHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + // Defer to the base class to do the bulk of this work. It calls EnumMemoryRegionsForEntry below for each + // entry in case we want to enumerate anything extra. + BaseEnumMemoryRegions(flags); +} + +void EEClassHashTable::EnumMemoryRegionsForEntry(EEClassHashEntry_t *pEntry, CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + // Nothing more to enumerate (the base class handles enumeration of the memory for the entry itself). +} +#endif // DACCESS_COMPILE |